Loading src/device/service/drivers/xr/README_XR.md +12 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,18 @@ arbitrary endpoints in the topology (with consequent underlying XR service insta PYTHONPATH=../../../../ ./service-cli.py list PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f ``` It is also possible to create direct XR services without multi-layer services. E.g.: ``` PYTHONPATH=../../../../ ./service-cli.py create-xr FooService X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 2|XR-T1" ``` Additionally it is possible to list services and endpoints: ``` PYTHONPATH=../../../../ ./service-cli.py list-endpoints 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. Loading src/device/service/drivers/xr/service-cli.py +71 −30 Original line number Diff line number Diff line Loading @@ -19,31 +19,33 @@ # # Run in this directory with PYTHONPATH=../../../../ # E.g.: # Multi-layer: # Create multi-layer service (L2 VPN over XR): # PYTHONPATH=../../../../ ./service-cli.py create 1 R1-EMU 13/1/2 500 2 R3-EMU 13/1/2 500 # Single-layer: # PYTHONPATH=../../../../ ./service-cli.py create-xr mydirectservice7 X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 1|XR-T1" # # Single-layer (XR without services on top of it): # PYTHONPATH=../../../../ ./service-cli.py create-xr FooService X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 2|XR-T1" # List services: # PYTHONPATH=../../../../ ./service-cli.py list # List possible endpoints: # PYTHONPATH=../../../../ ./service-cli.py list-endpoints # Delete service (if multi-layer, always deleter highest layer!) # PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f import argparse import logging #import traceback from copy import deepcopy from dataclasses import dataclass, field from typing import Dict from contextlib import contextmanager from common.Settings import get_setting from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient from tests.tools.mock_osm.MockOSM import MockOSM from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum, Service, Context, Empty from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum, Service, Empty, ServiceId from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Context import json_context, json_context_id from common.tools.object_factory.Topology import json_topology_id from common.tools.object_factory.ConfigRule import json_config_rule_set from copy import deepcopy from dataclasses import dataclass, field from typing import Dict LOGGER = logging.getLogger(__name__) Loading Loading @@ -75,8 +77,8 @@ def make_osm_wim(): class DevInfo: name: str uuid: str # endpoints name --> uuid endpoints: Dict[str, str] = field(default_factory= dict) endpoints_by_uuid: Dict[str, str] = field(default_factory= dict) def get_endpoint_uuid_or_exit(self, ep_name: str) -> str: if ep_name not in self.endpoints: Loading @@ -93,9 +95,17 @@ def get_devices(cc: ContextClient) -> Dict[str, DevInfo]: di = DevInfo(dev.name, dev.device_id.device_uuid.uuid) for ep in dev.device_endpoints: di.endpoints[ep.name] = ep.endpoint_id.endpoint_uuid.uuid di.endpoints_by_uuid[ep.endpoint_id.endpoint_uuid.uuid] = ep.name devices[dev.name] = di return devices def get_endpoint_map(devices: Dict[str, DevInfo]): ep_map = dict() for dev in devices.values(): for ep_name, ep_uuid in dev.endpoints.items(): ep_map[ep_uuid] = (dev.name, ep_name) return ep_map logging.basicConfig(level=logging.ERROR) parser = argparse.ArgumentParser(description='TF Service Management Utility') Loading Loading @@ -158,6 +168,11 @@ else: #print(f"=== WIM_MAPPING: {WIM_MAPPING}") with make_context_client() as client: # We only permit one context on our demos/testing response = client.ListContextIds(Empty()) assert len(response.context_ids) == 1 context_uuid=json_context_id(response.context_ids[0].context_uuid.uuid) osm_wim = make_osm_wim() if args.command == "create": Loading @@ -167,10 +182,36 @@ with make_context_client() as client: print(f"*** Get created service status --> {str(status)}") elif args.command == "delete": service_id = { "context_id": context_uuid, "service_uuid": { "uuid": args.service_uuid } } try: response = client.GetService(ServiceId(**service_id)) #print(grpc_message_to_json_string(response)) high_level_delete = response.service_type == ServiceTypeEnum.SERVICETYPE_L2NM or response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM print(f"Deleting service {response.name}, type {ServiceTypeEnum.Name(response.service_type)}, {high_level_delete=}") except: print(f"No service with uuid {args.service_uuid} ({service_id})") exit(-1) if high_level_delete: osm_wim.wim.check_credentials() try: osm_wim.wim.delete_connectivity_service(args.service_uuid) print(f"*** Service {args.service_uuid} is no longer present (delete was successfull or service did not exist)") print(f"*** Service {args.service_uuid} deleted (L2SM/L3SM layer)") except Exception as e: print(f"*** Failed to delete service {args.service_uuid}, {e}") else: with make_service_client() as service_client: try: service_client.DeleteService(ServiceId(**service_id)) print(f"*** Service {args.service_uuid} deleted (low level)") except Exception as e: print(f"*** Failed to delete service {args.service_uuid}, {e}") Loading @@ -188,10 +229,6 @@ with make_context_client() as client: } config_rule = json_config_rule_set('/settings', json_tapi_settings) response = client.ListContextIds(Empty()) assert len(response.context_ids) == 1 context_uuid=json_context_id(response.context_ids[0].context_uuid.uuid) devices = get_devices(client) if args.constellation not in devices: print(f"Constellation {args.constellation} does not exist as a device. See \"service-cli.py list-endpoints\"") Loading Loading @@ -236,22 +273,26 @@ with make_context_client() as client: print(f'UpdateService: {grpc_message_to_json_string(update_response)}') elif args.command == "list": devices = get_devices(client) ep_map = get_endpoint_map(devices) response = client.ListServices(ContextId(**CONTEXT_ID)) # print('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) for service in response.services: scs = "" # See if there are endpoint constraints that might be regognizable by the user. # Keys do not necessarily exist, so catch exceptions and ignore those constraints # that we cannot easily represent. for sc in service.service_constraints: try: scs += f"{sc.endpoint_location.endpoint_id.device_id.device_uuid.uuid}:{sc.endpoint_location.endpoint_id.endpoint_uuid.uuid} " except Exception: # pylint: disable=bare-except pass print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status)} {scs}") ep_list = [] for ep in service.service_endpoint_ids: ep_uuid = ep.endpoint_uuid.uuid if ep_uuid in ep_map: dev_name, ep_name = ep_map[ep_uuid] ep_list.append(f"{dev_name}:{ep_name}") ep_list.sort() eps = ", ".join(ep_list) #print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status)} {scs}") print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status):28} {eps}") elif args.command == "list-endpoints": devices = get_devices(client) Loading Loading
src/device/service/drivers/xr/README_XR.md +12 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,18 @@ arbitrary endpoints in the topology (with consequent underlying XR service insta PYTHONPATH=../../../../ ./service-cli.py list PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f ``` It is also possible to create direct XR services without multi-layer services. E.g.: ``` PYTHONPATH=../../../../ ./service-cli.py create-xr FooService X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 2|XR-T1" ``` Additionally it is possible to list services and endpoints: ``` PYTHONPATH=../../../../ ./service-cli.py list-endpoints 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. Loading
src/device/service/drivers/xr/service-cli.py +71 −30 Original line number Diff line number Diff line Loading @@ -19,31 +19,33 @@ # # Run in this directory with PYTHONPATH=../../../../ # E.g.: # Multi-layer: # Create multi-layer service (L2 VPN over XR): # PYTHONPATH=../../../../ ./service-cli.py create 1 R1-EMU 13/1/2 500 2 R3-EMU 13/1/2 500 # Single-layer: # PYTHONPATH=../../../../ ./service-cli.py create-xr mydirectservice7 X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 1|XR-T1" # # Single-layer (XR without services on top of it): # PYTHONPATH=../../../../ ./service-cli.py create-xr FooService X1-XR-CONSTELLATION "XR HUB 1|XR-T1" "XR LEAF 2|XR-T1" # List services: # PYTHONPATH=../../../../ ./service-cli.py list # List possible endpoints: # PYTHONPATH=../../../../ ./service-cli.py list-endpoints # Delete service (if multi-layer, always deleter highest layer!) # PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f import argparse import logging #import traceback from copy import deepcopy from dataclasses import dataclass, field from typing import Dict from contextlib import contextmanager from common.Settings import get_setting from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient from tests.tools.mock_osm.MockOSM import MockOSM from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum, Service, Context, Empty from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum, Service, Empty, ServiceId from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Context import json_context, json_context_id from common.tools.object_factory.Topology import json_topology_id from common.tools.object_factory.ConfigRule import json_config_rule_set from copy import deepcopy from dataclasses import dataclass, field from typing import Dict LOGGER = logging.getLogger(__name__) Loading Loading @@ -75,8 +77,8 @@ def make_osm_wim(): class DevInfo: name: str uuid: str # endpoints name --> uuid endpoints: Dict[str, str] = field(default_factory= dict) endpoints_by_uuid: Dict[str, str] = field(default_factory= dict) def get_endpoint_uuid_or_exit(self, ep_name: str) -> str: if ep_name not in self.endpoints: Loading @@ -93,9 +95,17 @@ def get_devices(cc: ContextClient) -> Dict[str, DevInfo]: di = DevInfo(dev.name, dev.device_id.device_uuid.uuid) for ep in dev.device_endpoints: di.endpoints[ep.name] = ep.endpoint_id.endpoint_uuid.uuid di.endpoints_by_uuid[ep.endpoint_id.endpoint_uuid.uuid] = ep.name devices[dev.name] = di return devices def get_endpoint_map(devices: Dict[str, DevInfo]): ep_map = dict() for dev in devices.values(): for ep_name, ep_uuid in dev.endpoints.items(): ep_map[ep_uuid] = (dev.name, ep_name) return ep_map logging.basicConfig(level=logging.ERROR) parser = argparse.ArgumentParser(description='TF Service Management Utility') Loading Loading @@ -158,6 +168,11 @@ else: #print(f"=== WIM_MAPPING: {WIM_MAPPING}") with make_context_client() as client: # We only permit one context on our demos/testing response = client.ListContextIds(Empty()) assert len(response.context_ids) == 1 context_uuid=json_context_id(response.context_ids[0].context_uuid.uuid) osm_wim = make_osm_wim() if args.command == "create": Loading @@ -167,10 +182,36 @@ with make_context_client() as client: print(f"*** Get created service status --> {str(status)}") elif args.command == "delete": service_id = { "context_id": context_uuid, "service_uuid": { "uuid": args.service_uuid } } try: response = client.GetService(ServiceId(**service_id)) #print(grpc_message_to_json_string(response)) high_level_delete = response.service_type == ServiceTypeEnum.SERVICETYPE_L2NM or response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM print(f"Deleting service {response.name}, type {ServiceTypeEnum.Name(response.service_type)}, {high_level_delete=}") except: print(f"No service with uuid {args.service_uuid} ({service_id})") exit(-1) if high_level_delete: osm_wim.wim.check_credentials() try: osm_wim.wim.delete_connectivity_service(args.service_uuid) print(f"*** Service {args.service_uuid} is no longer present (delete was successfull or service did not exist)") print(f"*** Service {args.service_uuid} deleted (L2SM/L3SM layer)") except Exception as e: print(f"*** Failed to delete service {args.service_uuid}, {e}") else: with make_service_client() as service_client: try: service_client.DeleteService(ServiceId(**service_id)) print(f"*** Service {args.service_uuid} deleted (low level)") except Exception as e: print(f"*** Failed to delete service {args.service_uuid}, {e}") Loading @@ -188,10 +229,6 @@ with make_context_client() as client: } config_rule = json_config_rule_set('/settings', json_tapi_settings) response = client.ListContextIds(Empty()) assert len(response.context_ids) == 1 context_uuid=json_context_id(response.context_ids[0].context_uuid.uuid) devices = get_devices(client) if args.constellation not in devices: print(f"Constellation {args.constellation} does not exist as a device. See \"service-cli.py list-endpoints\"") Loading Loading @@ -236,22 +273,26 @@ with make_context_client() as client: print(f'UpdateService: {grpc_message_to_json_string(update_response)}') elif args.command == "list": devices = get_devices(client) ep_map = get_endpoint_map(devices) response = client.ListServices(ContextId(**CONTEXT_ID)) # print('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) for service in response.services: scs = "" # See if there are endpoint constraints that might be regognizable by the user. # Keys do not necessarily exist, so catch exceptions and ignore those constraints # that we cannot easily represent. for sc in service.service_constraints: try: scs += f"{sc.endpoint_location.endpoint_id.device_id.device_uuid.uuid}:{sc.endpoint_location.endpoint_id.endpoint_uuid.uuid} " except Exception: # pylint: disable=bare-except pass print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status)} {scs}") ep_list = [] for ep in service.service_endpoint_ids: ep_uuid = ep.endpoint_uuid.uuid if ep_uuid in ep_map: dev_name, ep_name = ep_map[ep_uuid] ep_list.append(f"{dev_name}:{ep_name}") ep_list.sort() eps = ", ".join(ep_list) #print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status)} {scs}") print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {service.name:40} {ServiceStatusEnum.Name(service.service_status.service_status):28} {eps}") elif args.command == "list-endpoints": devices = get_devices(client) Loading