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://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"
]
},
{
"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/jovyan/work/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 = 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"
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",
"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": {
"jupyter": {
"source_hidden": true
}
},
"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": [
"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",
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
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
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-12 13:17:20,694 - __main__ - DEBUG - Starting at 20241112-131720\n",
"2024-11-12 13:17:20,695 - __main__ - DEBUG - \t pwd= /home/jovyan/work/mecapp\n",
"2024-11-12 13:17:20,696 - __main__ - DEBUG - >>> process_login\n",
"2024-11-12 13:17:20,696 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-11-12 13:17:20,779 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/11\" 201 0\n",
"2024-11-12 13:17:20,780 DEBUG response body: b'{\"user_code\":\"sbxxl3m7gk\",\"verification_uri\":\"\"}'\n",
"2024-11-12 13:17:20,781 - __main__ - DEBUG - process_login (step1): oauth: {'user_code': 'sbxxl3m7gk', 'verification_uri': ''}\n",
"2024-11-12 13:17:20,782 - __main__ - DEBUG - =======================> DO AUTHORIZATION WITH CODE : sbxxl3m7gk\n",
"2024-11-12 13:17:20,783 - __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: Tue, 12 Nov 2024 13:17:20 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-11-12 13:17:23,799 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/namespace?user_code=sbxxl3m7gk HTTP/11\" 200 0\n",
"2024-11-12 13:17:23,800 DEBUG response body: b'{\"sandbox_name\":\"sbxxl3m7gk\"}'\n",
"2024-11-12 13:17:23,802 - __main__ - DEBUG - process_login (step2): result: {'sandbox_name': 'sbxxl3m7gk'}\n",
"2024-11-12 13:17:23,802 - __main__ - INFO - Sandbox created: sbxxl3m7gk\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'GET /sandbox-api/v1/namespace?user_code=sbxxl3m7gk 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: Tue, 12 Nov 2024 13:17:23 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-11-12 13:17:33,803 - __main__ - DEBUG - >>> get_network_scenarios: sandbox=sbxxl3m7gk\n",
"2024-11-12 13:17:33,804 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-11-12 13:17:33,916 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbxxl3m7gk HTTP/11\" 200 0\n",
"2024-11-12 13:17:33,917 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-11-12 13:17:33,918 - __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-11-12 13:17:33,918 - __main__ - INFO - nw_scenarios: <class 'swagger_client.models.sandbox_network_scenario.SandboxNetworkScenario'>\n",
"2024-11-12 13:17:33,919 - __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=sbxxl3m7gk 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: Tue, 12 Nov 2024 13:17:33 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-11-12 13:17:43,920 - __main__ - DEBUG - >>> activate_network_scenario: sbxxl3m7gk\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'POST /sandbox-api/v1/sandboxNetworkScenarios/sbxxl3m7gk?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"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-12 13:17:44,933 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/sandboxNetworkScenarios/sbxxl3m7gk?network_scenario_id=4g-5g-macro-v2x HTTP/11\" 204 0\n",
"2024-11-12 13:17:44,934 DEBUG response body: b''\n",
"2024-11-12 13:17:44,940 - __main__ - INFO - Network scenario activated: 4g-5g-macro-v2x\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"reply: 'HTTP/1.1 204 No Content\\r\\n'\n",
"header: Date: Tue, 12 Nov 2024 13:17:44 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-12 13:18:04,940 - __main__ - 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",
"2024-11-12 13:18:34,941 - __main__ - DEBUG - >>> deactivate_network_scenario: sbxxl3m7gk\n",
"2024-11-12 13:18:34,943 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-11-12 13:18:35,247 DEBUG https://mec-platform2.etsi.org:443 \"DELETE /sandbox-api/v1/sandboxNetworkScenarios/sbxxl3m7gk/4g-5g-macro-v2x HTTP/11\" 204 0\n",
"2024-11-12 13:18:35,248 DEBUG response body: b''\n",
"2024-11-12 13:18:35,249 - __main__ - INFO - Network scenario deactivated: 4g-5g-macro-v2x\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'DELETE /sandbox-api/v1/sandboxNetworkScenarios/sbxxl3m7gk/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",
"header: Date: Tue, 12 Nov 2024 13:18:35 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-12 13:18:55,251 - __main__ - DEBUG - >>> process_logout: sandbox=sbxxl3m7gk\n",
"2024-11-12 13:18:55,252 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
"2024-11-12 13:18:55,301 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/logout?sandbox_name=sbxxl3m7gk HTTP/11\" 204 0\n",
"2024-11-12 13:18:55,302 DEBUG response body: b''\n",
"2024-11-12 13:18:55,302 - __main__ - DEBUG - To check that logout is effective, verify on the MEC Sandbox server that the MEC Sandbox is removed (kubectl get pods -A)\n",
"2024-11-12 13:18:55,302 - __main__ - DEBUG - Stopped at 20241112-131855\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"send: b'POST /sandbox-api/v1/logout?sandbox_name=sbxxl3m7gk 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",
"header: Date: Tue, 12 Nov 2024 13:18:55 GMT\n",
"header: Content-Type: application/json; charset=UTF-8\n",
"header: Connection: keep-alive\n",
"header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
]
}
],
"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, 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"