Loading src/compute/service/__main__.py +3 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ from common.Settings import ( from .ComputeService import ComputeService from .rest_server.RestServer import RestServer from .rest_server.nbi_plugins.debug_api import register_debug_api from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss Loading Loading @@ -59,9 +60,10 @@ def main(): grpc_service.start() rest_server = RestServer() register_debug_api(rest_server) register_etsi_bwm_api(rest_server) register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint register_ietf_nss(rest_server) # Registering NSS entrypoint register_debug_api(rest_server) rest_server.start() # Wait for Ctrl+C or termination signal Loading src/compute/service/rest_server/nbi_plugins/etsi_bwm/Resources.py 0 → 100644 +75 −0 Original line number Diff line number Diff line # 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. import copy from common.Constants import DEFAULT_CONTEXT_NAME from flask_restful import Resource, request from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient from .Tools import ( format_grpc_to_json, grpc_context_id, grpc_service_id, bwInfo_2_service, service_2_bwInfo) class _Resource(Resource): def __init__(self) -> None: super().__init__() self.client = ContextClient() self.service_client = ServiceClient() class BwInfo(_Resource): def get(self): service_list = self.client.ListServices(grpc_context_id(DEFAULT_CONTEXT_NAME)) bw_allocations = [service_2_bwInfo(service) for service in service_list.services] return bw_allocations def post(self): bwinfo = request.get_json() service = bwInfo_2_service(self.client, bwinfo) stripped_service = copy.deepcopy(service) stripped_service.ClearField('service_endpoint_ids') stripped_service.ClearField('service_constraints') stripped_service.ClearField('service_config') response = format_grpc_to_json(self.service_client.CreateService(stripped_service)) response = format_grpc_to_json(self.service_client.UpdateService(service)) return response class BwInfoId(_Resource): def get(self, allocationId: str): service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, allocationId)) return service_2_bwInfo(service) def put(self, allocationId: str): json_data = request.get_json() service = bwInfo_2_service(self.client, json_data) response = self.service_client.UpdateService(service) return format_grpc_to_json(response) def patch(self, allocationId: str): json_data = request.get_json() if not 'appInsId' in json_data: json_data['appInsId'] = allocationId service = bwInfo_2_service(self.client, json_data) response = self.service_client.UpdateService(service) return format_grpc_to_json(response) def delete(self, allocationId: str): self.service_client.DeleteService(grpc_service_id(DEFAULT_CONTEXT_NAME, allocationId)) return src/compute/service/rest_server/nbi_plugins/etsi_bwm/Tools.py 0 → 100644 +114 −0 Original line number Diff line number Diff line # 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. import json import logging import time from flask.json import jsonify from common.proto.context_pb2 import ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity, ConfigRule, ConfigRule_Custom, ConfigActionEnum from common.tools.grpc.Tools import grpc_message_to_json from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Service import json_service_id LOGGER = logging.getLogger(__name__) def service_2_bwInfo(service: Service) -> dict: response = {} # allocationDirection = '??' # String: 00 = Downlink (towards the UE); 01 = Uplink (towards the application/session); 10 = Symmetrical response['appInsId'] = service.service_id.context_id.context_uuid.uuid # String: Application instance identifier for constraint in service.service_constraints: if constraint.WhichOneof('constraint') == 'sla_capacity': response['fixedAllocation'] = str(constraint.sla_capacity.capacity_gbps*1000) # String: Size of requested fixed BW allocation in [bps] break for config_rule in service.service_config.config_rules: for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']: if config_rule.custom.resource_key == key: if key != 'sessionFilter': response[key] = config_rule.custom.resource_value else: response[key] = json.loads(config_rule.custom.resource_value) unixtime = time.time() response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent "seconds": int(unixtime), "nanoseconds": int(unixtime%1*1e9) } return response def bwInfo_2_service(client, bwInfo: dict) -> Service: service = Service() for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'timeStamp', 'sessionFilter']: if key not in bwInfo: continue config_rule = ConfigRule() config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule_custom = ConfigRule_Custom() config_rule_custom.resource_key = key if key != 'sessionFilter': config_rule_custom.resource_value = str(bwInfo[key]) else: config_rule_custom.resource_value = json.dumps(bwInfo[key]) config_rule.custom.CopyFrom(config_rule_custom) service.service_config.config_rules.append(config_rule) if 'sessionFilter' in bwInfo: a_ip = bwInfo['sessionFilter'][0]['sourceIp'] z_ip = bwInfo['sessionFilter'][0]['dstAddress'] devices = client.ListDevices(Empty()).devices for device in devices: for cr in device.device_config.config_rules: if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == '_connect/settings': for ep in json.loads(cr.custom.resource_value)['endpoints']: if 'ip' in ep and (ep['ip'] == a_ip or ep['ip'] == z_ip): ep_id = EndPointId() ep_id.endpoint_uuid.uuid = ep['uuid'] ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid service.service_endpoint_ids.append(ep_id) if len(service.service_endpoint_ids) < 2: LOGGER.error('No endpoints matched') return None service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM if 'appInsId' in bwInfo: service.service_id.service_uuid.uuid = bwInfo['appInsId'] service.service_id.context_id.context_uuid.uuid = 'admin' service.name = bwInfo['appInsId'] if 'fixedAllocation' in bwInfo: capacity = Constraint_SLA_Capacity() capacity.capacity_gbps = float(bwInfo['fixedAllocation']) constraint = Constraint() constraint.sla_capacity.CopyFrom(capacity) service.service_constraints.append(constraint) return service def format_grpc_to_json(grpc_reply): return jsonify(grpc_message_to_json(grpc_reply)) def grpc_context_id(context_uuid): return ContextId(**json_context_id(context_uuid)) def grpc_service_id(context_uuid, service_uuid): return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid))) src/compute/service/rest_server/nbi_plugins/etsi_bwm/__init__.py 0 → 100644 +29 −0 Original line number Diff line number Diff line # 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. from compute.service.rest_server.RestServer import RestServer from .Resources import BwInfo, BwInfoId URL_PREFIX = '/bwm/v1' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ # (endpoint_name, resource_class, resource_url) ('api.bw_info', BwInfo, '/bw_allocations'), ('api.bw_info_id', BwInfoId, '/bw_allocations/<path:allocationId>'), ] def register_etsi_bwm_api(rest_server : RestServer): for endpoint_name, resource_class, resource_url in RESOURCES: rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name) src/compute/service/rest_server/nbi_plugins/etsi_bwm/tests_etsi_bwm.txt 0 → 100644 +81 −0 Original line number Diff line number Diff line -----------------------GET----------------------- curl --request GET \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations -----------------------POST----------------------- curl --request POST \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations \ --header 'Content-Type: application/json' \ --data '{ "allocationDirection": "string", "appInsId": "service_uuid", "fixedAllocation": "123", "fixedBWPriority": "SEE_DESCRIPTION", "requestType": 0, "sessionFilter": [ { "dstAddress": "192.168.3.2", "dstPort": [ "b" ], "protocol": "string", "sourceIp": "192.168.1.2", "sourcePort": [ "a" ] } ], "timeStamp": { "nanoSeconds": 1, "seconds": 1 } }' -----------------------GET2----------------------- curl --request GET \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid -----------------------PUT----------------------- curl --request PUT \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid \ --header 'Content-Type: application/json' \ --data '{ "allocationDirection": "string", "appInsId": "service_uuid", "fixedAllocation": "123", "fixedBWPriority": "efefe", "requestType": 0, "sessionFilter": [ { "dstAddress": "192.168.3.2", "dstPort": [ "b" ], "protocol": "string", "sourceIp": "192.168.1.2", "sourcePort": [ "a" ] } ], "timeStamp": { "nanoSeconds": 1, "seconds": 1 } }' -----------------------PATCH----------------------- curl --request PATCH \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid \ --header 'Content-Type: application/json' \ --data '{ "fixedBWPriority": "uuuuuuuuuuuuuu" }' -----------------------DELETE----------------------- curl --request DELETE \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid No newline at end of file Loading
src/compute/service/__main__.py +3 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ from common.Settings import ( from .ComputeService import ComputeService from .rest_server.RestServer import RestServer from .rest_server.nbi_plugins.debug_api import register_debug_api from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss Loading Loading @@ -59,9 +60,10 @@ def main(): grpc_service.start() rest_server = RestServer() register_debug_api(rest_server) register_etsi_bwm_api(rest_server) register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint register_ietf_nss(rest_server) # Registering NSS entrypoint register_debug_api(rest_server) rest_server.start() # Wait for Ctrl+C or termination signal Loading
src/compute/service/rest_server/nbi_plugins/etsi_bwm/Resources.py 0 → 100644 +75 −0 Original line number Diff line number Diff line # 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. import copy from common.Constants import DEFAULT_CONTEXT_NAME from flask_restful import Resource, request from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient from .Tools import ( format_grpc_to_json, grpc_context_id, grpc_service_id, bwInfo_2_service, service_2_bwInfo) class _Resource(Resource): def __init__(self) -> None: super().__init__() self.client = ContextClient() self.service_client = ServiceClient() class BwInfo(_Resource): def get(self): service_list = self.client.ListServices(grpc_context_id(DEFAULT_CONTEXT_NAME)) bw_allocations = [service_2_bwInfo(service) for service in service_list.services] return bw_allocations def post(self): bwinfo = request.get_json() service = bwInfo_2_service(self.client, bwinfo) stripped_service = copy.deepcopy(service) stripped_service.ClearField('service_endpoint_ids') stripped_service.ClearField('service_constraints') stripped_service.ClearField('service_config') response = format_grpc_to_json(self.service_client.CreateService(stripped_service)) response = format_grpc_to_json(self.service_client.UpdateService(service)) return response class BwInfoId(_Resource): def get(self, allocationId: str): service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, allocationId)) return service_2_bwInfo(service) def put(self, allocationId: str): json_data = request.get_json() service = bwInfo_2_service(self.client, json_data) response = self.service_client.UpdateService(service) return format_grpc_to_json(response) def patch(self, allocationId: str): json_data = request.get_json() if not 'appInsId' in json_data: json_data['appInsId'] = allocationId service = bwInfo_2_service(self.client, json_data) response = self.service_client.UpdateService(service) return format_grpc_to_json(response) def delete(self, allocationId: str): self.service_client.DeleteService(grpc_service_id(DEFAULT_CONTEXT_NAME, allocationId)) return
src/compute/service/rest_server/nbi_plugins/etsi_bwm/Tools.py 0 → 100644 +114 −0 Original line number Diff line number Diff line # 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. import json import logging import time from flask.json import jsonify from common.proto.context_pb2 import ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity, ConfigRule, ConfigRule_Custom, ConfigActionEnum from common.tools.grpc.Tools import grpc_message_to_json from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Service import json_service_id LOGGER = logging.getLogger(__name__) def service_2_bwInfo(service: Service) -> dict: response = {} # allocationDirection = '??' # String: 00 = Downlink (towards the UE); 01 = Uplink (towards the application/session); 10 = Symmetrical response['appInsId'] = service.service_id.context_id.context_uuid.uuid # String: Application instance identifier for constraint in service.service_constraints: if constraint.WhichOneof('constraint') == 'sla_capacity': response['fixedAllocation'] = str(constraint.sla_capacity.capacity_gbps*1000) # String: Size of requested fixed BW allocation in [bps] break for config_rule in service.service_config.config_rules: for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']: if config_rule.custom.resource_key == key: if key != 'sessionFilter': response[key] = config_rule.custom.resource_value else: response[key] = json.loads(config_rule.custom.resource_value) unixtime = time.time() response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent "seconds": int(unixtime), "nanoseconds": int(unixtime%1*1e9) } return response def bwInfo_2_service(client, bwInfo: dict) -> Service: service = Service() for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'timeStamp', 'sessionFilter']: if key not in bwInfo: continue config_rule = ConfigRule() config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule_custom = ConfigRule_Custom() config_rule_custom.resource_key = key if key != 'sessionFilter': config_rule_custom.resource_value = str(bwInfo[key]) else: config_rule_custom.resource_value = json.dumps(bwInfo[key]) config_rule.custom.CopyFrom(config_rule_custom) service.service_config.config_rules.append(config_rule) if 'sessionFilter' in bwInfo: a_ip = bwInfo['sessionFilter'][0]['sourceIp'] z_ip = bwInfo['sessionFilter'][0]['dstAddress'] devices = client.ListDevices(Empty()).devices for device in devices: for cr in device.device_config.config_rules: if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == '_connect/settings': for ep in json.loads(cr.custom.resource_value)['endpoints']: if 'ip' in ep and (ep['ip'] == a_ip or ep['ip'] == z_ip): ep_id = EndPointId() ep_id.endpoint_uuid.uuid = ep['uuid'] ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid service.service_endpoint_ids.append(ep_id) if len(service.service_endpoint_ids) < 2: LOGGER.error('No endpoints matched') return None service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM if 'appInsId' in bwInfo: service.service_id.service_uuid.uuid = bwInfo['appInsId'] service.service_id.context_id.context_uuid.uuid = 'admin' service.name = bwInfo['appInsId'] if 'fixedAllocation' in bwInfo: capacity = Constraint_SLA_Capacity() capacity.capacity_gbps = float(bwInfo['fixedAllocation']) constraint = Constraint() constraint.sla_capacity.CopyFrom(capacity) service.service_constraints.append(constraint) return service def format_grpc_to_json(grpc_reply): return jsonify(grpc_message_to_json(grpc_reply)) def grpc_context_id(context_uuid): return ContextId(**json_context_id(context_uuid)) def grpc_service_id(context_uuid, service_uuid): return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid)))
src/compute/service/rest_server/nbi_plugins/etsi_bwm/__init__.py 0 → 100644 +29 −0 Original line number Diff line number Diff line # 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. from compute.service.rest_server.RestServer import RestServer from .Resources import BwInfo, BwInfoId URL_PREFIX = '/bwm/v1' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ # (endpoint_name, resource_class, resource_url) ('api.bw_info', BwInfo, '/bw_allocations'), ('api.bw_info_id', BwInfoId, '/bw_allocations/<path:allocationId>'), ] def register_etsi_bwm_api(rest_server : RestServer): for endpoint_name, resource_class, resource_url in RESOURCES: rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name)
src/compute/service/rest_server/nbi_plugins/etsi_bwm/tests_etsi_bwm.txt 0 → 100644 +81 −0 Original line number Diff line number Diff line -----------------------GET----------------------- curl --request GET \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations -----------------------POST----------------------- curl --request POST \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations \ --header 'Content-Type: application/json' \ --data '{ "allocationDirection": "string", "appInsId": "service_uuid", "fixedAllocation": "123", "fixedBWPriority": "SEE_DESCRIPTION", "requestType": 0, "sessionFilter": [ { "dstAddress": "192.168.3.2", "dstPort": [ "b" ], "protocol": "string", "sourceIp": "192.168.1.2", "sourcePort": [ "a" ] } ], "timeStamp": { "nanoSeconds": 1, "seconds": 1 } }' -----------------------GET2----------------------- curl --request GET \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid -----------------------PUT----------------------- curl --request PUT \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid \ --header 'Content-Type: application/json' \ --data '{ "allocationDirection": "string", "appInsId": "service_uuid", "fixedAllocation": "123", "fixedBWPriority": "efefe", "requestType": 0, "sessionFilter": [ { "dstAddress": "192.168.3.2", "dstPort": [ "b" ], "protocol": "string", "sourceIp": "192.168.1.2", "sourcePort": [ "a" ] } ], "timeStamp": { "nanoSeconds": 1, "seconds": 1 } }' -----------------------PATCH----------------------- curl --request PATCH \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid \ --header 'Content-Type: application/json' \ --data '{ "fixedBWPriority": "uuuuuuuuuuuuuu" }' -----------------------DELETE----------------------- curl --request DELETE \ --url http://10.1.7.203:80/restconf/bwm/v1/bw_allocations/service_uuid No newline at end of file