Skip to content
MEC application.ipynb 219 KiB
Newer Older
Yann Garcia's avatar
Yann Garcia committed
{
 "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",
    "<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>"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "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",
Yann Garcia's avatar
Yann Garcia committed
    "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",
Yann Garcia's avatar
Yann Garcia committed
    "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",
Yann Garcia's avatar
Yann Garcia committed
    "    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",
Yann Garcia's avatar
Yann Garcia committed
    "## 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",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "The project architecture is describe [here](images/project_arch.jpg).\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
    "directory:"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before going to create our MEC application skeleton, the following steps shall be done:\n",
Yann Garcia's avatar
Yann Garcia committed
    "1) Change the working directory (see the project architecture)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/home/yanng/dev/etsi-mec-sandbox/examples/demo6/python/mecapp\n"
     ]
    }
   ],
Yann Garcia's avatar
Yann Garcia committed
   "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"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
Yann Garcia's avatar
Yann Garcia committed
   "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",
Yann Garcia's avatar
Yann Garcia committed
    "import logging\n",
    "import threading\n",
Yann Garcia's avatar
Yann Garcia committed
    "import time\n",
    "import json\n",
    "import uuid\n",
    "\n",
    "import pprint\n",
    "\n",
    "import six\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "import swagger_client\n",
Yann Garcia's avatar
Yann Garcia committed
    "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"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3) Initialize of the global constants (cell 3)"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "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",
Yann Garcia's avatar
Yann Garcia committed
    "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",
Yann Garcia's avatar
Yann Garcia committed
    "LISTENER_IP         = '0.0.0.0'                                              # Listener IPv4 address for notification callback calls\n",
Yann Garcia's avatar
Yann Garcia committed
    "LISTENER_PORT       = 36001                                                  # Listener IPv4 port for notification callback calls\n",
    "CALLBACK_URI        = 'http://yanngarcia.ddns.net:36001/sandbox/v1'\n",
Yann Garcia's avatar
Yann Garcia committed
    "                                                                             #'https://yanngarcia.ddns.net:' + str(LISTENER_PORT) + '/jupyter/sandbox/demo6/v1/'"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "4) Setup the logger instance and the HTTP REST API (cell 4)"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
Yann Garcia's avatar
Yann Garcia committed
   "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",
Yann Garcia's avatar
Yann Garcia committed
    "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",
Yann Garcia's avatar
Yann Garcia committed
    "configuration               = swagger_client.Configuration()\n",
Yann Garcia's avatar
Yann Garcia committed
    "configuration.host          = MEC_SANDBOX_API_URL\n",
    "configuration.verify_ssl    = True\n",
Yann Garcia's avatar
Yann Garcia committed
    "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",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
Yann Garcia's avatar
Yann Garcia committed
    "# 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"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "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",
Yann Garcia's avatar
Yann Garcia committed
   "source": [
    "#### The login function\n",
    "\n",
    "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"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "# Login\n",
    "def process_login() -> str:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    Authenticate and create a new MEC Sandbox instance.\n",
    "    :return: The sandbox instance identifier on success, None otherwise\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\" \n",
    "    global PROVIDER, logger\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> process_login')\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    try:\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",
Yann Garcia's avatar
Yann Garcia committed
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling AuthorizationApi->login: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "# Logout\n",
    "def process_logout(sandbox_name: str) -> int:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    Delete the specified MEC Sandbox instance.\n",
    "    :param sandbox_name: The MEC Sandbox to delete\n",
Yann Garcia's avatar
Yann Garcia committed
    "    :return: 0 on success, -1 otherwise\n",
    "    \"\"\"\n",
    "    global logger\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> process_logout: sandbox=' + sandbox_name)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    try:\n",
    "        auth = swagger_client.AuthorizationApi(sandbox_api)\n",
    "        result = auth.logout(sandbox_name, async_req = False)  # noqa: E501\n",
Yann Garcia's avatar
Yann Garcia committed
    "        return 0\n",
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling AuthorizationApi->logout: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "    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",
   "execution_count": 7,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "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"
     ]
    }
   ],
Yann Garcia's avatar
Yann Garcia committed
   "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",
    "    global logger\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
    "    logger.debug('\\t pwd= ' + os.getcwd())\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "    # Wait for the MEC Sandbox is running\n",
Yann Garcia's avatar
Yann Garcia committed
    "    time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "  \n",
    "    logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # 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}'."
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_network_scenarios(sandbox_name: str) -> list:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    Retrieve the list of the available network scenarios.\n",
    "    :param sandbox_name: The MEC Sandbox instance to use\n",
Yann Garcia's avatar
Yann Garcia committed
    "    :return: The list of the available network scenarios on success, None otherwise\n",
    "    \"\"\"\n",
    "    global PROVIDER, logger, sandbox_api, configuration\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> get_network_scenarios: sandbox=' + sandbox_name)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    try:\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",
Yann Garcia's avatar
Yann Garcia committed
    "        return result\n",
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling SandboxNetworkScenariosApi->sandbox_network_scenarios_get: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
    "    logger.debug('\\t pwd= ' + os.getcwd())\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    # Login\n",
    "    sandbox = process_login()\n",
    "    if sandbox is None:\n",
    "        logger.error('Failed to instanciate a MEC Sandbox')\n",
