Commit 8b3a0373 authored by Ville Hallivuori's avatar Ville Hallivuori
Browse files

Allow deletion of low level services with service-cli.py

parent 199fbb2c
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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.

+71 −30
Original line number Diff line number Diff line
@@ -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__)

@@ -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:
@@ -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')
@@ -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":
@@ -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}")

@@ -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\"")
@@ -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)