Skip to content
MEC application.ipynb 272 KiB
Newer Older
    "        return result\n",
    "    def to_str(self):\n",
    "        return pprint.pformat(self.to_dict())\n",
    "    def __repr__(self):\n",
    "        return self.to_str()\n",
    "    def __eq__(self, other):\n",
Yann Garcia's avatar
Yann Garcia committed
    "        if not isinstance(other, EndPointInfoAddresses):\n",
    "            return False\n",
    "        return self.__dict__ == other.__dict__\n",
    "    def __ne__(self, other):\n",
    "        return not self == other\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "class EndPointInfoAlternative(object):\n",
    "    swagger_types = {'alternative': 'object'}\n",
    "    attribute_map = {'alternative': 'alternative'}\n",
    "    def __init__(self, alternative=None):  # noqa: E501\n",
    "        self._alternative = None\n",
    "        self.discriminator = None\n",
    "        self.alternative = alternative\n",
    "    @property\n",
Yann Garcia's avatar
Yann Garcia committed
    "    def alternative(self):\n",
    "        return self._alternative\n",
    "    @alternative.setter\n",
    "    def alternative(self, alternative):\n",
    "        if alternative is None:\n",
    "            raise ValueError(\"Invalid value for `alternative`, must not be `None`\")  # noqa: E501\n",
    "        self._alternative = alternative\n",
    "    def to_dict(self):\n",
    "        result = {}\n",
    "        for attr, _ in six.iteritems(self.swagger_types):\n",
    "            value = getattr(self, attr)\n",
    "            if isinstance(value, list):\n",
    "                result[attr] = list(map(\n",
Yann Garcia's avatar
Yann Garcia committed
    "                    lambda x: x.to_dict() if hasattr(x, \"to_dict\") else x,\n",
    "                    value\n",
    "                ))\n",
Yann Garcia's avatar
Yann Garcia committed
    "            elif hasattr(value, \"to_dict\"):\n",
    "                result[attr] = value.to_dict()\n",
    "            elif isinstance(value, dict):\n",
    "                result[attr] = dict(map(\n",
    "                    lambda item: (item[0], item[1].to_dict())\n",
Yann Garcia's avatar
Yann Garcia committed
    "                    if hasattr(item[1], \"to_dict\") else item,\n",
    "                    value.items()\n",
    "                ))\n",
    "            else:\n",
    "                result[attr] = value\n",
Yann Garcia's avatar
Yann Garcia committed
    "        if issubclass(EndPointInfoAlternative, dict):\n",
    "            for key, value in self.items():\n",
    "                result[key] = value\n",
    "        return result\n",
    "    def to_str(self):\n",
    "        return pprint.pformat(self.to_dict())\n",
    "    def __repr__(self):\n",
    "        return self.to_str()\n",
    "    def __eq__(self, other):\n",
Yann Garcia's avatar
Yann Garcia committed
    "        if not isinstance(other, EndPointInfoAlternative):\n",
    "            return False\n",
    "        return self.__dict__ == other.__dict__\n",
    "    def __ne__(self, other):\n",
    "        return not self == other\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function below is creating an application MEC services\n",
    "\n",
    "**Note:** This is call application MEC service in opposition of a standardized MEC service exposed by the MEC Sanbox such as MEC 013, MEC 030...\n",
    "\n",
    "**Reference:** ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.2.6.3.4 POST"
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
Yann Garcia's avatar
Yann Garcia committed
    "def create_mec_service(sandbox_name: str, app_inst_id: swagger_client.models.application_info.ApplicationInfo) -> object:\n",
    "    \"\"\"\n",
    "    Request to create a new application MEC service\n",
    "    :param sandbox_name: The MEC Sandbox instance to use\n",
    "    :param app_inst_id: The MEC application instance identifier\n",
    "    :param sub_id: The subscription identifier\n",
    "    :return The HTTP response, the response status and the headers on success, None otherwise\n",
    "    :see ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.2.6.3.4 POST\n",
    "    \"\"\"\n",
    "    global MEC_PLTF, CALLBACK_URI, logger, service_api\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "    logger.debug('>>> create_mec_service')\n",
    "    try:\n",
