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 appcation 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://forge.etsi.org/rep/mec/AdvantEDGE/-/blob/Task2_PoC/go-apps/meep-sandbox-sandbox_api/sandbox_api/swagger.yaml) and the [Swagger editor](https://editor-next.swagger.io/) is used to generate the python sub.\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://forge.etsi.org/rep/mec/AdvantEDGE/-/blob/Task2_PoC/go-apps/meep-sandbox-sandbox_api/sandbox_api/swagger.yaml).\n",
"The model folder contains the python implementation of the data type definitions introduced by the openApi [file](https://forge.etsi.org/rep/mec/AdvantEDGE/-/blob/Task2_PoC/go-apps/meep-sandbox-sandbox_api/sandbox_api/swagger.yaml).\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",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/home/yanng/dev/etsi-mec-sandbox/examples/demo6/python/mecapp\n"
]
}
],
"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 = 6 # 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 = 36001 # Listener IPv4 port for notification callback calls\n",
"CALLBACK_URI = 'http://yanngarcia.ddns.net:36001/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": [
"# Initialize the global variables\n",
"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"
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
239
240
241
242
]
},
{
"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",
" return -1\n",
" # End of function process_logout\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, let put in action our Login/Logout functions:"
]
},
{
"cell_type": "code",
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:14:38,661 - __main__ - DEBUG - Starting at 20241014-071438\n",
"2024-10-14 07:14:38,662 - __main__ - DEBUG - \t pwd= /home/yanng/dev/etsi-mec-sandbox/examples/demo6/python/mecapp\n",
"2024-10-14 07:14:38,663 - __main__ - DEBUG - >>> process_login\n",
"2024-10-14 07:14:38,663 DEBUG Starting new HTTPS connection (1): mec-platform2.etsi.org:443\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 2\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"send: b'{}'\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:14:38,980 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/1.1\" 201 48\n",
"2024-10-14 07:14:38,983 DEBUG response body: b'{\"user_code\":\"sbxu1qsfhq\",\"verification_uri\":\"\"}'\n",
"2024-10-14 07:14:38,986 - __main__ - DEBUG - process_login (step1): oauth: {'user_code': 'sbxu1qsfhq', 'verification_uri': ''}\n",
"2024-10-14 07:14:38,988 - __main__ - DEBUG - =======================> DO AUTHORIZATION WITH CODE : sbxu1qsfhq\n",
"2024-10-14 07:14:38,989 - __main__ - DEBUG - =======================> DO AUTHORIZATION HERE : \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"reply: 'HTTP/1.1 201 Created\\r\\n'\n",
"header: Date: Mon, 14 Oct 2024 05:14:38 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Content-Length: 48\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:14:42,012 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/namespace?user_code=sbxu1qsfhq HTTP/1.1\" 200 29\n",
"2024-10-14 07:14:42,013 DEBUG response body: b'{\"sandbox_name\":\"sbxu1qsfhq\"}'\n",
"2024-10-14 07:14:42,014 - __main__ - DEBUG - process_login (step2): result: {'sandbox_name': 'sbxu1qsfhq'}\n",
"2024-10-14 07:14:42,016 - __main__ - INFO - Sandbox created: sbxu1qsfhq\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'GET /sandbox-api/v1/namespace?user_code=sbxu1qsfhq HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"reply: 'HTTP/1.1 200 OK\\r\\n'\n",
"header: Date: Mon, 14 Oct 2024 05:14:41 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Content-Length: 29\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"ename": "NameError",
"evalue": "name 'process_logout' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[7], line 35\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;66;03m# End of function process_main\u001b[39;00m\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m---> 35\u001b[0m \u001b[43mprocess_main\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[0;32mIn[7], line 26\u001b[0m, in \u001b[0;36mprocess_main\u001b[0;34m()\u001b[0m\n\u001b[1;32m 23\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(STABLE_TIME_OUT) \u001b[38;5;66;03m# Wait for k8s pods up and running\u001b[39;00m\n\u001b[1;32m 25\u001b[0m \u001b[38;5;66;03m# Logout\u001b[39;00m\n\u001b[0;32m---> 26\u001b[0m \u001b[43mprocess_logout\u001b[49m(sandbox)\n\u001b[1;32m 28\u001b[0m \u001b[38;5;66;03m# Check that logout is effective\u001b[39;00m\n\u001b[1;32m 29\u001b[0m logger\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mTo check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
"\u001b[0;31mNameError\u001b[0m: name 'process_logout' is not defined"
]
}
],
"source": [
"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",
"metadata": {},
"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",
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
"\n",
" return None\n",
" # End of function get_network_scenarios\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putting everything together:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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",
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
"\n",
" return -1\n",
" # End of function deactivate_network_scenario\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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 \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": [
"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",
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:15:57,420 - __main__ - DEBUG - Starting at 20241014-071557\n",
"2024-10-14 07:15:57,421 - __main__ - DEBUG - \t pwd= /home/yanng/dev/etsi-mec-sandbox/examples/demo6/python/mecapp\n",
"2024-10-14 07:15:57,422 - __main__ - DEBUG - >>> process_login\n",
"2024-10-14 07:15:57,423 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-10-14 07:15:57,540 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/1.1\" 201 48\n",
"2024-10-14 07:15:57,541 DEBUG response body: b'{\"user_code\":\"sbx1urljfj\",\"verification_uri\":\"\"}'\n",
"2024-10-14 07:15:57,542 - __main__ - DEBUG - process_login (step1): oauth: {'user_code': 'sbx1urljfj', 'verification_uri': ''}\n",
"2024-10-14 07:15:57,542 - __main__ - DEBUG - =======================> DO AUTHORIZATION WITH CODE : sbx1urljfj\n",
"2024-10-14 07:15:57,543 - __main__ - DEBUG - =======================> DO AUTHORIZATION HERE : \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 2\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"send: b'{}'\n",
"reply: 'HTTP/1.1 201 Created\\r\\n'\n",
"header: Date: Mon, 14 Oct 2024 05:15:57 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Content-Length: 48\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:16:00,570 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/namespace?user_code=sbx1urljfj HTTP/1.1\" 200 29\n",
"2024-10-14 07:16:00,571 DEBUG response body: b'{\"sandbox_name\":\"sbx1urljfj\"}'\n",
"2024-10-14 07:16:00,572 - __main__ - DEBUG - process_login (step2): result: {'sandbox_name': 'sbx1urljfj'}\n",
"2024-10-14 07:16:00,573 - __main__ - INFO - Sandbox created: sbx1urljfj\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'GET /sandbox-api/v1/namespace?user_code=sbx1urljfj HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"reply: 'HTTP/1.1 200 OK\\r\\n'\n",
"header: Date: Mon, 14 Oct 2024 05:16:00 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Content-Length: 29\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:16:06,580 - __main__ - DEBUG - >>> get_network_scenarios: sandbox=sbx1urljfj\n",
"2024-10-14 07:16:06,581 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-10-14 07:16:06,684 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbx1urljfj HTTP/1.1\" 200 186\n",
"2024-10-14 07:16:06,685 DEBUG response body: b'[{\"id\":\"4g-5g-macro-v2x\"},{\"id\":\"4g-5g-macro-v2x-fed\"},{\"id\":\"4g-5g-wifi-macro\"},{\"id\":\"4g-macro\"},{\"id\":\"4g-wifi-macro\"},{\"id\":\"dual-mep-4g-5g-wifi-macro\"},{\"id\":\"dual-mep-short-path\"}]'\n",
"2024-10-14 07:16:06,686 - __main__ - DEBUG - get_network_scenarios: result: [{'id': '4g-5g-macro-v2x'}, {'id': '4g-5g-macro-v2x-fed'}, {'id': '4g-5g-wifi-macro'}, {'id': '4g-macro'}, {'id': '4g-wifi-macro'}, {'id': 'dual-mep-4g-5g-wifi-macro'}, {'id': 'dual-mep-short-path'}]\n",
"2024-10-14 07:16:06,687 - __main__ - INFO - nw_scenarios: <class 'swagger_client.models.sandbox_network_scenario.SandboxNetworkScenario'>\n",
"2024-10-14 07:16:06,687 - __main__ - INFO - nw_scenarios: [{'id': '4g-5g-macro-v2x'}, {'id': '4g-5g-macro-v2x-fed'}, {'id': '4g-5g-wifi-macro'}, {'id': '4g-macro'}, {'id': '4g-wifi-macro'}, {'id': 'dual-mep-4g-5g-wifi-macro'}, {'id': 'dual-mep-short-path'}]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbx1urljfj HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nAccept: application/json\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"reply: 'HTTP/1.1 200 OK\\r\\n'\n",
"header: Date: Mon, 14 Oct 2024 05:16:06 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Content-Length: 186\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-10-14 07:16:12,694 - __main__ - DEBUG - >>> activate_network_scenario: sbx1urljfj\n",
"2024-10-14 07:16:12,749 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/sandboxNetworkScenarios/sbx1urljfj?network_scenario_id=4g-5g-macro-v2x HTTP/1.1\" 204 0\n",
"2024-10-14 07:16:12,751 DEBUG response body: b''\n",
"2024-10-14 07:16:12,752 - __main__ - INFO - Network scenario activated: 4g-5g-macro-v2x\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'POST /sandbox-api/v1/sandboxNetworkScenarios/sbx1urljfj?network_scenario_id=4g-5g-macro-v2x HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nUser-Agent: Swagger-Codegen/1.0.0/python\\r\\n\\r\\n'\n",
"send: b'{}'\n",
"reply: 'HTTP/1.1 204 No Content\\r\\n'\n",