Skip to content
Snippets Groups Projects
Commit 1f0b8016 authored by Carlos Manso's avatar Carlos Manso
Browse files

ETSI MEC BW Management API initial support

parent b6bfee3d
No related branches found
No related tags found
2 merge requests!235Release TeraFlowSDN 3.0,!120Resolve "(CTTC) Implement ETSI MEC Bandwidth Management API in NBI component"
......@@ -23,6 +23,8 @@ from .rest_server.RestServer import RestServer
from .rest_server.nbi_plugins.debug_api import register_debug_api
from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss
from .rest_server.nbi_plugins.etsi_mec_015 import register_mec_015_api
terminate = threading.Event()
LOGGER = None
......@@ -62,6 +64,7 @@ def main():
register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint
register_ietf_nss(rest_server) # Registering NSS entrypoint
register_debug_api(rest_server)
register_mec_015_api(rest_server)
rest_server.start()
# Wait for Ctrl+C or termination signal
......
# 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 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)
import copy
from common.Constants import DEFAULT_CONTEXT_NAME
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
# 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 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
import time
import json
import logging
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)))
# 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_mec_015_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)
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