From 542c5abaeb32d3efc1637fc7f9f33aa770fe862d Mon Sep 17 00:00:00 2001 From: Ville Hallivuori <VHallivuori@infinera.com> Date: Fri, 10 Feb 2023 14:26:06 +0200 Subject: [PATCH] Add service-cli tool for improved testing of XR services --- src/device/service/drivers/xr/README_XR.md | 12 ++ src/device/service/drivers/xr/service-cli.py | 144 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100755 src/device/service/drivers/xr/service-cli.py diff --git a/src/device/service/drivers/xr/README_XR.md b/src/device/service/drivers/xr/README_XR.md index 108a36769..c741c3e80 100644 --- a/src/device/service/drivers/xr/README_XR.md +++ b/src/device/service/drivers/xr/README_XR.md @@ -137,6 +137,18 @@ 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 diff --git a/src/device/service/drivers/xr/service-cli.py b/src/device/service/drivers/xr/service-cli.py new file mode 100755 index 000000000..01bd2aaa1 --- /dev/null +++ b/src/device/service/drivers/xr/service-cli.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +#pylint: disable=invalid-name, missing-function-docstring, line-too-long, logging-fstring-interpolation, missing-class-docstring, missing-module-docstring +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Manage L2 services (with underlying XR connectivity) without need to use unit test +# files or excessive JSON definitions +# +# Run in this directory with PYTHONPATH=../../../../ +# E.g.: +# 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 + + +import argparse +import logging +import traceback +from contextlib import contextmanager + +from common.Settings import get_setting +from context.client.ContextClient import ContextClient +from tests.tools.mock_osm.MockOSM import MockOSM +from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum +from common.tools.grpc.Tools import grpc_message_to_json_string + +LOGGER = logging.getLogger(__name__) + +WIM_USERNAME = 'admin' +WIM_PASSWORD = 'admin' + +@contextmanager +def make_context_client(): + try: + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + finally: + _client.close() + +def make_osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + +logging.basicConfig(level=logging.ERROR) + +parser = argparse.ArgumentParser(description='TF Service Management Utility') +subparsers = parser.add_subparsers(dest="command") +subparsers.required = True + +create_parser = subparsers.add_parser('create') +create_parser.add_argument('site1', type=int, help='One endpoint of the service, e.g. 1') +create_parser.add_argument('device1', type=str, help='One endpoint of the service, e.g. R1-EMU') +create_parser.add_argument('interface1', type=str, help='One endpoint of the service, e.g. 13/1/2') +create_parser.add_argument('vlan1', type=int, help='VLAN in first endpoint, e.g. 500') + +create_parser.add_argument('site2', type=int, help='One endpoint of the service, e.g. 2') +create_parser.add_argument('device2', type=str, help='One endpoint of the service, e.g. R3-EMU') +create_parser.add_argument('interface2', type=str, help='One endpoint of the service, e.g. 13/1/2') +create_parser.add_argument('vlan2', type=int, help='VLAN in first endpoint, e.g. 500') + +delete_parser = subparsers.add_parser('delete') +delete_parser.add_argument('service_uuid', type=str, help='UUID of the service to be deleted') + +list_parser = subparsers.add_parser('list') + +args = parser.parse_args() + +WIM_SERVICE_TYPE = 'ELINE' +CONTEXT_ID = {'context_uuid': {'uuid': 'admin'}} + +if args.command == "create": + endpoint1 = f"{args.device1}:{args.interface1}" + endpoint2 = f"{args.device2}:{args.interface2}" + + WIM_MAPPING = [ + {'device-id': args.device1, 'service_endpoint_id': endpoint1, + 'service_mapping_info': {'bearer': {'bearer-reference': endpoint1}, 'site-id': args.site1}}, + {'device-id': args.device2, 'service_endpoint_id': endpoint2, + 'service_mapping_info': {'bearer': {'bearer-reference': endpoint2}, 'site-id': args.site2}}, + ] + WIM_SERVICE_CONNECTION_POINTS = [ + {'service_endpoint_id': endpoint1, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': args.vlan1}}, + {'service_endpoint_id': endpoint2, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': args.vlan2}}, + ] +else: + WIM_MAPPING = [] + WIM_SERVICE_CONNECTION_POINTS = [] + +#print(str(args)) +print(f"=== WIM_SERVICE_TYPE: {WIM_SERVICE_TYPE}") +print(f"=== WIM_SERVICE_CONNECTION_POINTS: {WIM_SERVICE_CONNECTION_POINTS}") +print(f"=== WIM_MAPPING: {WIM_MAPPING}") + +with make_context_client() as client: + osm_wim = make_osm_wim(); + + if args.command == "create": + service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) + print(f"*** Create connectivity service --> {service_uuid}") + status = osm_wim.get_connectivity_service_status(service_uuid) + print(f"*** Get created service status --> {str(status)}") + + elif args.command == "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)") + except Exception as e: + print(f"*** Failed to delete service {args.service_uuid}, {e}") + elif args.command == "list": + 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: + pass + + print(f"{service.service_id.service_uuid.uuid:36} {ServiceTypeEnum.Name(service.service_type):40} {ServiceStatusEnum.Name(service.service_status.service_status)} {scs}") + + -- GitLab