Skip to content
Snippets Groups Projects
Commit f5d3088b authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch 'feat/xr_device_driver_service_tool' into 'develop'

feat/xr_device_driver_service_tool

See merge request !63
parents a1ece51a 8b3a0373
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!63feat/xr_device_driver_service_tool
...@@ -146,6 +146,18 @@ arbitrary endpoints in the topology (with consequent underlying XR service insta ...@@ -146,6 +146,18 @@ arbitrary endpoints in the topology (with consequent underlying XR service insta
PYTHONPATH=../../../../ ./service-cli.py list PYTHONPATH=../../../../ ./service-cli.py list
PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f 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 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. CocroachDB persistence, it is sufficient to load the topology once and it will persist.
......
...@@ -19,21 +19,33 @@ ...@@ -19,21 +19,33 @@
# #
# Run in this directory with PYTHONPATH=../../../../ # Run in this directory with PYTHONPATH=../../../../
# E.g.: # E.g.:
# 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 # PYTHONPATH=../../../../ ./service-cli.py create 1 R1-EMU 13/1/2 500 2 R3-EMU 13/1/2 500
# 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 # 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 # PYTHONPATH=../../../../ ./service-cli.py delete 43a8046a-5dec-463d-82f7-7cc3442dbf4f
import argparse import argparse
import logging import logging
import traceback from copy import deepcopy
from dataclasses import dataclass, field
from typing import Dict
from contextlib import contextmanager from contextlib import contextmanager
from common.Settings import get_setting from common.Settings import get_setting
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from service.client.ServiceClient import ServiceClient
from tests.tools.mock_osm.MockOSM import MockOSM from tests.tools.mock_osm.MockOSM import MockOSM
from common.proto.context_pb2 import ContextId, ServiceTypeEnum, ServiceStatusEnum 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.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
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
...@@ -48,11 +60,52 @@ def make_context_client(): ...@@ -48,11 +60,52 @@ def make_context_client():
finally: finally:
_client.close() _client.close()
@contextmanager
def make_service_client():
try:
_client = ServiceClient(get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC'))
yield _client
finally:
_client.close()
def make_osm_wim(): def make_osm_wim():
wim_url = 'http://{:s}:{:s}'.format( wim_url = 'http://{:s}:{:s}'.format(
get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP')))
return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD)
@dataclass
class DevInfo:
name: str
uuid: str
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:
print(f"Endpoint {ep_name} does not exist in device {self.name}. See \"service-cli.py list-endpoints\"")
exit(-1)
return self.endpoints[ep_name]
def get_devices(cc: ContextClient) -> Dict[str, DevInfo]:
r = cc.ListDevices(Empty())
# print(grpc_message_to_json_string(r))
devices = dict()
for dev in r.devices:
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) logging.basicConfig(level=logging.ERROR)
parser = argparse.ArgumentParser(description='TF Service Management Utility') parser = argparse.ArgumentParser(description='TF Service Management Utility')
...@@ -74,6 +127,13 @@ delete_parser = subparsers.add_parser('delete') ...@@ -74,6 +127,13 @@ delete_parser = subparsers.add_parser('delete')
delete_parser.add_argument('service_uuid', type=str, help='UUID of the service to be deleted') delete_parser.add_argument('service_uuid', type=str, help='UUID of the service to be deleted')
list_parser = subparsers.add_parser('list') list_parser = subparsers.add_parser('list')
list_parser = subparsers.add_parser('list-endpoints')
create_xr_parser = subparsers.add_parser('create-xr')
create_xr_parser.add_argument('service_name', type=str, help='Service Name')
create_xr_parser.add_argument('constellation', type=str, help='XR Constellation')
create_xr_parser.add_argument('interface1', type=str, help='One endpoint of the service')
create_xr_parser.add_argument('interface2', type=str, help='Second endpoint of the service')
args = parser.parse_args() args = parser.parse_args()
...@@ -103,12 +163,17 @@ else: ...@@ -103,12 +163,17 @@ else:
WIM_SERVICE_CONNECTION_POINTS = [] WIM_SERVICE_CONNECTION_POINTS = []
#print(str(args)) #print(str(args))
print(f"=== WIM_SERVICE_TYPE: {WIM_SERVICE_TYPE}") #print(f"=== WIM_SERVICE_TYPE: {WIM_SERVICE_TYPE}")
print(f"=== WIM_SERVICE_CONNECTION_POINTS: {WIM_SERVICE_CONNECTION_POINTS}") #print(f"=== WIM_SERVICE_CONNECTION_POINTS: {WIM_SERVICE_CONNECTION_POINTS}")
print(f"=== WIM_MAPPING: {WIM_MAPPING}") #print(f"=== WIM_MAPPING: {WIM_MAPPING}")
with make_context_client() as client: with make_context_client() as client:
osm_wim = make_osm_wim(); # 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": if args.command == "create":
service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS)
...@@ -117,28 +182,122 @@ with make_context_client() as client: ...@@ -117,28 +182,122 @@ with make_context_client() as client:
print(f"*** Get created service status --> {str(status)}") print(f"*** Get created service status --> {str(status)}")
elif args.command == "delete": elif args.command == "delete":
osm_wim.wim.check_credentials() service_id = {
"context_id": context_uuid,
"service_uuid": {
"uuid": args.service_uuid
}
}
try: try:
osm_wim.wim.delete_connectivity_service(args.service_uuid) response = client.GetService(ServiceId(**service_id))
print(f"*** Service {args.service_uuid} is no longer present (delete was successfull or service did not exist)") #print(grpc_message_to_json_string(response))
except Exception as e:
print(f"*** Failed to delete service {args.service_uuid}, {e}") 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} 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}")
elif args.command == "create-xr":
CONTEXT_NAME = 'admin'
CONTEXT_ID = json_context_id(CONTEXT_NAME)
CONTEXT = json_context(CONTEXT_NAME, name=CONTEXT_NAME)
json_tapi_settings = {
'capacity_value' : 50.0,
'capacity_unit' : 'GHz',
'layer_proto_name': 'PHOTONIC_MEDIA',
'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
'direction' : 'UNIDIRECTIONAL',
}
config_rule = json_config_rule_set('/settings', json_tapi_settings)
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\"")
exit(-1)
else:
dev_info = devices[args.constellation]
constellation_uuid = dev_info.uuid
interface1_uuid = dev_info.get_endpoint_uuid_or_exit(args.interface1)
interface2_uuid = dev_info.get_endpoint_uuid_or_exit(args.interface2)
print(f"Constellation {args.constellation:40}: {constellation_uuid:36}")
print(f"Interface 1 {args.interface1:40}: {interface1_uuid:36}")
print(f"Interface 2 {args.interface2:40}: {interface2_uuid:36}")
service_request = {
"name": args.service_name,
"service_id": {
"context_id": {"context_uuid": {"uuid": response.context_ids[0].context_uuid.uuid}},
"service_uuid": {"uuid": args.service_name}
},
'service_type' : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
"service_endpoint_ids": [
{'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': interface1_uuid}, 'topology_id': json_topology_id("admin", context_id=context_uuid)},
{'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': interface2_uuid}, 'topology_id': json_topology_id("admin", context_id=context_uuid)}
],
'service_status' : {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED},
'service_constraints' : [],
}
with make_service_client() as service_client:
sr = deepcopy(service_request)
endpoints, sr['service_endpoint_ids'] = sr['service_endpoint_ids'], []
create_response = service_client.CreateService(Service(**sr))
print(f'CreateService: {grpc_message_to_json_string(create_response)}')
sr['service_endpoint_ids'] = endpoints
#sr['service_id']['service_uuid'] = create_response
sr['service_config'] = {'config_rules': [config_rule]}
update_response = service_client.UpdateService(Service(**sr))
print(f'UpdateService: {grpc_message_to_json_string(update_response)}')
elif args.command == "list": elif args.command == "list":
devices = get_devices(client)
ep_map = get_endpoint_map(devices)
response = client.ListServices(ContextId(**CONTEXT_ID)) response = client.ListServices(ContextId(**CONTEXT_ID))
#print('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) # print('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response)))
for service in response.services: for service in response.services:
scs = "" 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}") 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)
for name in sorted(devices.keys()):
dev = devices[name]
print(f"{name:40} {dev.uuid:36}")
for ep_name in sorted(dev.endpoints.keys()):
print(f" {ep_name:40} {dev.endpoints[ep_name]:36}")
...@@ -17,7 +17,11 @@ export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get service/contextservice --namesp ...@@ -17,7 +17,11 @@ export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get service/contextservice --namesp
export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service/contextservice --namespace tfs -o jsonpath='{.spec.ports[?(@.name=="grpc")].port}') export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service/contextservice --namespace tfs -o jsonpath='{.spec.ports[?(@.name=="grpc")].port}')
export COMPUTESERVICE_SERVICE_HOST=$(kubectl get service/computeservice --namespace tfs --template '{{.spec.clusterIP}}') export COMPUTESERVICE_SERVICE_HOST=$(kubectl get service/computeservice --namespace tfs --template '{{.spec.clusterIP}}')
export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service/computeservice --namespace tfs -o jsonpath='{.spec.ports[?(@.name=="http")].port}') export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service/computeservice --namespace tfs -o jsonpath='{.spec.ports[?(@.name=="http")].port}')
export SERVICESERVICE_SERVICE_HOST=$(kubectl get service/serviceservice --namespace tfs --template '{{.spec.clusterIP}}')
export SERVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service/serviceservice --namespace tfs -o jsonpath='{.spec.ports[?(@.name=="grpc")].port}')
echo "CONTEXTSERVICE_SERVICE_HOST=$CONTEXTSERVICE_SERVICE_HOST" echo "CONTEXTSERVICE_SERVICE_HOST=$CONTEXTSERVICE_SERVICE_HOST"
echo "CONTEXTSERVICE_SERVICE_PORT_GRPC=$CONTEXTSERVICE_SERVICE_PORT_GRPC" echo "CONTEXTSERVICE_SERVICE_PORT_GRPC=$CONTEXTSERVICE_SERVICE_PORT_GRPC"
echo "COMPUTESERVICE_SERVICE_HOST=$COMPUTESERVICE_SERVICE_HOST" echo "COMPUTESERVICE_SERVICE_HOST=$COMPUTESERVICE_SERVICE_HOST"
echo "COMPUTESERVICE_SERVICE_PORT_HTTP=$COMPUTESERVICE_SERVICE_PORT_HTTP" echo "COMPUTESERVICE_SERVICE_PORT_HTTP=$COMPUTESERVICE_SERVICE_PORT_HTTP"
echo "SERVICESERVICE_SERVICE_HOST=$SERVICESERVICE_SERVICE_HOST"
echo "SERVICESERVICE_SERVICE_PORT_GRPC=$SERVICESERVICE_SERVICE_PORT_GRPC"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment