Loading src/device/service/drivers/xr/README_XR.md +54 −10 Original line number Diff line number Diff line Loading @@ -25,6 +25,19 @@ cd ~/.kube microk8s config > config ``` Helm 3 is mandatory as of February 2023. Enable it with microk8s command. Then create wrapper shell script to expose it with standard name: ``` sudo su - cat > /usr/bin/helm3 #!/bin/sh microk8s.helm3 "$@" ^D chmod 755 /usr/bin/helm3 ``` Using symbolic link does not work, because snap wraps the real binary and won't work if name is different. Local Docker registry is needed for build results. Use the following command to start local registry (docker will pull necessary images from Internet) ```bash Loading @@ -32,23 +45,33 @@ docker run -d -p 32000:5000 --restart=always --name registry registry:2 ``` Setup mydeploy script outside the git repo. E.g. following will do. SOURCE IT ON ALL SHELLS. IMPORTANT: September 2022 version of controller has a bug where any update to device trigger update to device until GRPC endpoints are so loaded that K8s kills device service. XR does not need automation service, so it can be left out. Use https://labs.etsi.org/rep/tfs/controller/-/blob/develop/my_deploy.sh as example. Script requires more variables than before as of February 2023. ```bash # See https://labs.etsi.org/rep/tfs/controller/-/blob/develop/my_deploy.sh # Use docker run -d -p 32000:5000 --restart=always --name registry registry:2 export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # Without automation service (see note above) export TFS_COMPONENTS="context device pathcomp service slice compute monitoring webui" # Correct setting # export TFS_COMPONENTS="context device automation pathcomp service slice compute monitoring webui" # Pre-rebase #export TFS_COMPONENTS="context device automation service compute monitoring webui" export TFS_COMPONENTS="context device automation monitoring pathcomp service slice compute webui load_generator" export TFS_IMAGE_TAG="dev" export TFS_K8S_NAMESPACE="tfs" export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" export TFS_GRAFANA_PASSWORD="admin123+" #export TFS_SKIP_BUILD="" export CRDB_NAMESPACE="crdb" export CRDB_USERNAME="tfs" export CRDB_PASSWORD="tfs123" export CRDB_DATABASE="tfs" export CRDB_DEPLOY_MODE="single" export CRDB_DROP_DATABASE_IF_EXISTS="" export CRDB_REDEPLOY="" export NATS_NAMESPACE="nats" export NATS_REDEPLOY="" export QDB_NAMESPACE="qdb" export QDB_USERNAME="admin" export QDB_PASSWORD="quest" export QDB_TABLE="tfs_monitoring" export QDB_REDEPLOY="" ``` Build is containerized, pytest used for setup is not. Teraflow has some third party venv suggestion in docs. However standard venv works. Create: Loading Loading @@ -114,11 +137,32 @@ Setup service by following commands in src directory. Kubernetes endpoins change python -m pytest --verbose tests/ofc22/tests/test_functional_create_service_xr.py ``` For topology different than used by the test_functional_create/delete_service_xr.py, one can also use service-cli.py tool in the xr module directory. It allows creation of ELINE services between arbitrary endpoints in the topology (with consequent underlying XR service instantiation). Run in *xr module directory*. Representative examples: ``` PYTHONPATH=../../../../ ./service-cli.py create 1 R1-EMU 13/1/2 500 2 R3-EMU 13/1/2 500 PYTHONPATH=../../../../ ./service-cli.py list PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f ``` The PYTHONPATH is mandatory. Suitable topology JSON must have been loaded before. With the CocroachDB persistence, it is sufficient to load the topology once and it will persist. Good logs to check are: * kubectl logs service/deviceservice --namespace tfs * kubectl logs service/webuiservice --namespace tfs New 2.0 version of Teraflow has persistent database. To clean up any failed state (e.g. from debugging session), set before deploy: ``` export CRDB_DROP_DATABASE_IF_EXISTS=YES ``` In normal test runs it is not necessary to clear the database. However DO NOT RE-UPLOAD THE TOPOLOGY JSON FILE if DB has not been cleared. ## Unit Tests Run in src directory (src under repo top level) with command: Loading src/device/service/drivers/xr/XrDriver.py +4 −2 Original line number Diff line number Diff line Loading @@ -106,8 +106,10 @@ class XrDriver(_Driver): def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: LOGGER.info(f"SetConfig[{self}]: {resources=}") # Logged config seems like: # Pre-February 2023 #[('/service[52ff5f0f-fda4-40bd-a0b1-066f4ff04079:optical]', '{"capacity_unit": "GHz", "capacity_value": 1, "direction": "UNIDIRECTIONAL", "input_sip": "XR HUB 1|XR-T4", "layer_protocol_name": "PHOTONIC_MEDIA", "layer_protocol_qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC", "output_sip": "XR LEAF 1|XR-T1", "uuid": "52ff5f0f-fda4-40bd-a0b1-066f4ff04079:optical"}')] # Post February 2023 #[('/services/service[e1b9184c-767d-44b9-bf83-a1f643d82bef]', '{"capacity_unit": "GHz", "capacity_value": 50.0, "direction": "UNIDIRECTIONAL", "input_sip": "XR LEAF 1|XR-T1", "layer_protocol_name": "PHOTONIC_MEDIA", "layer_protocol_qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC", "output_sip": "XR HUB 1|XR-T4", "uuid": "e1b9184c-767d-44b9-bf83-a1f643d82bef"}')] with self.__lock: if self.__constellation is None: self.__constellation = self.__cm_connection.get_constellation_by_hub_name(self.__hub_module_name) Loading Loading @@ -157,7 +159,7 @@ class XrDriver(_Driver): else: LOGGER.info(f"DeleteConfig: Connection {service_uuid} delete failure (was {str(connection)})") if self.__constellation.is_vti_mode(): if connection.is_vti_mode(): active_tc = self.__cm_connection.get_transport_capacity_by_teraflow_uuid(service_uuid) if active_tc is not None: if self.__cm_connection.delete_transport_capacity(active_tc.href): Loading src/device/service/drivers/xr/cm-cli.py 100644 → 100755 +0 −0 File mode changed from 100644 to 100755. View file src/device/service/drivers/xr/cm/cm_connection.py +17 −17 Original line number Diff line number Diff line Loading @@ -241,7 +241,7 @@ class CmConnection: return self.__acquire_access_token() def list_constellations(self) -> List[Constellation]: r = self.__get("/api/v1/ns/xr-networks?content=expanded") r = self.__get("/api/v1/xr-networks?content=expanded") if not r.is_valid_json_list_with_status(200): return [] return [Constellation(c) for c in r.json] Loading @@ -252,13 +252,13 @@ class CmConnection: ('content', 'expanded'), ('q', '{"hubModule.state.module.moduleName": "' + hub_module_name + '"}') ] r = self.__get("/api/v1/ns/xr-networks?content=expanded", params=qparams) r = self.__get("/api/v1/xr-networks?content=expanded", params=qparams) if not r.is_valid_json_list_with_status(200, 1, 1): return None return Constellation(r.json[0]) def get_transport_capacities(self) -> List[TransportCapacity]: r= self.__get("/api/v1/ns/transport-capacities?content=expanded") r= self.__get("/api/v1/transport-capacities?content=expanded") if not r.is_valid_json_list_with_status(200): return [] return [TransportCapacity(from_json=t) for t in r.json] Loading @@ -268,7 +268,7 @@ class CmConnection: ('content', 'expanded'), ('q', '{"state.name": "' + tc_name + '"}') ] r = self.__get("/api/v1/ns/transport-capacities?content=expanded", params=qparams) r = self.__get("/api/v1/transport-capacities?content=expanded", params=qparams) if not r.is_valid_json_list_with_status(200, 1, 1): return TransportCapacity(from_json=r.json[0]) else: Loading @@ -280,17 +280,17 @@ class CmConnection: def create_transport_capacity(self, tc: TransportCapacity) -> Optional[str]: # Create wants a list, so wrap connection to list tc_config = [tc.create_config()] resp = self.__post("/api/v1/ns/transport-capacities", tc_config) resp = self.__post("/api/v1/transport-capacities", tc_config) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: tc.href = resp.json[0]["href"] LOGGER.info(f"Created transport-capcity {tc}") #LOGGER.info(self.__get(f"/api/v1/ns/transport-capacities{tc.href}?content=expanded")) #LOGGER.info(self.__get(f"/api/v1/transport-capacities{tc.href}?content=expanded")) return tc.href else: return None def delete_transport_capacity(self, href: str) -> bool: resp = self.__delete(f"/api/v1/ns/transport-capacities{href}") resp = self.__delete(f"/api/v1/transport-capacities{href}") # Returns empty body if resp.is_valid_with_status_ignore_body(202): Loading Loading @@ -399,7 +399,7 @@ class CmConnection: # Create wants a list, so wrap connection to list cfg = [connection.create_config()] resp = self.__post("/api/v1/ncs/network-connections", cfg) resp = self.__post("/api/v1/network-connections", cfg) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: connection.href = resp.json[0]["href"] LOGGER.info(f"IPM accepted create request for connection {connection}") Loading Loading @@ -433,7 +433,7 @@ class CmConnection: # Perform deletes for ep_href in ep_deletes: resp = self.__delete(f"/api/v1/ncs{ep_href}") resp = self.__delete(f"/api/v1{ep_href}") if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: EP-UPDATE: Deleted connection endpoint {ep_href}") else: Loading @@ -441,21 +441,21 @@ class CmConnection: # Update capacities for otherwise similar endpoints for ep_href, ep_cfg in ep_updates: resp = self.__put(f"/api/v1/ncs{ep_href}", ep_cfg) resp = self.__put(f"/api/v1{ep_href}", ep_cfg) if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: EP-UPDATE: Updated connection endpoint {ep_href} with {ep_cfg}") else: LOGGER.info(f"update_connection: EP-UPDATE: Failed to update connection endpoint {ep_href} with {ep_cfg}: {resp}") # Perform adds resp = self.__post(f"/api/v1/ncs{href}/endpoints", ep_creates) resp = self.__post(f"/api/v1{href}/endpoints", ep_creates) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: LOGGER.info(f"update_connection: EP-UPDATE: Created connection endpoints {resp.json[0]} with {ep_creates}") else: LOGGER.info(f"update_connection: EP-UPDATE: Failed to create connection endpoints {resp.json[0] if resp.json else None} with {ep_creates}: {resp}") # Connection update (excluding endpoints) resp = self.__put(f"/api/v1/ncs{href}", cfg) resp = self.__put(f"/api/v1{href}", cfg) # Returns empty body if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: Updated connection {connection}") Loading @@ -466,7 +466,7 @@ class CmConnection: return None def delete_connection(self, href: str) -> bool: resp = self.__delete(f"/api/v1/ncs{href}") resp = self.__delete(f"/api/v1{href}") #print(resp) # Returns empty body if resp.is_valid_with_status_ignore_body(202): Loading @@ -489,7 +489,7 @@ class CmConnection: ('content', 'expanded'), ('q', '{"state.name": "' + connection_name + '"}') ] r = self.__get("/api/v1/ncs/network-connections", params=qparams) r = self.__get("/api/v1/network-connections", params=qparams) if r.is_valid_json_list_with_status(200, 1, 1): return Connection(from_json=r.json[0]) else: Loading @@ -499,7 +499,7 @@ class CmConnection: qparams = [ ('content', 'expanded'), ] r = self.__get(f"/api/v1/ncs{href}", params=qparams) r = self.__get(f"/api/v1{href}", params=qparams) if r.is_valid_json_obj_with_status(200): return Connection(from_json=r.json) else: Loading @@ -509,14 +509,14 @@ class CmConnection: return self.get_connection_by_name(f"TF:{uuid}") def get_connections(self): r = self.__get("/api/v1/ncs/network-connections?content=expanded") r = self.__get("/api/v1/network-connections?content=expanded") if r.is_valid_json_list_with_status(200): return [Connection(from_json=c) for c in r.json] else: return [] def service_uuid(self, key: str) -> Optional[str]: service = re.match(r"^/service\[(.+)\]$", key) service = re.match(r"^(?:/services)/service\[(.+)\]$", key) if service: return service.group(1) else: Loading src/device/service/drivers/xr/cm/connection.py +3 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,9 @@ class Connection: endpoints = ", ".join((str(ep) for ep in self.endpoints)) return f"name: {name}, id: {self.href}, service-mode: {self.serviceMode}, end-points: [{endpoints}]" def is_vti_mode(self) -> bool: return "XR-VTI-P2P" == self.serviceMode def __guess_service_mode_from_emulated_enpoints(self): for ep in self.endpoints: if ep.vlan is not None: Loading Loading
src/device/service/drivers/xr/README_XR.md +54 −10 Original line number Diff line number Diff line Loading @@ -25,6 +25,19 @@ cd ~/.kube microk8s config > config ``` Helm 3 is mandatory as of February 2023. Enable it with microk8s command. Then create wrapper shell script to expose it with standard name: ``` sudo su - cat > /usr/bin/helm3 #!/bin/sh microk8s.helm3 "$@" ^D chmod 755 /usr/bin/helm3 ``` Using symbolic link does not work, because snap wraps the real binary and won't work if name is different. Local Docker registry is needed for build results. Use the following command to start local registry (docker will pull necessary images from Internet) ```bash Loading @@ -32,23 +45,33 @@ docker run -d -p 32000:5000 --restart=always --name registry registry:2 ``` Setup mydeploy script outside the git repo. E.g. following will do. SOURCE IT ON ALL SHELLS. IMPORTANT: September 2022 version of controller has a bug where any update to device trigger update to device until GRPC endpoints are so loaded that K8s kills device service. XR does not need automation service, so it can be left out. Use https://labs.etsi.org/rep/tfs/controller/-/blob/develop/my_deploy.sh as example. Script requires more variables than before as of February 2023. ```bash # See https://labs.etsi.org/rep/tfs/controller/-/blob/develop/my_deploy.sh # Use docker run -d -p 32000:5000 --restart=always --name registry registry:2 export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # Without automation service (see note above) export TFS_COMPONENTS="context device pathcomp service slice compute monitoring webui" # Correct setting # export TFS_COMPONENTS="context device automation pathcomp service slice compute monitoring webui" # Pre-rebase #export TFS_COMPONENTS="context device automation service compute monitoring webui" export TFS_COMPONENTS="context device automation monitoring pathcomp service slice compute webui load_generator" export TFS_IMAGE_TAG="dev" export TFS_K8S_NAMESPACE="tfs" export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" export TFS_GRAFANA_PASSWORD="admin123+" #export TFS_SKIP_BUILD="" export CRDB_NAMESPACE="crdb" export CRDB_USERNAME="tfs" export CRDB_PASSWORD="tfs123" export CRDB_DATABASE="tfs" export CRDB_DEPLOY_MODE="single" export CRDB_DROP_DATABASE_IF_EXISTS="" export CRDB_REDEPLOY="" export NATS_NAMESPACE="nats" export NATS_REDEPLOY="" export QDB_NAMESPACE="qdb" export QDB_USERNAME="admin" export QDB_PASSWORD="quest" export QDB_TABLE="tfs_monitoring" export QDB_REDEPLOY="" ``` Build is containerized, pytest used for setup is not. Teraflow has some third party venv suggestion in docs. However standard venv works. Create: Loading Loading @@ -114,11 +137,32 @@ Setup service by following commands in src directory. Kubernetes endpoins change python -m pytest --verbose tests/ofc22/tests/test_functional_create_service_xr.py ``` For topology different than used by the test_functional_create/delete_service_xr.py, one can also use service-cli.py tool in the xr module directory. It allows creation of ELINE services between arbitrary endpoints in the topology (with consequent underlying XR service instantiation). Run in *xr module directory*. Representative examples: ``` PYTHONPATH=../../../../ ./service-cli.py create 1 R1-EMU 13/1/2 500 2 R3-EMU 13/1/2 500 PYTHONPATH=../../../../ ./service-cli.py list PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f ``` The PYTHONPATH is mandatory. Suitable topology JSON must have been loaded before. With the CocroachDB persistence, it is sufficient to load the topology once and it will persist. Good logs to check are: * kubectl logs service/deviceservice --namespace tfs * kubectl logs service/webuiservice --namespace tfs New 2.0 version of Teraflow has persistent database. To clean up any failed state (e.g. from debugging session), set before deploy: ``` export CRDB_DROP_DATABASE_IF_EXISTS=YES ``` In normal test runs it is not necessary to clear the database. However DO NOT RE-UPLOAD THE TOPOLOGY JSON FILE if DB has not been cleared. ## Unit Tests Run in src directory (src under repo top level) with command: Loading
src/device/service/drivers/xr/XrDriver.py +4 −2 Original line number Diff line number Diff line Loading @@ -106,8 +106,10 @@ class XrDriver(_Driver): def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: LOGGER.info(f"SetConfig[{self}]: {resources=}") # Logged config seems like: # Pre-February 2023 #[('/service[52ff5f0f-fda4-40bd-a0b1-066f4ff04079:optical]', '{"capacity_unit": "GHz", "capacity_value": 1, "direction": "UNIDIRECTIONAL", "input_sip": "XR HUB 1|XR-T4", "layer_protocol_name": "PHOTONIC_MEDIA", "layer_protocol_qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC", "output_sip": "XR LEAF 1|XR-T1", "uuid": "52ff5f0f-fda4-40bd-a0b1-066f4ff04079:optical"}')] # Post February 2023 #[('/services/service[e1b9184c-767d-44b9-bf83-a1f643d82bef]', '{"capacity_unit": "GHz", "capacity_value": 50.0, "direction": "UNIDIRECTIONAL", "input_sip": "XR LEAF 1|XR-T1", "layer_protocol_name": "PHOTONIC_MEDIA", "layer_protocol_qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC", "output_sip": "XR HUB 1|XR-T4", "uuid": "e1b9184c-767d-44b9-bf83-a1f643d82bef"}')] with self.__lock: if self.__constellation is None: self.__constellation = self.__cm_connection.get_constellation_by_hub_name(self.__hub_module_name) Loading Loading @@ -157,7 +159,7 @@ class XrDriver(_Driver): else: LOGGER.info(f"DeleteConfig: Connection {service_uuid} delete failure (was {str(connection)})") if self.__constellation.is_vti_mode(): if connection.is_vti_mode(): active_tc = self.__cm_connection.get_transport_capacity_by_teraflow_uuid(service_uuid) if active_tc is not None: if self.__cm_connection.delete_transport_capacity(active_tc.href): Loading
src/device/service/drivers/xr/cm-cli.py 100644 → 100755 +0 −0 File mode changed from 100644 to 100755. View file
src/device/service/drivers/xr/cm/cm_connection.py +17 −17 Original line number Diff line number Diff line Loading @@ -241,7 +241,7 @@ class CmConnection: return self.__acquire_access_token() def list_constellations(self) -> List[Constellation]: r = self.__get("/api/v1/ns/xr-networks?content=expanded") r = self.__get("/api/v1/xr-networks?content=expanded") if not r.is_valid_json_list_with_status(200): return [] return [Constellation(c) for c in r.json] Loading @@ -252,13 +252,13 @@ class CmConnection: ('content', 'expanded'), ('q', '{"hubModule.state.module.moduleName": "' + hub_module_name + '"}') ] r = self.__get("/api/v1/ns/xr-networks?content=expanded", params=qparams) r = self.__get("/api/v1/xr-networks?content=expanded", params=qparams) if not r.is_valid_json_list_with_status(200, 1, 1): return None return Constellation(r.json[0]) def get_transport_capacities(self) -> List[TransportCapacity]: r= self.__get("/api/v1/ns/transport-capacities?content=expanded") r= self.__get("/api/v1/transport-capacities?content=expanded") if not r.is_valid_json_list_with_status(200): return [] return [TransportCapacity(from_json=t) for t in r.json] Loading @@ -268,7 +268,7 @@ class CmConnection: ('content', 'expanded'), ('q', '{"state.name": "' + tc_name + '"}') ] r = self.__get("/api/v1/ns/transport-capacities?content=expanded", params=qparams) r = self.__get("/api/v1/transport-capacities?content=expanded", params=qparams) if not r.is_valid_json_list_with_status(200, 1, 1): return TransportCapacity(from_json=r.json[0]) else: Loading @@ -280,17 +280,17 @@ class CmConnection: def create_transport_capacity(self, tc: TransportCapacity) -> Optional[str]: # Create wants a list, so wrap connection to list tc_config = [tc.create_config()] resp = self.__post("/api/v1/ns/transport-capacities", tc_config) resp = self.__post("/api/v1/transport-capacities", tc_config) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: tc.href = resp.json[0]["href"] LOGGER.info(f"Created transport-capcity {tc}") #LOGGER.info(self.__get(f"/api/v1/ns/transport-capacities{tc.href}?content=expanded")) #LOGGER.info(self.__get(f"/api/v1/transport-capacities{tc.href}?content=expanded")) return tc.href else: return None def delete_transport_capacity(self, href: str) -> bool: resp = self.__delete(f"/api/v1/ns/transport-capacities{href}") resp = self.__delete(f"/api/v1/transport-capacities{href}") # Returns empty body if resp.is_valid_with_status_ignore_body(202): Loading Loading @@ -399,7 +399,7 @@ class CmConnection: # Create wants a list, so wrap connection to list cfg = [connection.create_config()] resp = self.__post("/api/v1/ncs/network-connections", cfg) resp = self.__post("/api/v1/network-connections", cfg) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: connection.href = resp.json[0]["href"] LOGGER.info(f"IPM accepted create request for connection {connection}") Loading Loading @@ -433,7 +433,7 @@ class CmConnection: # Perform deletes for ep_href in ep_deletes: resp = self.__delete(f"/api/v1/ncs{ep_href}") resp = self.__delete(f"/api/v1{ep_href}") if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: EP-UPDATE: Deleted connection endpoint {ep_href}") else: Loading @@ -441,21 +441,21 @@ class CmConnection: # Update capacities for otherwise similar endpoints for ep_href, ep_cfg in ep_updates: resp = self.__put(f"/api/v1/ncs{ep_href}", ep_cfg) resp = self.__put(f"/api/v1{ep_href}", ep_cfg) if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: EP-UPDATE: Updated connection endpoint {ep_href} with {ep_cfg}") else: LOGGER.info(f"update_connection: EP-UPDATE: Failed to update connection endpoint {ep_href} with {ep_cfg}: {resp}") # Perform adds resp = self.__post(f"/api/v1/ncs{href}/endpoints", ep_creates) resp = self.__post(f"/api/v1{href}/endpoints", ep_creates) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: LOGGER.info(f"update_connection: EP-UPDATE: Created connection endpoints {resp.json[0]} with {ep_creates}") else: LOGGER.info(f"update_connection: EP-UPDATE: Failed to create connection endpoints {resp.json[0] if resp.json else None} with {ep_creates}: {resp}") # Connection update (excluding endpoints) resp = self.__put(f"/api/v1/ncs{href}", cfg) resp = self.__put(f"/api/v1{href}", cfg) # Returns empty body if resp.is_valid_with_status_ignore_body(202): LOGGER.info(f"update_connection: Updated connection {connection}") Loading @@ -466,7 +466,7 @@ class CmConnection: return None def delete_connection(self, href: str) -> bool: resp = self.__delete(f"/api/v1/ncs{href}") resp = self.__delete(f"/api/v1{href}") #print(resp) # Returns empty body if resp.is_valid_with_status_ignore_body(202): Loading @@ -489,7 +489,7 @@ class CmConnection: ('content', 'expanded'), ('q', '{"state.name": "' + connection_name + '"}') ] r = self.__get("/api/v1/ncs/network-connections", params=qparams) r = self.__get("/api/v1/network-connections", params=qparams) if r.is_valid_json_list_with_status(200, 1, 1): return Connection(from_json=r.json[0]) else: Loading @@ -499,7 +499,7 @@ class CmConnection: qparams = [ ('content', 'expanded'), ] r = self.__get(f"/api/v1/ncs{href}", params=qparams) r = self.__get(f"/api/v1{href}", params=qparams) if r.is_valid_json_obj_with_status(200): return Connection(from_json=r.json) else: Loading @@ -509,14 +509,14 @@ class CmConnection: return self.get_connection_by_name(f"TF:{uuid}") def get_connections(self): r = self.__get("/api/v1/ncs/network-connections?content=expanded") r = self.__get("/api/v1/network-connections?content=expanded") if r.is_valid_json_list_with_status(200): return [Connection(from_json=c) for c in r.json] else: return [] def service_uuid(self, key: str) -> Optional[str]: service = re.match(r"^/service\[(.+)\]$", key) service = re.match(r"^(?:/services)/service\[(.+)\]$", key) if service: return service.group(1) else: Loading
src/device/service/drivers/xr/cm/connection.py +3 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,9 @@ class Connection: endpoints = ", ".join((str(ep) for ep in self.endpoints)) return f"name: {name}, id: {self.href}, service-mode: {self.serviceMode}, end-points: [{endpoints}]" def is_vti_mode(self) -> bool: return "XR-VTI-P2P" == self.serviceMode def __guess_service_mode_from_emulated_enpoints(self): for ep in self.endpoints: if ep.vlan is not None: Loading