Newer
Older
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# How to develop a MEC application using the MEC Sandbox HTTP REST API\n",
"This tutorial introduces the step by step procedure to create a basic MEC application following ETSI MEC standards.\n",
"It uses the ETSI MEC Sandbox simulator.\n",
"\n",
"<div class=\"alert alert-block alert-danger\">\n",
" <b>Note:</b> These source code examples are simplified and ignore return codes and error checks to a large extent. We do this to highlight how to use the MEC Sandbox API and the different MEC satndards and reduce unrelated code.\n",
"A real-world application will of course properly check every return value and exit correctly at the first serious error.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is a MEC application\n",
"\n",
"See [The Wiki MEC web site](https://www.etsi.org/technologies/multi-access-edge-computing)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The basics of developing a MEC application\n",
"\n",
"The developement of a MEC application follows a strict process in order to access the ETSI MEC services and provides valuable services to the customers.\n",
"Mainly, this process can be split in several steps:\n",
"1. Global initializations (constant, variables...)\n",
"2. Create a new instance of a MEC Sandbox (Note that using an existing one could be a solution too (see Annex A))\n",
"3. Activate a network scenario in order to access the ETSI MEC services\n",
"4. Create a new application identifier\n",
"5. Register our MEC application and subscribe to service termination (see MEC 011)\n",
"6. Use MEC services in order to provide valuable services to the customers\n",
" 6.1. Apply MEC services required subscriptions (e.g. MEC 013 location subscription)\n",
"7. Terminate the MEC application\n",
" 7.1. Remove MEC services subscriptions\n",
" 7.2. Deactivate the current network scenario\n",
" 7.3. Delete the instance of the MEC Sandbox\n",
"8. Release all the MEC application resources\n",
"\n",
"**Note:** Several application identifier can be created to address several MEC application.\n",
"\n",
"## Use the MEC Sandbox HTTP REST API models and code\n",
"\n",
"The MEC sandbox provides a piece of code (the python sub) that shall be used to develop the MEC application and interact with the MEC Sandbox. This piece of code mainly contains swagger models to serialize/deserialize JSON data structures and HTTP REST API call functions.\n",
"The openApi file is availabe [here](https://labs.etsi.org/rep/mec/etsi-mec-sandbox/-/blob/STF678_Task1_2_3_4/go-apps/meep-sandbox-api/api/swagger.yaml) and the [Swagger editor](https://editor-next.swagger.io/) is used to generate the python stub.\n",
"The project architecture is describe [here](images/project_arch.jpg).\n",
"The sandbox_api folder contains the python implementation of the HTTP REST API definitions introduced by the openApi [file](https://labs.etsi.org/rep/mec/etsi-mec-sandbox/-/blob/STF678_Task1_2_3_4/go-apps/meep-sandbox-api/api/swagger.yaml).\n",
"The model folder contains the python implementation of the data type definitions introduced by the openApi [file](https://labs.etsi.org/rep/mec/etsi-mec-sandbox/-/blob/STF678_Task1_2_3_4/go-apps/meep-sandbox-api/api/swagger.yaml).\n",
"\n",
"<div class=\"alert alert-warning\" role=\"alert\">\n",
" <b>Note:</b> The sub-paragraph 'Putting everything together' is a specific paragraph where all the newly features introduced in the main paragraph are put together to create an executable block of code. It is possible to skip this block of code by removing the comment character (#) on first line of this block of code.\n",
"</div>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Before going to create our MEC application skeleton, the following steps shall be done:\n",
"1) Change the working directory (see the project architecture)"
]
},
{
"cell_type": "code",
"source": [
"import os\n",
"os.chdir(os.path.join(os.getcwd(), '../mecapp'))\n",
"print(os.getcwd())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2) Apply the python imports"
"metadata": {},
"outputs": [],
"source": [
"from __future__ import division # Import floating-point division (1/4=0.25) instead of Euclidian division (1/4=0)\n",
"\n",
"import os\n",
"import sys\n",
"import threading\n",
"import time\n",
"import json\n",
"import uuid\n",
"\n",
"import pprint\n",
"\n",
"import six\n",
"from swagger_client.rest import ApiException\n",
"\n",
"from http import HTTPStatus\n",
"from http.server import BaseHTTPRequestHandler, HTTPServer\n",
"\n",
"try:\n",
" import urllib3\n",
"except ImportError:\n",
" raise ImportError('Swagger python client requires urllib3.')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3) Initialize of the global constants (cell 3)"
"MEC_SANDBOX_URL = 'https://mec-platform2.etsi.org' # MEC Sandbox host/base URL\n",
"MEC_SANDBOX_API_URL = 'https://mec-platform2.etsi.org/sandbox-api/v1' # MEC Sandbox API host/base URL\n",
"PROVIDER = 'Jupyter2024' # Login provider value - To skip authorization: 'github'\n",
"MEC_PLTF = 'mep1' # MEC plateform name. Linked to the network scenario\n",
"LOGGER_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' # Logging format\n",
"STABLE_TIME_OUT = 10 # Timer to wait for MEC Sndbox reaches its stable state (K8S pods in running state)\n",
"LOGIN_TIMEOUT = 3 #30 # Timer to wait for user to authorize from GITHUB\n",
"LISTENER_IP = '0.0.0.0' # Listener IPv4 address for notification callback calls\n",
"LISTENER_PORT = 31111 # Listener IPv4 port for notification callback calls. Default: 36001\n",
"CALLBACK_URI = 'http://mec-platform2.etsi.org:31111/sandbox/v1'\n",
" #'https://yanngarcia.ddns.net:' + str(LISTENER_PORT) + '/jupyter/sandbox/demo6/v1/'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4) Setup the logger instance and the HTTP REST API (cell 4)"
"metadata": {},
"outputs": [],
"source": [
"# Initialize the logger\n",
"logger = logging.getLogger(__name__)\n",
"logger.setLevel(logging.DEBUG)\n",
"logging.basicConfig(filename='/tmp/' + time.strftime('%Y%m%d-%H%M%S') + '.log')\n",
"l = logging.StreamHandler()\n",
"l.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))\n",
"logger.addHandler(l)\n",
"\n",
"# Setup the HTTP REST API configuration to be used to send request to MEC Sandbox API \n",
"configuration.verify_ssl = True\n",
"configuration.debug = True\n",
"configuration.logger_format = LOGGER_FORMAT\n",
"# Create an instance of ApiClient\n",
"sandbox_api = swagger_client.ApiClient(configuration, 'Content-Type', 'application/json')\n",
"\n",
"# Setup the HTTP REST API configuration to be used to send request to MEC Services\n",
"configuration1 = swagger_client.Configuration()\n",
"configuration1.host = MEC_SANDBOX_URL\n",
"configuration1.verify_ssl = True\n",
"configuration1.debug = True\n",
"configuration1.logger_format = LOGGER_FORMAT\n",
"# Create an instance of ApiClient\n",
"service_api = swagger_client.ApiClient(configuration1, 'Content-Type', 'application/json')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"5) Setup the global variables (cell 5)"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"nw_scenarios = [] # The list of available network scenarios\n",
"nw_scenario_idx = -1 # The network scenario idx to activate (deactivate)\n",
"app_inst_id = None # The requested application instance identifier\n",
"got_notification = False # Set to true if a POST notification is received"
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create our first MEC application\n",
"\n",
"The first step to develop a MEC application is to create the application skeleton which contains the minimum steps below:\n",
" \n",
"- Login to instanciate a MEC Sandbox\n",
"- Logout to delete a existing MEC Sandbox"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### First steps: the login/logout\n",
"\n",
"Here is the first squeleton with the following sequence:\n",
"- Login\n",
"- Print sandbox identifier\n",
"- Logout\n",
"- Check that logout is effective\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"To log to the MEC Sandbox, \n",
"the login process is done in two step. In step 1, a user code is requested to GITHUB. In step 2, the user has to enter this user code to https://github.com/login/device and proceed to the authorization.\n",
"Please, pay attention to the log '=======================> DO AUTHORIZATION WITH CODE :' which indicates you the user code to use for the authorization.\n",
"It uses the HTTP POST request with the URL 'POST /sandbox-sandbox_api/v1/login?provide=github' (see PROVIDER constant).\n"
"metadata": {},
"outputs": [],
"source": [
"# Login\n",
"def process_login() -> str:\n",
" \"\"\"\n",
" Authenticate and create a new MEC Sandbox instance.\n",
" :return: The sandbox instance identifier on success, None otherwise\n",
" global PROVIDER, logger\n",
" logger.debug('>>> process_login')\n",
" auth = swagger_client.AuthorizationApi(sandbox_api)\n",
" oauth = auth.login(PROVIDER, async_req = False)\n",
" logger.debug('process_login (step1): oauth: ' + str(oauth))\n",
" # Wait for the MEC Sandbox is running\n",
" logger.debug('=======================> DO AUTHORIZATION WITH CODE : ' + oauth.user_code)\n",
" logger.debug('=======================> DO AUTHORIZATION HERE : ' + oauth.verification_uri)\n",
" if oauth.verification_uri == \"\":\n",
" time.sleep(LOGIN_TIMEOUT) # Skip scecurity, wait for a few seconds\n",
" else:\n",
" time.sleep(10 * LOGIN_TIMEOUT) # Wait for Authirization from user side\n",
" namespace = auth.get_namespace(oauth.user_code)\n",
" logger.debug('process_login (step2): result: ' + str(namespace))\n",
" return namespace.sandbox_name\n",
" logger.error('Exception when calling AuthorizationApi->login: %s\\n' % e)\n",
"\n",
" return None\n",
" # End of function process_login\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The logout function\n",
"\n",
"It uses the HTTP POST request with the URL 'POST /sandbox-sandbox_api/v1/logout?sandbox_name={sandbox_name}'.\n"
"metadata": {},
"outputs": [],
"source": [
"# Logout\n",
"def process_logout(sandbox_name: str) -> int:\n",
" \"\"\"\n",
" Delete the specified MEC Sandbox instance.\n",
" :param sandbox_name: The MEC Sandbox to delete\n",
" :return: 0 on success, -1 otherwise\n",
" \"\"\"\n",
" logger.debug('>>> process_logout: sandbox=' + sandbox_name)\n",
" auth = swagger_client.AuthorizationApi(sandbox_api)\n",
" result = auth.logout(sandbox_name, async_req = False) # noqa: E501\n",
" logger.error('Exception when calling AuthorizationApi->logout: %s\\n' % e)\n",
" # End of function process_logout\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, let put in action our Login/Logout functions:"
]
},
{
"cell_type": "code",
"%%script echo skipping\n",
"# Uncomment the line above to skip execution of this cell\n",
"def process_main():\n",
" \"\"\"\n",
" This is the skeleton of our MEC application:\n",
" - Login\n",
" - Print sandbox identifier\n",
" - Logout\n",
" - Check that logout is effective\n",
" This skeleton will be the bas of the next sprint in order to achieve a full implementation of a MEC application\n",
" \"\"\" \n",
" logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" logger.debug('\\t pwd= ' + os.getcwd())\n",
"\n",
" # Login\n",
" sandbox = process_login()\n",
" if sandbox is None:\n",
" return\n",
"\n",
" # Print sandbox identifier\n",
" logger.info('Sandbox created: ' + sandbox)\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
"\n",
" # Logout\n",
" process_logout(sandbox)\n",
"\n",
" # Check that logout is effective\n",
" logger.debug('To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)')\n",
" logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" # End of function process_main\n",
"\n",
"if __name__ == '__main__':\n",
" process_main()\n"
]
},
{
"cell_type": "markdown",
"source": [
"### Second step: Retrieve the list of network scenarios\n",
"\n",
"Let's go futhur and see how we can retrieve the list of the network scenarios available in order to activate one of them and access the MEC services exposed such as MEC 013 or MEC 030.\n",
"\n",
"The sequence will be:\n",
"- Login\n",
"- Print sandbox identifier\n",
"- Print available network scenarios\n",
"- Logout\n",
"- Check that logout is effective\n",
"\n",
"The login and logout functions are described in cell 3 and 4.\n",
"\n",
"To retrieve the list of the network scenarios, let's create a new function called 'get_network_scenarios'. It uses the HTTP GET request with the URL '/sandbox-sandbox_api/v1/sandboxNetworkScenarios?sandbox_name={sandbox_name}'."
"def get_network_scenarios(sandbox_name: str) -> list:\n",
" \"\"\"\n",
" Retrieve the list of the available network scenarios.\n",
" :param sandbox_name: The MEC Sandbox instance to use\n",
" :return: The list of the available network scenarios on success, None otherwise\n",
" \"\"\"\n",
" global PROVIDER, logger, sandbox_api, configuration\n",
" logger.debug('>>> get_network_scenarios: sandbox=' + sandbox_name)\n",
" nw = swagger_client.SandboxNetworkScenariosApi(sandbox_api)\n",
" result = nw.sandbox_network_scenarios_get(sandbox_name, async_req = False) # noqa: E501\n",
" logger.debug('get_network_scenarios: result: ' + str(result))\n",
" return result\n",
" except ApiException as e:\n",
" logger.error('Exception when calling SandboxNetworkScenariosApi->sandbox_network_scenarios_get: %s\\n' % e)\n",
"\n",
" return None\n",
" # End of function get_network_scenarios\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Putting everything together\n",
"\n",
"Here the logic is:\n",
"- Login\n",
"- Print sandbox identifier\n",
"- Print available network scenarios\n",
"- Logout\n",
"- Check that logout is effective\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Uncomment the ;line above to skip execution of this cell\n",
"def process_main():\n",
" \"\"\"\n",
" This is the first sprint of our skeleton of our MEC application:\n",
" - Login\n",
" - Print sandbox identifier\n",
" - Print available network scenarios\n",
" - Logout\n",
" - Check that logout is effective\n",
" \"\"\" \n",
" global logger, nw_scenarios \n",
" logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" logger.debug('\\t pwd= ' + os.getcwd())\n",
"\n",
" # Login\n",
" sandbox = process_login()\n",
" if sandbox is None:\n",
" logger.error('Failed to instanciate a MEC Sandbox')\n",
" return\n",
"\n",
" # Print sandbox identifier\n",
" logger.info('Sandbox created: ' + sandbox)\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
"\n",
" # Print available network scenarios\n",
" nw_scenarios = get_network_scenarios(sandbox)\n",
" if nw_scenarios is None:\n",
" logger.error('Failed to retrieve the list of network scenarios')\n",
" logger.info('nw_scenarios: %s', str(type(nw_scenarios[0])))\n",
" logger.info('nw_scenarios: %s', str(nw_scenarios))\n",
" logger.info('nw_scenarios: No scenario available')\n",
"\n",
" # Logout\n",
" process_logout(sandbox)\n",
"\n",
" # Check that logout is effective\n",
" logger.debug('To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)')\n",
" logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" # End of function process_main\n",
"\n",
"if __name__ == '__main__':\n",
" process_main()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Third step: Activate and deactivate a network scenario\n",
"\n",
"Having a list of network scenarion, the next step is to actvate (and deactivate) a network scenario. This step is mandatory to create a new application instance id and access the MEC services.\n",
"\n",
"In this section, we will arbitrary activate the network scenario called '4g-5g-macro-v2x', which is at the index 0 of the nw_scenarios. "
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def select_network_scenario_based_on_criteria(criterias_list: list) -> int:\n",
" \"\"\"\n",
" Select the network scenario to activate based of the provided list of criterias.\n",
" :param criterias_list: The list of criterias to select the correct network scenario\n",
" :return: 0 on success, -1 otherwise\n",
" \"\"\"\n",
"\n",
" return 0 # The index of the '4g-5g-macro-v2x' network scenario - Hard coded"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The activate function\n",
"\n",
"The process to activate a scenario is based on an HTTP POST request with the URL '/sandboxNetworkScenarios/{sandbox_name}?network_scenario_id={network_scenario_id}'.\n"
]
},
{
"cell_type": "code",
"def activate_network_scenario(sandbox_name: str) -> int:\n",
" \"\"\"\n",
" Activate the specified network scenario.\n",
" :param sandbox_name: The MEC Sandbox instance to use\n",
" :return: 0 on success, -1 otherwise\n",
" \"\"\"\n",
" global logger, sandbox_api, nw_scenarios, nw_scenario_idx\n",
" logger.debug('>>> activate_network_scenario: ' + sandbox_name)\n",
"\n",
" nw_scenario_idx = select_network_scenario_based_on_criteria([])\n",
" if nw_scenario_idx == -1:\n",
" logger.error('activate_network_scenario: Failed to select a network scenarion')\n",
" nw = swagger_client.SandboxNetworkScenariosApi(sandbox_api)\n",
" nw.sandbox_network_scenario_post(sandbox_name, nw_scenarios[nw_scenario_idx].id, async_req = False) # noqa: E501\n",
" logger.error('Exception when calling SandboxNetworkScenariosApi->activate_network_scenario: %s\\n' % e)\n",
"\n",
" return -1\n",
" # End of function activate_network_scenario\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The deactivate function\n",
"\n",
"The process to deactivate a scenario is based on an HTTP DELETE request with the URL '/sandboxNetworkScenarios/{sandbox_name}?network_scenario_id={network_scenario_id}'.\n"
]
},
{
"cell_type": "code",
"def deactivate_network_scenario(sandbox: str) -> int:\n",
" \"\"\"\n",
" Deactivate the current network scenario.\n",
" :param sandbox: The MEC Sandbox instance to use\n",
" :return: 0 on success, -1 otherwise\n",
" \"\"\"\n",
" global logger, sandbox_api, nw_scenarios, nw_scenario_idx\n",
" logger.debug('>>> deactivate_network_scenario: ' + sandbox)\n",
" nw = swagger_client.SandboxNetworkScenariosApi(sandbox_api)\n",
" nw.sandbox_network_scenario_delete(sandbox, nw_scenarios[nw_scenario_idx].id, async_req = False) # noqa: E501\n",
" return 0\n",
" logger.error('Exception when calling SandboxNetworkScenariosApi->deactivate_network_scenario: %s\\n' % e)\n",
"\n",
" return -1\n",
" # End of function deactivate_network_scenario\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Putting everything together\n",
"\n",
"Now, it is time to create the second iteration of our MEC application.\n",
"\n",
"The sequence is the following:\n",
"- Login\n",
"- Print sandbox identifier\n",
"- Print available network scenarios\n",
"- Activate a network scenario\n",
"- Check that the network scenario is activated and the MEC services are running\n",
"- Deactivate a network scenario\n",
"- Logout\n",
"- Check that logout is effective\n",
"\n"
]
},
{
"cell_type": "code",
"# Uncomment the ;line above to skip execution of this cell\n",
"def process_main():\n",
" \"\"\"\n",
" This is the second sprint of our skeleton of our MEC application:\n",
" - Login\n",
" - Print sandbox identifier\n",
" - Print available network scenarios\n",
" - Activate a network scenario\n",
" - Check that the network scenario is activated and the MEC services are running\n",
" - Deactivate a network scenario\n",
" - Logout\n",
" - Check that logout is effective\n",
" \"\"\" \n",
" global logger, nw_scenarios, nw_scenario_idx\n",
" logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" logger.debug('\\t pwd= ' + os.getcwd())\n",
"\n",
" # Login\n",
" sandbox = process_login()\n",
" if sandbox is None:\n",
" logger.error('Failed to instanciate a MEC Sandbox')\n",
" return\n",
"\n",
" # Print sandbox identifier\n",
" logger.info('Sandbox created: ' + sandbox)\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
"\n",
" # Print available network scenarios\n",
" nw_scenarios = get_network_scenarios(sandbox)\n",
" if nw_scenarios is None:\n",
" logger.error('Failed to retrieve the list of network scenarios')\n",
" logger.info('nw_scenarios: %s', str(type(nw_scenarios[0])))\n",
" logger.info('nw_scenarios: %s', str(nw_scenarios))\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
" logger.info('nw_scenarios: No scenario available')\n",
"\n",
" # Activate a network scenario based on a list of criterias (hard coded!!!)\n",
" if activate_network_scenario(sandbox) == -1:\n",
" logger.error('Failed to activate network scenario')\n",
" logger.info('Network scenario activated: ' + nw_scenarios[nw_scenario_idx].id)\n",
" time.sleep(2 * STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
"\n",
" # Check that the network scenario is activated and the MEC services are running \n",
" logger.info('To check that the network scenario is activated, verify on the MEC Sandbox server that the MEC services are running (kubectl get pods -A)')\n",
" time.sleep(30) # Sleep for 30 seconds\n",
"\n",
" # Deactivate a network scenario based on a list of criterias (hard coded!!!)\n",
" if deactivate_network_scenario(sandbox) == -1:\n",
" logger.error('Failed to deactivate network scenario')\n",
" logger.info('Network scenario deactivated: ' + nw_scenarios[nw_scenario_idx].id)\n",
" time.sleep(2 * STABLE_TIME_OUT)\n",
"\n",
" # Logout\n",
" process_logout(sandbox)\n",
"\n",
" # Check that logout is effective\n",
" logger.debug('To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)')\n",
" logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" # End of function process_main\n",
"\n",
"if __name__ == '__main__':\n",
" process_main()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fourth step: Create and delete an appliction instance id\n",
"\n",
"To enable our MEC application to be part of the activated network scenario, we need to request the MEC sandbox to create a new application instance identifier. Our MEC application will use this identifier to register to the MEC Sandbox according to MEC 011.\n",
"Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 5.2.2 MEC application start-up\n",
"\n",
"\n",
"It is like the MEC application was instanciated by the MEC platform and it is executed locally.\n"
"def request_application_instance_id(sandbox_name: str) -> swagger_client.models.ApplicationInfo:\n",
" Request the creation of a new MEC application instance identifier.\n",
" It is like the MEC application was instanciated by the MEC platform and it is executed locally.\n",
" :param sandbox_name: The MEC Sandbox instance to use\n",
" :return: The MEC application instance identifier on success, None otherwise\n",
" :see ETSI GS MEC 011 V3.2.1 (2024-04) Clause 5.2.2 MEC application start-up\n",
" global MEC_PLTF, logger, sandbox_api, configuration\n",
" logger.debug('>>> request_application_instance_id: ' + sandbox_name)\n",
"\n",
" # Create a instance of our MEC application\n",
" try:\n",
" a = swagger_client.models.ApplicationInfo(id=str(uuid.uuid4()), name='JupyterMecApp', node_name=MEC_PLTF, type='USER') # noqa: E501\n",
" nw = swagger_client.SandboxAppInstancesApi(sandbox_api)\n",
" result = nw.sandbox_app_instances_post(a, sandbox_name, async_req = False) # noqa: E501\n",
" logger.debug('request_application_instance_id: result: ' + str(result))\n",
" return result\n",
" except ApiException as e:\n",
" logger.error('Exception when calling SandboxAppInstancesApi->sandbox_app_instances_post: %s\\n' % e)\n",
"\n",
" return None\n",
" # End of function request_application_instance_id"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### The appliction instance id deletion function"
"def delete_application_instance_id(sandbox_name: str, app_inst_id: str) -> int:\n",
" Request the deletion of a MEC application.\n",
" :param sandbox: The MEC Sandbox instance to use\n",
" :param app_inst_id: The MEC application instance identifier\n",
" :return: 0 on success, -1 otherwise\n",
" global logger, sandbox_api, configuration\n",
" logger.debug('>>> delete_application_instance_id: ' + sandbox_name)\n",
" logger.debug('>>> delete_application_instance_id: ' + app_inst_id)\n",
" nw = swagger_client.SandboxAppInstancesApi(sandbox_api)\n",
" nw.sandbox_app_instances_delete(sandbox_name, app_inst_id, async_req = False) # noqa: E501\n",
" return 0\n",
" logger.error('Exception when calling SandboxAppInstancesApi->sandbox_app_instances_delete: %s\\n' % e)\n",
"\n",
" return -1\n",
" # End of function deletet_application_instance_id"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Getting the list of applications"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def get_applications_list(sandbox_name: str) -> list:\n",
" Request the list of the MEC application available on the MEC Platform.\n",
" :param sandbox: The MEC Sandbox instance to use\n",
" :return: 0 on success, -1 otherwise\n",
" global logger, sandbox_api, configuration\n",
" logger.debug('>>> get_applications_list: ' + sandbox_name)\n",
" nw = swagger_client.SandboxAppInstancesApi(sandbox_api)\n",
" result = nw.sandbox_app_instances_get(sandbox_name, async_req = False) # noqa: E501\n",
" logger.debug('get_applications_list: result: ' + str(result))\n",
" return result\n",
" except ApiException as e:\n",
" logger.error('Exception when calling SandboxAppInstancesApi->get_applications_list: %s\\n' % e)\n",
" return None \n",
" # End of function delete_application_instance_id"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Putting everything together\n",
"\n",
"It is time now to create the our third iteration of our MEC application.\n",
"\n",
"The sequence is the following:\n",
"- Login\n",
"- Print sandbox identifier\n",
"- Print available network scenarios\n",
"- Activate a network scenario\n",
"- Request for a new application instance identifier\n",
"- Retrieve the list of the applications instance identifier\n",
"- Check the demo application is present in the list of applications\n",
"- Delete our application instance identifier\n",
"- Deactivate a network scenario\n",
"- Logout\n",
"- Check that logout is effective\n"
]
},
{
"cell_type": "code",
"# Uncomment the ;line above to skip execution of this cell\n",
"def process_main():\n",
" \"\"\"\n",
" This is the second sprint of our skeleton of our MEC application:\n",
" - Login\n",
" - Print sandbox identifier\n",
" - Print available network scenarios\n",
" - Activate a network scenario\n",
" - Request for a new application instance identifier\n",
" - Retrieve the list of the applications instance identifier\n",
" - Check the demo application is present in the list of applications\n",
" - Deactivate a network scenario\n",
" - Logout\n",
" - Check that logout is effective\n",
" \"\"\" \n",
" global logger, nw_scenarios, nw_scenario_idx\n",
" logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" logger.debug('\\t pwd= ' + os.getcwd())\n",
"\n",
" # Login\n",
" sandbox = process_login()\n",
" if sandbox is None:\n",
" logger.error('Failed to instanciate a MEC Sandbox')\n",
" return\n",
"\n",
" # Print sandbox identifier\n",
" logger.info('Sandbox created: ' + sandbox)\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
"\n",
" # Print available network scenarios\n",
" nw_scenarios = get_network_scenarios(sandbox)\n",
" if nw_scenarios is None:\n",
" logger.error('Failed to retrieve the list of network scenarios')\n",
" logger.info('nw_scenarios: %s', str(type(nw_scenarios[0])))\n",
" logger.info('nw_scenarios: %s', str(nw_scenarios))\n",
" time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
" logger.info('nw_scenarios: No scenario available')\n",
"\n",
" # Activate a network scenario based on a list of criterias (hard coded!!!)\n",
" if activate_network_scenario(sandbox) == -1:\n",
" logger.error('Failed to activate network scenario')\n",
" logger.info('Network scenario activated: ' + nw_scenarios[nw_scenario_idx].id)\n",
" time.sleep(2 * STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
" # Request for a new application instance identifier\n",
" app_inst_id = request_application_instance_id(sandbox)\n",
" if app_inst_id == None:\n",
" logger.error('Failed to request an application instance identifier')\n",
" logger.info('app_inst_id: %s', str(type(app_inst_id)))\n",
" logger.info('app_inst_id: %s', str(app_inst_id))\n",
" # Check the demo application is present in the list of applications\n",
" app_list = get_applications_list(sandbox)\n",
" if app_list is None:\n",
" logger.error('Failed to request the list of applications')\n",
" logger.info('app_list: %s', str(type(app_list)))\n",
" logger.info('app_list: %s', str(app_list))\n",
" # Check if our application is present in the list of applications\n",
" found = False\n",
" for item in app_list:\n",
" if item.id == app_inst_id.id:\n",
" found = True\n",
" break\n",
" if not found:\n",
" logger.error('Failed to retrieve our application instance identifier')\n",
" # Delete the application instance identifier\n",
" if delete_application_instance_id(sandbox, app_inst_id.id) == -1:\n",
" logger.error('Failed to delete the application instance identifier')\n",
" logger.info('app_inst_id deleted: ' + app_inst_id.id)\n",
"\n",
" # Deactivate a network scenario based on a list of criterias (hard coded!!!)\n",
" if deactivate_network_scenario(sandbox) == -1:\n",
" logger.error('Failed to deactivate network scenario')\n",
" logger.info('Network scenario deactivated: ' + nw_scenarios[nw_scenario_idx].id)\n",
" time.sleep(2 * STABLE_TIME_OUT)\n",
"\n",
" # Logout\n",
" process_logout(sandbox)\n",
"\n",
" # Check that logout is effective\n",
" logger.debug('To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)')\n",
" logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
" # End of function process_main\n",
"\n",
"if __name__ == '__main__':\n",
" process_main()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## MEC Registration and the READY confirmation\n",
"\n",
"Having an application instance identifier allows us to register with the MEC Sandbox and interact with it (e.g. to send service queries, to subscribe to events and to recieve notifications...).\n",
"\n",
"The standard MEC 011 Clause 5.2.2 MEC application start-up describes the start up process. Basically, our MEC application has to:\n",
"1. Indicates that it is running by sending a Confirm Ready message\n",
"2. Retrieve the list of MEC services \n",
"\n",
"To do so, a MEC application needs to be able to send requests but also to receive notifications (POST requests) and to reply to them."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fifth step: Send the READY confirmation\n",
"\n",
"The MEC application instance confirms towards the MEC platform that it is up and running. It corresponds to step 4c described inETSI GS MEC 011 V3.2.1 (2024-04)11 Clause 5.2.2 MEC application start-up.\n"
"metadata": {},
"outputs": [],
"source": [
"def send_ready_confirmation(sandbox_name: str, app_inst_id: swagger_client.models.application_info.ApplicationInfo) -> int:\n",
" \"\"\"\n",
" Send the ready_confirmation to indicate that the MEC application is active.\n",
" :param sandbox_name: The MEC Sandbox instance to use\n",
" :param app_inst_id: The MEC application instance identifier\n",
" :return: 0 on success, -1 otherwise\n",
" :see ETSI GS MEC 011 V3.2.1 (2024-04) Clause 5.2.2 MEC application start-up - Step 4c\n",
" \"\"\"\n",
" global MEC_PLTF, logger, service_api\n",
" logger.debug('>>> send_ready_confirmation: ' + app_inst_id.id)\n",
" url = '/{sandbox_name}/{mec_pltf}/mec_app_support/v2/applications/{app_inst_id}/confirm_ready'\n",
" logger.debug('send_ready_confirmation: url: ' + url)\n",
" path_params = {}\n",
" path_params['sandbox_name'] = sandbox_name\n",
" path_params['mec_pltf'] = MEC_PLTF\n",
" path_params['app_inst_id'] = app_inst_id.id\n",
" header_params = {}\n",
" # HTTP header `Accept`\n",
" header_params['Accept'] = 'application/json' # noqa: E501\n",
" header_params['Content-Type'] = 'application/json' # noqa: E501\n",
" # JSON indication READY\n",