Yann Garcia's avatar
Yann Garcia committed
    "        url = '/{sandbox_name}/{mec_pltf}/mec_service_mgmt/v1/applications/{app_inst_id}/services'\n",
    "        logger.debug('create_mec_service: url: ' + url)\n",
    "        path_params = {}\n",
    "        path_params['sandbox_name'] = sandbox_name\n",
    "        path_params['mec_pltf'] = MEC_PLTF\n",
Yann Garcia's avatar
Yann Garcia committed
    "        path_params['app_inst_id'] = app_inst_id.id\n",
    "        # HTTP header `Accept`\n",
Yann Garcia's avatar
Yann Garcia committed
    "        header_params = {}\n",
    "        header_params['Accept'] = 'application/json'  # noqa: E501\n",
    "        # HTTP header `Content-Type`\n",
    "        header_params['Content-Type'] = 'application/json'  # noqa: E501\n",
    "        # Body request\n",
Yann Garcia's avatar
Yann Garcia committed
    "        callback = CALLBACK_URI + '/statistic/v1/quantity'\n",
    "        transport_info = TransportInfo(id=str(uuid.uuid4()), name='HTTP REST API', type='REST_HTTP', protocol='HTTP', version='2.0', security=SecurityInfo(), endpoint=OneOfTransportInfoEndpoint())\n",
    "        transport_info.endpoint.uris=[EndPointInfoUris(callback)]\n",
    "        category_ref = CategoryRef(href=callback, id=str(uuid.uuid4()), name='Demo', version='1.0.0')\n",
    "        appServiceInfo = ServiceInfo(ser_name='demo6 MEC Service', ser_category=category_ref, version='1.0.0', state='ACTIVE',transport_info=transport_info, serializer='JSON')\n",
    "        (result, status, headers) = service_api.call_api(url, 'POST', header_params=header_params, path_params=path_params, body=appServiceInfo, async_req=False)\n",
    "        return (result, status, headers['Location'])\n",
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling call_api: %s\\n' % e)\n",
    "    return None\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # End of function create_mec_service"
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function below is deleting an application MEC services.\n",
    "\n",
    "**Reference:** ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.2.7.3.5 DELETE"
   ]
  },