Yann Garcia's avatar
Yann Garcia committed
    "        return\n",
    "\n",
    "    # Print sandbox identifier\n",
    "    logger.info('Sandbox created: ' + sandbox)\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # Wait for the MEC Sandbox is running\n",
Yann Garcia's avatar
Yann Garcia committed
    "    time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "    elif len(nw_scenarios) != 0:\n",
    "        logger.info('nw_scenarios: %s', str(type(nw_scenarios[0])))\n",
    "        logger.info('nw_scenarios: %s', str(nw_scenarios))\n",
Yann Garcia's avatar
Yann Garcia committed
    "    else:\n",
    "        logger.info('nw_scenarios: No scenario available')\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "  \n",
    "    logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # 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",
   "execution_count": 10,
Yann Garcia's avatar
Yann Garcia committed
   "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",
Yann Garcia's avatar
Yann Garcia committed
    "    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",
   "execution_count": 11,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "def activate_network_scenario(sandbox_name: str) -> int:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    Activate the specified network scenario.\n",
    "    :param sandbox_name: The MEC Sandbox instance to use\n",
Yann Garcia's avatar
Yann Garcia committed
    "    :return: 0 on success, -1 otherwise\n",
    "    \"\"\"\n",
    "    global logger, sandbox_api, nw_scenarios, nw_scenario_idx\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> activate_network_scenario: ' + sandbox_name)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "        return -1\n",
    "\n",
    "    try:\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",
Yann Garcia's avatar
Yann Garcia committed
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling SandboxNetworkScenariosApi->activate_network_scenario: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
   "execution_count": 12,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "def deactivate_network_scenario(sandbox: str) -> int:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\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",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> deactivate_network_scenario: ' + sandbox)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    try:\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",
Yann Garcia's avatar
Yann Garcia committed
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling SandboxNetworkScenariosApi->deactivate_network_scenario: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
    "    logger.debug('\\t pwd= ' + os.getcwd())\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    # Login\n",
    "    sandbox = process_login()\n",
    "    if sandbox is None:\n",
    "        logger.error('Failed to instanciate a MEC Sandbox')\n",
Yann Garcia's avatar
Yann Garcia committed
    "        return\n",
    "\n",
    "    # Print sandbox identifier\n",
    "    logger.info('Sandbox created: ' + sandbox)\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # Wait for the MEC Sandbox is running\n",
Yann Garcia's avatar
Yann Garcia committed
    "    time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "    elif len(nw_scenarios) != 0:\n",
    "        logger.info('nw_scenarios: %s', str(type(nw_scenarios[0])))\n",
    "        logger.info('nw_scenarios: %s', str(nw_scenarios))\n",
Yann Garcia's avatar
Yann Garcia committed
    "        # Wait for the MEC Sandbox is running\n",
Yann Garcia's avatar
Yann Garcia committed
    "        time.sleep(STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
Yann Garcia's avatar
Yann Garcia committed
    "    else:\n",
    "        logger.info('nw_scenarios: No scenario available')\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "    else:\n",
    "        logger.info('Network scenario activated: ' + nw_scenarios[nw_scenario_idx].id)\n",
Yann Garcia's avatar
Yann Garcia committed
    "        # Wait for the MEC services are running\n",
    "        time.sleep(2 * STABLE_TIME_OUT) # Wait for k8s pods up and running\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "    else:\n",
    "        logger.info('Network scenario deactivated: ' + nw_scenarios[nw_scenario_idx].id)\n",
Yann Garcia's avatar
Yann Garcia committed
    "        # Wait for the MEC services are terminated\n",
    "        time.sleep(2 * STABLE_TIME_OUT)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "  \n",
    "    logger.debug('Stopped at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # 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",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "Reference: ETSI GS MEC 011 V3.2.1 (2024-04) Clause 5.2.2 MEC application start-up\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "#### The appliction instance id creation function\n",
    "\n",
    "It is like the MEC application was instanciated by the MEC platform and it is executed locally.\n"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "def request_application_instance_id(sandbox_name: str) -> swagger_client.models.ApplicationInfo:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\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",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    global MEC_PLTF, logger, sandbox_api, configuration\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> request_application_instance_id: ' + sandbox_name)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\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",
Yann Garcia's avatar
Yann Garcia committed
    "        return result\n",
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling SandboxAppInstancesApi->sandbox_app_instances_post: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    return None\n",
    "    # End of function request_application_instance_id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### The appliction instance id deletion function"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "outputs": [],
   "source": [
    "def delete_application_instance_id(sandbox_name: str, app_inst_id: str) -> int:\n",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\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",
Yann Garcia's avatar
Yann Garcia committed
    "    \"\"\"\n",
    "    global logger, sandbox_api, configuration\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    logger.debug('>>> delete_application_instance_id: ' + sandbox_name)\n",
    "    logger.debug('>>> delete_application_instance_id: ' + app_inst_id)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    try:\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",
Yann Garcia's avatar
Yann Garcia committed
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling SandboxAppInstancesApi->sandbox_app_instances_delete: %s\\n' % e)\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "    return -1\n",
    "    # End of function deletet_application_instance_id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Getting the list of applications"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "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"
   ]
  },
Yann Garcia's avatar
Yann Garcia committed
  {
   "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",
Yann Garcia's avatar
Yann Garcia committed
    "- Deactivate a network scenario\n",
    "- Logout\n",
    "- Check that logout is effective\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
Yann Garcia's avatar
Yann Garcia committed
   "metadata": {},
   "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",