Yann Garcia's avatar
Yann Garcia committed
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
Yann Garcia's avatar
Yann Garcia committed
   "outputs": [],
   "source": [
Yann Garcia's avatar
Yann Garcia committed
    "def delete_mec_service(resource_url: str) -> int:\n",
    "    \"\"\"\n",
    "    Request to create a new application MEC service\n",
    "    :param sandbox_name: The MEC Sandbox instance to use\n",
    "    :param app_inst_id: The MEC application instance identifier\n",
    "    :param sub_id: The subscription identifier\n",
    "    :return 0 on success, -1 otherwise\n",
    "    :see ETSI GS MEC 011 V3.2.1 (2024-04) Clause 8.2.7.3.5 DELETE\n",
    "    \"\"\"\n",
Yann Garcia's avatar
Yann Garcia committed
    "    global logger\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "    logger.debug('>>> delete_mec_subscription: resource_url: ' + resource_url)\n",
    "    try:\n",
    "        res = urllib3.util.parse_url(resource_url)\n",
    "        if res is None:\n",
    "            logger.error('delete_mec_subscription: Failed to paerse URL')\n",
    "            return -1\n",
    "        header_params = {}\n",
    "        # HTTP header `Accept`\n",
    "        header_params['Accept'] = 'application/json'  # noqa: E501\n",
    "        # HTTP header `Content-Type`\n",
    "        header_params['Content-Type'] = 'application/json'  # noqa: E501\n",
    "        service_api.call_api(res.path, 'DELETE', header_params=header_params, async_req=False)\n",
    "        return 0\n",
    "    except ApiException as e:\n",
    "        logger.error('Exception when calling call_api: %s\\n' % e)\n",
    "    return -1\n",
    "    # End of function delete_mec_service"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-11-12 13:35:50,697 - __main__ - DEBUG - Starting at 20241112-133550\n",
      "2024-11-12 13:35:50,698 - __main__ - DEBUG - \t pwd= /home/jovyan/work/mecapp\n",
      "2024-11-12 13:35:50,699 - __main__ - DEBUG - >>> process_login\n",
      "2024-11-12 13:35:50,701 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
      "2024-11-12 13:35:50,791 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/login?provider=Jupyter2024 HTTP/11\" 201 0\n",
      "2024-11-12 13:35:50,792 DEBUG response body: b'{\"user_code\":\"sbxbp3kdqu\",\"verification_uri\":\"\"}'\n",
      "2024-11-12 13:35:50,793 - __main__ - DEBUG - process_login (step1): oauth: {'user_code': 'sbxbp3kdqu', 'verification_uri': ''}\n",
      "2024-11-12 13:35:50,794 - __main__ - DEBUG - =======================> DO AUTHORIZATION WITH CODE : sbxbp3kdqu\n",
      "2024-11-12 13:35:50,796 - __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:35:50 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:35:53,809 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/namespace?user_code=sbxbp3kdqu HTTP/11\" 200 0\n",
      "2024-11-12 13:35:53,810 DEBUG response body: b'{\"sandbox_name\":\"sbxbp3kdqu\"}'\n",
      "2024-11-12 13:35:53,811 - __main__ - DEBUG - process_login (step2): result: {'sandbox_name': 'sbxbp3kdqu'}\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'GET /sandbox-api/v1/namespace?user_code=sbxbp3kdqu 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:35:53 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:36:03,816 - __main__ - DEBUG - >>> get_network_scenarios: sandbox=sbxbp3kdqu\n",
      "2024-11-12 13:36:03,819 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
      "2024-11-12 13:36:03,974 DEBUG https://mec-platform2.etsi.org:443 \"GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbxbp3kdqu HTTP/11\" 200 0\n",
      "2024-11-12 13:36:03,975 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:36:03,975 - __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"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'GET /sandbox-api/v1/sandboxNetworkScenarios?sandbox_name=sbxbp3kdqu 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:36:03 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:36:13,976 - __main__ - DEBUG - >>> activate_network_scenario: sbxbp3kdqu\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'POST /sandbox-api/v1/sandboxNetworkScenarios/sbxbp3kdqu?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:36:15,256 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/sandboxNetworkScenarios/sbxbp3kdqu?network_scenario_id=4g-5g-macro-v2x HTTP/11\" 204 0\n",
      "2024-11-12 13:36:15,258 DEBUG response body: b''\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "reply: 'HTTP/1.1 204 No Content\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:36:15 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:36:35,262 - __main__ - DEBUG - >>> request_application_instance_id: sbxbp3kdqu\n",
      "2024-11-12 13:36:35,265 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
      "2024-11-12 13:36:35,396 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/sandboxAppInstances/sbxbp3kdqu HTTP/11\" 201 0\n",
      "2024-11-12 13:36:35,397 DEBUG response body: b'{\"id\":\"068fbd70-b8bf-4af3-85bf-b560e5bfaddc\",\"name\":\"JupyterMecApp\",\"nodeName\":\"mep1\",\"type\":\"USER\"}'\n",
      "2024-11-12 13:36:35,397 - __main__ - DEBUG - request_application_instance_id: result: {'id': '068fbd70-b8bf-4af3-85bf-b560e5bfaddc',\n",
      " 'name': 'JupyterMecApp',\n",
      " 'node_name': 'mep1',\n",
      " 'persist': None,\n",
      " 'type': 'USER'}\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'POST /sandbox-api/v1/sandboxAppInstances/sbxbp3kdqu HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 107\\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'{\"id\": \"068fbd70-b8bf-4af3-85bf-b560e5bfaddc\", \"name\": \"JupyterMecApp\", \"nodeName\": \"mep1\", \"type\": \"USER\"}'\n",
      "reply: 'HTTP/1.1 201 Created\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:36:35 GMT\n",
      "header: Content-Type: application/json; charset=UTF-8\n",
      "header: Content-Length: 100\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:36:45,398 - __main__ - DEBUG - >>> send_ready_confirmation: 068fbd70-b8bf-4af3-85bf-b560e5bfaddc\n",
      "2024-11-12 13:36:45,400 - __main__ - DEBUG - send_ready_confirmation: url: /{sandbox_name}/{mec_pltf}/mec_app_support/v2/applications/{app_inst_id}/confirm_ready\n",
      "2024-11-12 13:36:45,403 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
      "2024-11-12 13:36:45,464 DEBUG https://mec-platform2.etsi.org:443 \"POST /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/confirm_ready HTTP/11\" 204 0\n",
      "2024-11-12 13:36:45,466 DEBUG response body: b''\n",
      "2024-11-12 13:36:45,467 - __main__ - DEBUG - >>> send_subscribe_termination: 068fbd70-b8bf-4af3-85bf-b560e5bfaddc\n",
      "2024-11-12 13:36:45,468 - __main__ - DEBUG - send_subscribe_termination: url: /{sandbox_name}/{mec_pltf}/mec_app_support/v2/applications/{app_inst_id}/subscriptions\n",
      "2024-11-12 13:36:45,483 DEBUG https://mec-platform2.etsi.org:443 \"POST /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions HTTP/11\" 201 0\n",
      "2024-11-12 13:36:45,486 DEBUG response body: b'{\"subscriptionType\":\"AppTerminationNotificationSubscription\",\"callbackReference\":\"http://mec-platform2.etsi.org:31111/sandbox/v1/mec011/v2/termination\",\"_links\":{\"self\":{\"href\":\"https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions/sub-q-kCgregXGwXCyGz\"}},\"appInstanceId\":\"068fbd70-b8bf-4af3-85bf-b560e5bfaddc\"}'\n",
      "2024-11-12 13:36:45,487 - __main__ - DEBUG - >>> extract_sub_id: resource_url: https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions/sub-q-kCgregXGwXCyGz\n",
      "2024-11-12 13:36:45,489 - __main__ - DEBUG - >>> create_mec_service\n",
      "2024-11-12 13:36:45,490 - __main__ - DEBUG - create_mec_service: url: /{sandbox_name}/{mec_pltf}/mec_service_mgmt/v1/applications/{app_inst_id}/services\n",
      "2024-11-12 13:36:45,530 DEBUG https://mec-platform2.etsi.org:443 \"POST /sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services HTTP/11\" 201 0\n",
      "2024-11-12 13:36:45,531 DEBUG response body: b'{\"serInstanceId\":\"25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\",\"serName\":\"demo6 MEC Service\",\"serCategory\":{\"href\":\"http://mec-platform2.etsi.org:31111/sandbox/v1/statistic/v1/quantity\",\"id\":\"9795944c-2777-4ef9-b9fd-f85e3ffccbae\",\"name\":\"Demo\",\"version\":\"1.0.0\"},\"version\":\"1.0.0\",\"state\":\"ACTIVE\",\"transportInfo\":{\"id\":\"960f105a-69d1-43f9-baa8-ba593cd4f01b\",\"name\":\"HTTP REST API\",\"type\":\"REST_HTTP\",\"protocol\":\"HTTP\",\"version\":\"2.0\",\"endpoint\":{\"uris\":null,\"fqdn\":null,\"addresses\":null,\"alternative\":null},\"security\":{}},\"serializer\":\"JSON\",\"scopeOfLocality\":\"MEC_HOST\",\"consumedLocalOnly\":true,\"isLocal\":true,\"_links\":{\"self\":{\"href\":\"https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\"}}}'\n",
      "2024-11-12 13:36:45,531 - __main__ - INFO - mec_service_resource: https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\n",
      "2024-11-12 13:36:45,532 - __main__ - INFO - body: b'{\"serInstanceId\":\"25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\",\"serName\":\"demo6 MEC Service\",\"serCategory\":{\"href\":\"http://mec-platform2.etsi.org:31111/sandbox/v1/statistic/v1/quantity\",\"id\":\"9795944c-2777-4ef9-b9fd-f85e3ffccbae\",\"name\":\"Demo\",\"version\":\"1.0.0\"},\"version\":\"1.0.0\",\"state\":\"ACTIVE\",\"transportInfo\":{\"id\":\"960f105a-69d1-43f9-baa8-ba593cd4f01b\",\"name\":\"HTTP REST API\",\"type\":\"REST_HTTP\",\"protocol\":\"HTTP\",\"version\":\"2.0\",\"endpoint\":{\"uris\":null,\"fqdn\":null,\"addresses\":null,\"alternative\":null},\"security\":{}},\"serializer\":\"JSON\",\"scopeOfLocality\":\"MEC_HOST\",\"consumedLocalOnly\":true,\"isLocal\":true,\"_links\":{\"self\":{\"href\":\"https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\"}}}'\n",
      "2024-11-12 13:36:45,533 - __main__ - INFO - data: {'serInstanceId': '25e0201a-0f52-4e1f-9290-e4dc9f1eee4a', 'serName': 'demo6 MEC Service', 'serCategory': {'href': 'http://mec-platform2.etsi.org:31111/sandbox/v1/statistic/v1/quantity', 'id': '9795944c-2777-4ef9-b9fd-f85e3ffccbae', 'name': 'Demo', 'version': '1.0.0'}, 'version': '1.0.0', 'state': 'ACTIVE', 'transportInfo': {'id': '960f105a-69d1-43f9-baa8-ba593cd4f01b', 'name': 'HTTP REST API', 'type': 'REST_HTTP', 'protocol': 'HTTP', 'version': '2.0', 'endpoint': {'uris': None, 'fqdn': None, 'addresses': None, 'alternative': None}, 'security': {}}, 'serializer': 'JSON', 'scopeOfLocality': 'MEC_HOST', 'consumedLocalOnly': True, 'isLocal': True, '_links': {'self': {'href': 'https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a'}}}\n",
      "2024-11-12 13:36:45,533 - __main__ - INFO - =============> Execute the command: curl http://mec-platform2.etsi.org:31111/sandbox/v1/statistic/v1/quantity\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'POST /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/confirm_ready HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 23\\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'{\"indication\": \"READY\"}'\n",
      "reply: 'HTTP/1.1 204 No Content\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:36:45 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",
      "send: b'POST /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 212\\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'{\"subscriptionType\": \"AppTerminationNotificationSubscription\", \"callbackReference\": \"http://mec-platform2.etsi.org:31111/sandbox/v1/mec011/v2/termination\", \"appInstanceId\": \"068fbd70-b8bf-4af3-85bf-b560e5bfaddc\"}'\n",
      "reply: 'HTTP/1.1 201 Created\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:36:45 GMT\n",
      "header: Content-Type: application/json; charset=UTF-8\n",
      "header: Content-Length: 387\n",
      "header: Connection: keep-alive\n",
      "header: Location: https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions/sub-q-kCgregXGwXCyGz\n",
      "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n",
      "send: b'POST /sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services HTTP/1.1\\r\\nHost: mec-platform2.etsi.org\\r\\nAccept-Encoding: identity\\r\\nContent-Length: 453\\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'{\"serName\": \"demo6 MEC Service\", \"serCategory\": {\"href\": \"http://mec-platform2.etsi.org:31111/sandbox/v1/statistic/v1/quantity\", \"id\": \"9795944c-2777-4ef9-b9fd-f85e3ffccbae\", \"name\": \"Demo\", \"version\": \"1.0.0\"}, \"version\": \"1.0.0\", \"state\": \"ACTIVE\", \"transportInfo\": {\"id\": \"960f105a-69d1-43f9-baa8-ba593cd4f01b\", \"name\": \"HTTP REST API\", \"type\": \"REST_HTTP\", \"protocol\": \"HTTP\", \"version\": \"2.0\", \"endpoint\": {}, \"security\": {}}, \"serializer\": \"JSON\"}'\n",
      "reply: 'HTTP/1.1 201 Created\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:36:45 GMT\n",
      "header: Content-Type: application/json; charset=UTF-8\n",
      "header: Content-Length: 797\n",
      "header: Connection: keep-alive\n",
      "header: Location: https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\n",
      "header: Strict-Transport-Security: max-age=15724800; includeSubDomains\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-11-12 13:37:15,534 - __main__ - DEBUG - >>> delete_mec_subscription: resource_url: https://mec-platform2.etsi.org/sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a\n",
      "2024-11-12 13:37:15,545 DEBUG https://mec-platform2.etsi.org:443 \"DELETE /sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a HTTP/11\" 204 0\n",
      "2024-11-12 13:37:15,552 DEBUG response body: b''\n",
      "2024-11-12 13:37:15,553 - __main__ - DEBUG - >>> delete_subscribe_termination: 068fbd70-b8bf-4af3-85bf-b560e5bfaddc\n",
      "2024-11-12 13:37:15,555 - __main__ - DEBUG - delete_subscribe_termination: url: /{sandbox_name}/{mec_pltf}/mec_app_support/v2/applications/{app_inst_id}/subscriptions/{sub_id}\n",
      "2024-11-12 13:37:15,564 DEBUG https://mec-platform2.etsi.org:443 \"DELETE /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions/sub-q-kCgregXGwXCyGz HTTP/11\" 204 0\n",
      "2024-11-12 13:37:15,565 DEBUG response body: b''\n",
      "2024-11-12 13:37:15,566 - __main__ - DEBUG - >>> delete_application_instance_id: sbxbp3kdqu\n",
      "2024-11-12 13:37:15,567 - __main__ - DEBUG - >>> delete_application_instance_id: 068fbd70-b8bf-4af3-85bf-b560e5bfaddc\n",
      "2024-11-12 13:37:15,611 DEBUG https://mec-platform2.etsi.org:443 \"DELETE /sandbox-api/v1/sandboxAppInstances/sbxbp3kdqu/068fbd70-b8bf-4af3-85bf-b560e5bfaddc HTTP/11\" 204 0\n",
      "2024-11-12 13:37:15,612 DEBUG response body: b''\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'DELETE /sbxbp3kdqu/mep1/mec_service_mgmt/v1/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/services/25e0201a-0f52-4e1f-9290-e4dc9f1eee4a 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 204 No Content\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:37:15 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",
      "send: b'DELETE /sbxbp3kdqu/mep1/mec_app_support/v2/applications/068fbd70-b8bf-4af3-85bf-b560e5bfaddc/subscriptions/sub-q-kCgregXGwXCyGz 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 204 No Content\\r\\n'\n",
      "header: Date: Tue, 12 Nov 2024 13:37:15 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",
      "send: b'DELETE /sandbox-api/v1/sandboxAppInstances/sbxbp3kdqu/068fbd70-b8bf-4af3-85bf-b560e5bfaddc 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:37:15 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:37:25,614 - __main__ - DEBUG - >>> deactivate_network_scenario: sbxbp3kdqu\n",
      "2024-11-12 13:37:25,649 DEBUG https://mec-platform2.etsi.org:443 \"DELETE /sandbox-api/v1/sandboxNetworkScenarios/sbxbp3kdqu/4g-5g-macro-v2x HTTP/11\" 204 0\n",
      "2024-11-12 13:37:25,651 DEBUG response body: b''\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'DELETE /sandbox-api/v1/sandboxNetworkScenarios/sbxbp3kdqu/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:37:25 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:37:45,657 - __main__ - DEBUG - >>> process_logout: sandbox=sbxbp3kdqu\n",
      "2024-11-12 13:37:45,658 DEBUG Resetting dropped connection: mec-platform2.etsi.org\n",
      "2024-11-12 13:37:45,756 DEBUG https://mec-platform2.etsi.org:443 \"POST /sandbox-api/v1/logout?sandbox_name=sbxbp3kdqu HTTP/11\" 204 0\n",
      "2024-11-12 13:37:45,758 DEBUG response body: b''\n",
      "2024-11-12 13:37:45,759 - __main__ - DEBUG - Stopped at 20241112-133745\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "send: b'POST /sandbox-api/v1/logout?sandbox_name=sbxbp3kdqu 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:37:45 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 fith sprint of our skeleton of our MEC application:\n",
    "        - Mec application setup\n",
Yann Garcia's avatar
Yann Garcia committed
    "        - Create new MEC service\n",
    "        - Send a request to our MEC service\n",
    "        - Delete newly created MEC service\n",
    "        - Mec application termination\n",
    "    \"\"\" \n",
Yann Garcia's avatar
Yann Garcia committed
    "    global LISTENER_IP, LISTENER_PORT, CALLBACK_URI, logger\n",
    "\n",
    "    logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
    "    logger.debug('\\t pwd= ' + os.getcwd())\n",
    "\n",
    "    # Setup the MEC application\n",
Yann Garcia's avatar
Yann Garcia committed
    "    sandbox_name, app_inst_id, sub_id = mec_app_setup()\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # Create the MEC service\n",
    "    result, status, mec_service_resource = create_mec_service(sandbox_name, app_inst_id)\n",
    "    if status != 201:\n",
    "        logger.error('Failed to create MEC service')\n",
    "    else:\n",
Yann Garcia's avatar
Yann Garcia committed
    "        logger.info('mec_service_resource: %s', mec_service_resource)\n",
    "\n",
    "    # Any processing here\n",
    "    logger.info('body: ' + str(result.data))\n",
    "    data = json.loads(result.data)\n",
    "    logger.info('data: %s', str(data))\n",
    "    logger.info('=============> Execute the command: curl %s/statistic/v1/quantity', CALLBACK_URI)\n",
    "    time.sleep(30)\n",
    "\n",
    "    # Delete the MEC servce\n",
    "    delete_mec_service(mec_service_resource)\n",
    "\n",
    "    # Terminate the MEC application\n",
    "    mec_app_termination(sandbox_name, app_inst_id, sub_id)\n",
    "\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": [
    "# Support of CAPIF (3GPP TS 29.222: 3rd Generation Partnership Project; Technical Specification Group Core Network and Terminals; Common API Framework for 3GPP Northbound APIs)\n",
    "\n",
    "MEC-CAPIF support is described in ETSI GS MEC 011 (V3.2.1) Clause 9 [4]\n",
    "\n",
    "The sample code below illustrates the usage of MEC-CAPI endpoints:\n",
    "- /service-apis/v1/allServiceAPIs\n",
    "- /published-apis/v1/{apfId}/service-apis\n",
    "\n",
    "<b>To be continued<b>\n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "    <b>Note:</b> The functions used in the sample code below are defined after this cell.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_main():\n",
    "    \"\"\"\n",
    "    This code illustrates the usage of MEC-CAPI endpoints:\n",
    "        - /service-apis/v1/allServiceAPIs\n",
    "        - /published-apis/v1/{apfId}/service-apis\n",
    "    \"\"\" \n",
    "    global LISTENER_IP, LISTENER_PORT, CALLBACK_URI, logger\n",
    "\n",
    "    logger.debug('Starting at ' + time.strftime('%Y%m%d-%H%M%S'))\n",
    "    logger.debug('\\t pwd= ' + os.getcwd())\n",
    "\n",
    "    # Start notification server in a daemonized thread\n",
    "    httpd = start_notification_server()\n",
    "\n",
    "    # Setup the MEC application\n",
    "    sandbox_name, app_inst_id, sub_id = mec_app_setup()\n",
    "\n",
    "    # Get the list of the MEC sevices using CAPIF endpoints\n",
    "    result, status, mec_services = capif_get_all_mec_services(sandbox_name)\n",
    "    if status != 200:\n",
    "        logger.error('Failed to get the list of MEC services using CAPIF endpoint')\n",
    "    else:\n",
    "        logger.info('capif_get_all_mec_services: %s', mec_services)\n",
    "\n",
    "    # Get the list of the MEC sevices for our AppInstanceId using CAPIF endpoints\n",
    "    result, status, mec_services = capif_get_mec_services(sandbox_name, app_inst_id)\n",
    "    if status != 200:\n",
    "        logger.error('Failed to get the list of MEC services for our AppInstanceId using CAPIF endpoint')\n",
    "    else:\n",
    "        logger.info('capif_get_all_mec_services: %s', mec_services)\n",
    "\n",
    "    # In the previous request, the list of services for our AppInstanceId is empty.\n",
    "    # Let's create a new MEC service\n",
    "    result, status, mec_service_resource = create_mec_service(sandbox_name, app_inst_id)\n",
    "    if status != 201:\n",
    "        logger.error('Failed to create MEC service')\n",
    "    else:\n",
    "        logger.info('mec_service_resource: %s', mec_service_resource)\n",
    "\n",
    "    # Now, we can send the CAPIF endpoint\n",
    "    # To be continued\n",
    "\n",
    "    # Stop notification server\n",
    "    stop_notification_server(httpd)\n",
    "\n",
Yann Garcia's avatar
Yann Garcia committed
    "    # Delete the MEC servce\n",
    "    delete_mec_service(mec_service_resource)\n",
    "\n",
    "    # Terminate the MEC application\n",
    "    mec_app_termination(sandbox_name, app_inst_id, sub_id)\n",
    "\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"
   ]
  },
Yann Garcia's avatar
Yann Garcia committed
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Annexes\n",
    "\n",
    "## Annex A: How to use an existing MEC sandbox instance\n",
    "\n",
    "This case is used when the MEC Sandbox API is not used. The procedure is the following:\n",
    "- Log to the MEC Sandbox using a WEB browser\n",
    "- Select a network scenario\n",
    "- Create a new application instance\n",
    "\n",
    "When it is done, the newly created application instance is used by your application when required. This application instance is usually passed to your application in the command line or using a configuration file\n"
Yann Garcia's avatar
Yann Garcia committed
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bibliography\n",
Yann Garcia's avatar
Yann Garcia committed
    "\n",
    "1. ETSI GS MEC 002 (V2.2.1) (01-2022): \"Multi-access Edge Computing (MEC); Phase 2: Use Cases and Requirements\".\n",
    "2. ETSI GS MEC 010-1 (V1.1.1) (10-2017): \"Mobile Edge Computing (MEC); Mobile Edge Management; Part 1: System, host and platform management\".\n",
    "3. ETSI GS MEC 010-2 (V2.2.1) (02-2022): \"Multi-access Edge Computing (MEC); MEC Management; Part 2: Application lifecycle, rules and requirements management\".\n",
    "4. ETSI GS MEC 011 (V3.2.1) (09-2022): \"Multi-access Edge Computing (MEC); Edge Platform Application Enablement\".\n",
Yann Garcia's avatar
Yann Garcia committed
    "5. ETSI GS MEC 012 (V2.2.1) (02-2022): \"Multi-access Edge Computing (MEC); Radio Network Information API\".\n",
    "6. ETSI GS MEC 013 (V2.2.1) (01-2022): \"Multi-access Edge Computing (MEC); Location API\".\n",
    "7. ETSI GS MEC 014 (V2.1.1) (03-2021): \"Multi-access Edge Computing (MEC); UE Identity API\".\n",
    "8. ETSI GS MEC 015 (V2.1.1) (06-2020): \"Multi-Access Edge Computing (MEC); Traffic Management APIs\".\n",
    "9. ETSI GS MEC 016 (V2.2.1) (04-2020): \"Multi-access Edge Computing (MEC); Device application interface\".\n",
    "10. ETSI GS MEC 021 (V2.2.1) (02-2022): \"Multi-access Edge Computing (MEC); Application Mobility Service API\".\n",
    "11. ETSI GS MEC 028 (V2.3.1) (07-2022): \"Multi-access Edge Computing (MEC); WLAN Access Information API\".\n",
    "12. ETSI GS MEC 029 (V2.2.1) (01-2022): \"Multi-access Edge Computing (MEC); Fixed Access Information API\".\n",
    "13. ETSI GS MEC 030 (V3.2.1) (05-2022): \"Multi-access Edge Computing (MEC); V2X Information Service API\".\n",
Yann Garcia's avatar
Yann Garcia committed
    "14. ETSI GR MEC-DEC 025 (V2.1.1) (06-2019): \"Multi-access Edge Computing (MEC); MEC Testing Framework\".\n",
    "15. ETSI GR MEC 001 (V3.1.1) (01-2022): \"Multi-access Edge Computing (MEC); Terminology\".\n",
    "16. ETSI GR MEC 003 (V3.1.1): Multi-access Edge Computing (MEC);\n",
    "17. 3GPP TS 29.222: 3rd Generation Partnership Project; Technical Specification Group Core Network and Terminals; Common API Framework for 3GPP Northbound APIs\n",
    "Framework and Reference Architecture\n",
    "18. [The Wiki MEC web site](https://www.etsi.org/technologies/multi-access-edge-computing)\n"
Yann Garcia's avatar
Yann Garcia committed
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
Yann Garcia's avatar
Yann Garcia committed
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}