Skip to content
Snippets Groups Projects
Commit f2f4cbc3 authored by Shayan Hajipour's avatar Shayan Hajipour
Browse files

enhancement:

- /restconf/data endpoint added as a dummpy endpoint to TFS NBI
- connection group endpoint added to TFS NBI
- ietf_slice_handler enhanced to support TFS NBI
- slice json test files added
parent b465479a
No related branches found
No related tags found
2 merge requests!359Release TeraFlowSDN 5.0,!302Resolve "Elaborate IETF Slice NBI to support adding/deleting new sdp, connection group, and match criteria"
Showing
with 2221 additions and 273 deletions
......@@ -12,3 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from flask.json import jsonify
from flask_restful import Resource
from nbi.service.rest_server.RestServer import RestServer
from .tools.HttpStatusCodes import HTTP_CREATED
URL_PREFIX = "/restconf/data"
class BaseServer(Resource):
def post(self):
response = jsonify({})
response.status_code = HTTP_CREATED
return response
def _add_resource(rest_server: RestServer, resource: Resource, *urls, **kwargs):
urls = [(URL_PREFIX + url) for url in urls]
rest_server.add_resource(resource, *urls, **kwargs)
def register_ietf_nss(rest_server: RestServer):
_add_resource(rest_server, BaseServer, "")
......@@ -13,47 +13,61 @@
# limitations under the License.
import logging
from flask.json import jsonify
from flask_restful import Resource
from common.proto.context_pb2 import SliceStatusEnum
from common.tools.context_queries.Slice import get_slice_by_uuid
from common.tools.grpc.Tools import grpc_message_to_json
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from ..tools.Authentication import HTTP_AUTH
from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
from ..tools.HttpStatusCodes import (
HTTP_GATEWAYTIMEOUT,
HTTP_NOCONTENT,
HTTP_OK,
HTTP_SERVERERROR,
)
LOGGER = logging.getLogger(__name__)
class NSS_Service(Resource):
# @HTTP_AUTH.login_required
def get(self, slice_id : str):
LOGGER.debug('GET Slice ID: {:s}'.format(str(slice_id)))
def get(self, slice_id: str):
LOGGER.debug("GET Slice ID: {:s}".format(str(slice_id)))
try:
context_client = ContextClient()
target = get_slice_by_uuid(context_client, slice_id, rw_copy=True)
if target is None:
raise Exception('Slice({:s}) not found in database'.format(str(slice_id)))
raise Exception(
"Slice({:s}) not found in database".format(str(slice_id))
)
if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member
raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member
raise Exception("Slice retrieval failed. Wrong Slice Id was returned")
slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE
slice_status = target.slice_status.slice_status # pylint: disable=no-member
slice_status = target.slice_status.slice_status # pylint: disable=no-member
response = jsonify(grpc_message_to_json(target))
response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT
response.status_code = (
HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT
)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Retrieving Slice({:s})'.format(str(slice_id)))
response = jsonify({'error': str(e)})
except Exception as e: # pylint: disable=broad-except
LOGGER.exception(
"Something went wrong Retrieving Slice({:s})".format(str(slice_id))
)
response = jsonify({"error": str(e)})
response.status_code = HTTP_SERVERERROR
return response
# @HTTP_AUTH.login_required
def delete(self, slice_id : str):
LOGGER.debug('DELETE Slice ID: {:s}'.format(str(slice_id)))
def delete(self, slice_id: str):
LOGGER.debug("DELETE Slice ID: {:s}".format(str(slice_id)))
try:
context_client = ContextClient()
target = get_slice_by_uuid(context_client, slice_id)
......@@ -62,17 +76,25 @@ class NSS_Service(Resource):
response.status_code = HTTP_OK
if target is None:
LOGGER.warning('Slice({:s}) not found in database. Nothing done.'.format(str(slice_id)))
LOGGER.warning(
"Slice({:s}) not found in database. Nothing done.".format(
str(slice_id)
)
)
response.status_code = HTTP_NOCONTENT
else:
if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member
raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
if target.slice_id.slice_uuid.uuid != slice_id and target.name != slice_id: # pylint: disable=no-member
raise Exception(
"Slice retrieval failed. Wrong Slice Id was returned"
)
slice_client = SliceClient()
slice_client.DeleteSlice(target.slice_id)
LOGGER.debug(f"Slice({slice_id}) successfully deleted")
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Something went wrong Deleting Slice({:s})'.format(str(slice_id)))
response = jsonify({'error': str(e)})
LOGGER.exception(
"Something went wrong Deleting Slice({:s})".format(str(slice_id))
)
response = jsonify({"error": str(e)})
response.status_code = HTTP_SERVERERROR
return response
\ No newline at end of file
return response
......@@ -49,6 +49,10 @@ class NSS_Service_Match_Criteria(Resource):
)
slice_client = SliceClient()
slice_client.UpdateSlice(slice_request)
slice_request = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running(
slice_id, context_client
)
_ = context_client.SetSlice(slice_request)
response = jsonify({})
response.status_code = HTTP_CREATED
......
......@@ -12,15 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Dict
from flask import request
from flask.json import jsonify
from flask_restful import Resource
from werkzeug.exceptions import UnsupportedMediaType
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from ..tools.Authentication import HTTP_AUTH
from ..tools.HttpStatusCodes import (
......@@ -44,8 +40,8 @@ class NSS_Service_Match_Criterion(Resource):
slice_request = IETFSliceHandler.delete_match_criteria(
slice_id, sdp_id, int(match_criterion_id), context_client
)
slice_client = SliceClient()
slice_client.UpdateSlice(slice_request)
context_client = ContextClient()
_ = context_client.SetSlice(slice_request)
response = jsonify({})
response.status_code = HTTP_CREATED
......
......@@ -19,6 +19,7 @@ from flask.json import jsonify
from flask_restful import Resource
from werkzeug.exceptions import UnsupportedMediaType
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from ..tools.HttpStatusCodes import HTTP_CREATED
......@@ -39,7 +40,10 @@ class NSS_Services(Resource):
if not request.is_json:
raise UnsupportedMediaType("JSON payload is required")
request_data: Dict = request.json
slice_request = IETFSliceHandler.create_slice_service(request_data)
context_client = ContextClient()
slice_request = IETFSliceHandler.create_slice_service(
request_data, context_client
)
slice_client = SliceClient()
slice_client.CreateSlice(slice_request)
......
......@@ -20,6 +20,7 @@ from flask_restful import Resource
from werkzeug.exceptions import UnsupportedMediaType
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from ..tools.Authentication import HTTP_AUTH
from ..tools.HttpStatusCodes import HTTP_CREATED
......@@ -28,7 +29,7 @@ from .ietf_slice_handler import IETFSliceHandler
LOGGER = logging.getLogger(__name__)
class NSS_Service_Connection_Groups(Resource):
class NSS_Service_Connection_Group(Resource):
# @HTTP_AUTH.login_required
def get(self):
response = jsonify({"message": "All went well!"})
......@@ -41,6 +42,11 @@ class NSS_Service_Connection_Groups(Resource):
slice_request = IETFSliceHandler.delete_connection_group(
slice_id, connection_group_id, context_client
)
slice_client = SliceClient()
slice_client.UpdateSlice(slice_request)
slice_request = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running(
slice_id, context_client
)
_ = context_client.SetSlice(slice_request)
response = jsonify({})
......
......@@ -23,6 +23,7 @@ from .NSS_Service import NSS_Service
from .NSS_Service_Match_Criteria import NSS_Service_Match_Criteria
from .NSS_Service_Match_Criterion import NSS_Service_Match_Criterion
from .NSS_Services import NSS_Services
from .NSS_Services_Connection_Group import NSS_Service_Connection_Group
from .NSS_Services_Connection_Groups import NSS_Service_Connection_Groups
from .NSS_Services_SDP import NSS_Service_SDP
from .NSS_Services_SDPs import NSS_Service_SDPs
......@@ -57,6 +58,11 @@ def register_ietf_nss(rest_server: RestServer):
NSS_Service_Connection_Groups,
"/network-slice-services/slice-service=<string:slice_id>/connection-groups",
)
_add_resource(
rest_server,
NSS_Service_Connection_Group,
"/network-slice-services/slice-service=<string:slice_id>/connection-groups/connection-group=<string:connection_group_id>",
)
_add_resource(
rest_server,
NSS_Service_Match_Criteria,
......
import json
import logging
import uuid
from typing import Optional
from common.Constants import DEFAULT_CONTEXT_NAME
from common.proto.context_pb2 import Constraint, EndPointId, Slice, SliceStatusEnum
from common.tools.context_queries.Slice import get_slice_by_uuid
from common.proto.context_pb2 import (
ConfigRule,
Constraint,
DeviceId,
Empty,
EndPointId,
ServiceConfig,
Slice,
SliceStatusEnum,
)
from common.tools.context_queries.Slice import get_slice_by_defualt_name
from common.tools.grpc.ConfigRules import update_config_rule_custom
from common.tools.grpc.Tools import grpc_message_to_json
from context.client import ContextClient
......@@ -14,18 +24,91 @@ from .YangValidator import YangValidator
LOGGER = logging.getLogger(__name__)
RESOURCE_KEY = "ietf_data"
RUNNING_RESOURCE_KEY = "running_ietf_slice"
CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice"
ADDRESS_PREFIX = 24
RAISE_IF_DIFFERS = False
def get_custom_config_rule(
service_config: ServiceConfig, resource_key: str
) -> Optional[ConfigRule]:
for cr in service_config.config_rules:
if (
cr.WhichOneof("config_rule") == "custom"
and cr.custom.resource_key == resource_key
):
return cr
def sort_endpoints(endpoinst_list: list, sdps: list, connection_group: dict) -> list:
src_sdp_id = connection_group["connectivity-construct"][0]["p2p-sender-sdp"]
sdp_id_name_mapping = {sdp["id"]: sdp["node-id"] for sdp in sdps}
if endpoinst_list[0].device_id.device_uuid.uuid == sdp_id_name_mapping[src_sdp_id]:
return endpoinst_list
return endpoinst_list[::-1]
def replace_ont_endpoint_with_emu_dc(
endpoint_list: list, context_client: ContextClient
) -> list:
link_list = context_client.ListLinks(Empty())
links = list(link_list.links)
devices_list = context_client.ListDevices(Empty())
devices = devices_list.devices
uuid_name_map = {d.device_id.device_uuid.uuid: d.name for d in devices}
uuid_device_map = {d.device_id.device_uuid.uuid: d for d in devices}
name_device_map = {d.name: d for d in devices}
endpoint_id_1 = endpoint_list[0]
device_uuid_1 = endpoint_id_1.device_id.device_uuid.uuid
device_1 = name_device_map[device_uuid_1]
endpoint_id_2 = endpoint_list[1]
device_uuid_2 = endpoint_id_2.device_id.device_uuid.uuid
device_2 = name_device_map[device_uuid_2]
if device_1.controller_id != DeviceId():
for link in links:
link_endpoints = list(link.link_endpoint_ids)
link_ep_1 = link_endpoints[0]
link_ep_2 = link_endpoints[1]
if (
device_uuid_1 == uuid_name_map[link_ep_1.device_id.device_uuid.uuid]
and uuid_device_map[link_ep_2.device_id.device_uuid.uuid].device_type
== "emu-datacenter"
):
endpoint_list[0] = link_ep_2
break
elif device_2.controller_id != DeviceId():
for link in links:
link_endpoints = list(link.link_endpoint_ids)
link_ep_1 = link_endpoints[0]
link_ep_2 = link_endpoints[1]
if (
device_uuid_2 == uuid_name_map[link_ep_1.device_id.device_uuid.uuid]
and uuid_device_map[link_ep_2.device_id.device_uuid.uuid].device_type
== "emu-datacenter"
):
endpoint_list[1] = link_ep_2
break
else:
raise Exception(
"one of the sdps should be managed by a controller and the other one should not be controlled"
)
return endpoint_list
def validate_ietf_slice_data(request_data: dict) -> None:
yang_validator = YangValidator("ietf-network-slice-service")
_ = yang_validator.parse_to_dict(request_data)
yang_validator.destroy()
class IETFSliceHandler:
@staticmethod
def create_slice_service(request_data: dict) -> Slice:
yang_validator = YangValidator("ietf-network-slice-service")
_ = yang_validator.parse_to_dict(request_data)
yang_validator.destroy()
def create_slice_service(
request_data: dict, context_client: ContextClient
) -> Slice:
request_data = {"network-slice-services": request_data}
validate_ietf_slice_data(request_data)
slice_services = request_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_id = slice_service["id"]
......@@ -66,10 +149,8 @@ class IETFSliceHandler:
endpoint_config_rule_fields,
)
)
slice_request.slice_endpoint_ids.extend(list_endpoints)
if len(connection_group_ids) != 1:
raise Exception("SDPs target-connection-group-id do not match")
LOGGER.debug(f"Connection groups detected: {len(connection_groups)}")
list_constraints = []
for cg in connection_groups:
if cg["id"] != list(connection_group_ids)[0]:
......@@ -89,8 +170,14 @@ class IETFSliceHandler:
)
list_constraints.append(constraint)
break
else:
raise Exception("connection group not found")
list_endpoints = sort_endpoints(list_endpoints, sdps, cg)
list_endpoints = replace_ont_endpoint_with_emu_dc(
list_endpoints, context_client
)
slice_request.slice_endpoint_ids.extend(list_endpoints)
slice_request.slice_constraints.extend(list_constraints)
LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove
# TODO adding owner, needs to be recoded after updating the bindings
owner = slice_id
slice_request.slice_owner.owner_string = owner
......@@ -101,7 +188,14 @@ class IETFSliceHandler:
name: (value, RAISE_IF_DIFFERS) for name, value in request_data.items()
}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, ietf_slice_fields
slice_request.slice_config.config_rules,
RUNNING_RESOURCE_KEY,
ietf_slice_fields,
)
update_config_rule_custom(
slice_request.slice_config.config_rules,
CANDIDATE_RESOURCE_KEY,
ietf_slice_fields,
)
for ep_cr_key, ep_cr_fields in endpoint_config_rules:
......@@ -119,11 +213,14 @@ class IETFSliceHandler:
if len(sdps) != 1:
raise Exception("Number of SDPs should be 1")
new_sdp = sdps[0]
slice_request = get_slice_by_uuid(context_client, slice_uuid)
# slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request = get_slice_by_defualt_name(
context_client, slice_uuid, rw_copy=False
)
for cr in slice_request.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
if cr.custom.resource_key == CANDIDATE_RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
......@@ -134,7 +231,7 @@ class IETFSliceHandler:
slice_sdps.append(new_sdp)
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, fields
slice_request.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
return slice_request
......@@ -142,11 +239,14 @@ class IETFSliceHandler:
def delete_sdp(
slice_uuid: str, sdp_id: str, context_client: ContextClient
) -> Slice:
slice_request = get_slice_by_uuid(context_client, slice_uuid)
# slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request = get_slice_by_defualt_name(
context_client, slice_uuid, rw_copy=False
)
for cr in slice_request.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
if cr.custom.resource_key == CANDIDATE_RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
......@@ -160,7 +260,7 @@ class IETFSliceHandler:
slice_sdps.pop(sdp_idx)
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, fields
slice_request.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
return slice_request
......@@ -172,11 +272,12 @@ class IETFSliceHandler:
if len(connection_groups) != 1:
raise Exception("Number of connection groups should be 1")
new_connection_group = connection_groups[0]
slice = get_slice_by_uuid(context_client, slice_id)
# slice = get_slice_by_uuid(context_client, slice_id)
slice = get_slice_by_defualt_name(context_client, slice_id, rw_copy=False)
for cr in slice.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
if cr.custom.resource_key == CANDIDATE_RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
......@@ -186,23 +287,24 @@ class IETFSliceHandler:
slice_connection_groups = slice_service["connection-groups"]["connection-group"]
slice_connection_groups.append(new_connection_group)
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
update_config_rule_custom(slice.slice_config.config_rules, RESOURCE_KEY, fields)
update_config_rule_custom(
slice.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
validate_ietf_slice_data(ietf_data)
return slice
@staticmethod
def delete_connection_group(
slice_uuid: str, connection_group_id: str, context_client: ContextClient
) -> Slice:
slice_request = get_slice_by_uuid(context_client, slice_uuid)
for cr in slice_request.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
raise Exception("ietf data not found")
slice_services = ietf_data["network-slice-services"]["slice-service"]
# slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request = get_slice_by_defualt_name(
context_client, slice_uuid, rw_copy=False
)
slice_config = slice_request.slice_config
cr = get_custom_config_rule(slice_config, CANDIDATE_RESOURCE_KEY)
candidate_ietf_data = json.loads(cr.custom.resource_value)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_connection_groups = slice_service["connection-groups"]["connection-group"]
sdp_idx = list(
......@@ -211,27 +313,74 @@ class IETFSliceHandler:
for slice_cr in slice_connection_groups
)
).index(True)
slice_connection_groups.pop(sdp_idx)
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
removed_connection_group = slice_connection_groups.pop(sdp_idx)
fields = {
name: (value, RAISE_IF_DIFFERS)
for name, value in candidate_ietf_data.items()
}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, fields
slice_request.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
# src_sdp_id = removed_connection_group["connectivity-construct"][0][
# "p2p-sender-sdp"
# ]
# dst_sdp_id = removed_connection_group["connectivity-construct"][0][
# "p2p-receiver-sdp"
# ]
# cr = get_custom_config_rule(slice_config, RUNNING_RESOURCE_KEY)
# running_ietf_data = json.loads(cr.custom.resource_value)
# slice_services = running_ietf_data["network-slice-services"]["slice-service"]
# slice_service = slice_services[0]
# sdps = slice_service["sdps"]["sdp"]
slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED
# list_endpoints = []
# for sdp in sdps:
# if sdp["id"] == src_sdp_id:
# attachment_circuits = sdp["attachment-circuits"]["attachment-circuit"]
# if len(attachment_circuits) != 1:
# raise Exception("All SDPs should have 1 attachment-circuit")
# endpoint = EndPointId()
# endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME
# endpoint.device_id.device_uuid.uuid = sdp["node-id"]
# endpoint.endpoint_uuid.uuid = attachment_circuits[0]["ac-tp-id"]
# list_endpoints.append(endpoint)
# break
# else:
# raise Exception("Second SDP not found")
# for sdp in sdps:
# if sdp["id"] == dst_sdp_id:
# attachment_circuits = sdp["attachment-circuits"]["attachment-circuit"]
# if len(attachment_circuits) != 1:
# raise Exception("All SDPs should have 1 attachment-circuit")
# endpoint = EndPointId()
# endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME
# endpoint.device_id.device_uuid.uuid = sdp["node-id"]
# endpoint.endpoint_uuid.uuid = attachment_circuits[0]["ac-tp-id"]
# list_endpoints.append(endpoint)
# break
# else:
# raise Exception("SDP not found")
# del slice_request.slice_endpoint_ids[:]
# slice_request.slice_endpoint_ids.extend(list_endpoints)
return slice_request
@staticmethod
def create_match_criteria(
request_data: dict, slice_id: str, sdp_id: str, context_client: ContextClient
request_data: dict, slice_name: str, sdp_id: str, context_client: ContextClient
) -> Slice:
match_criteria = request_data["match-criterion"]
if len(match_criteria) != 1:
raise Exception("Number of SDPs should be 1")
new_match_criterion = match_criteria[0]
target_connection_group_id = new_match_criterion["target-connection-group-id"]
slice_request = get_slice_by_uuid(context_client, slice_id)
# slice_request = get_slice_by_uuid(context_client, slice_id)
slice_request = get_slice_by_defualt_name(
context_client, slice_name, rw_copy=False
)
for cr in slice_request.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
if cr.custom.resource_key == CANDIDATE_RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
......@@ -277,9 +426,6 @@ class IETFSliceHandler:
break
else:
raise Exception("SDP not found")
del slice_request.slice_endpoint_ids[:]
slice_request.slice_endpoint_ids.extend(list_endpoints)
LOGGER.debug(f"Connection groups detected: {len(connection_groups)}")
list_constraints = []
for cg in connection_groups:
if cg["id"] != target_connection_group_id:
......@@ -300,13 +446,18 @@ class IETFSliceHandler:
list_constraints.append(constraint)
break
else:
raise Exception("Connection group not found")
raise Exception("connection group not found")
del slice_request.slice_constraints[:]
# del slice_request.slice_endpoint_ids[:]
# list_endpoints = sort_endpoints(list_endpoints, sdps, cg)
# list_endpoints = replace_ont_endpoint_with_emu_dc(
# list_endpoints, context_client
# )
# slice_request.slice_endpoint_ids.extend(list_endpoints)
slice_request.slice_constraints.extend(list_constraints)
LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, fields
slice_request.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
return slice_request
......@@ -317,12 +468,14 @@ class IETFSliceHandler:
match_criterion_id: int,
context_client: ContextClient,
) -> Slice:
slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED
# slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request = get_slice_by_defualt_name(
context_client, slice_uuid, rw_copy=False
)
for cr in slice_request.slice_config.config_rules:
if cr.WhichOneof("config_rule") != "custom":
continue
if cr.custom.resource_key == RESOURCE_KEY:
if cr.custom.resource_key == CANDIDATE_RESOURCE_KEY:
ietf_data = json.loads(cr.custom.resource_value)
break
else:
......@@ -343,6 +496,32 @@ class IETFSliceHandler:
raise Exception("Second SDP not found")
fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()}
update_config_rule_custom(
slice_request.slice_config.config_rules, RESOURCE_KEY, fields
slice_request.slice_config.config_rules, CANDIDATE_RESOURCE_KEY, fields
)
return slice_request
@staticmethod
def copy_candidate_ietf_slice_data_to_running(
slice_uuid: str, context_client: ContextClient
) -> Slice:
# slice_request = get_slice_by_uuid(context_client, slice_uuid)
slice_request = get_slice_by_defualt_name(
context_client, slice_uuid, rw_copy=False
)
for cr in slice_request.slice_config.config_rules:
if (
cr.WhichOneof("config_rule") == "custom"
and cr.custom.resource_key == CANDIDATE_RESOURCE_KEY
):
candidate_resource_value_dict = json.loads(cr.custom.resource_value)
fields = {
name: (value, RAISE_IF_DIFFERS)
for name, value in candidate_resource_value_dict.items()
}
break
else:
raise Exception("candidate ietf slice data not found")
update_config_rule_custom(
slice_request.slice_config.config_rules, RUNNING_RESOURCE_KEY, fields
)
return slice_request
This diff is collapsed.
......@@ -6,10 +6,8 @@
"connectivity-construct": [
{
"id": 1,
"p2mp-sender-sdp": "1",
"p2mp-receiver-sdp": [
"3"
],
"p2p-sender-sdp": "1",
"p2p-receiver-sdp": "3",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
......@@ -34,10 +32,8 @@
},
{
"id": 2,
"p2mp-sender-sdp": "3",
"p2mp-receiver-sdp": [
"1"
],
"p2p-sender-sdp": "3",
"p2p-receiver-sdp": "1",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
......
......@@ -4,7 +4,7 @@
"index": 2,
"match-type": [
{
"type": "ietf-nss ietf-network-slice-service:vlan",
"type": "ietf-network-slice-service:vlan",
"value": [
"101"
]
......
{
"network-slice-services": {
"slice-service": [
{
"id": "slice1",
"description": "network slice 1, connect to VM1",
"sdps": {
"sdp": [
{
"id": "1",
"node-id": "172.16.204.220",
"sdp-ip-address": [
"172.16.204.220"
],
"service-match-criteria": {
"match-criterion": [
{
"index": 1,
"match-type": [
{
"type": "ietf-network-slice-service:vlan",
"value": [
"101"
]
},
{
"type": "ietf-network-slice-service:destination-ip-prefix",
"value": [
"172.16.104.221/24"
]
},
"slice-service": [
{
"id": "slice1",
"description": "network slice 1, connect to VM1",
"sdps": {
"sdp": [
{
"id": "1",
"node-id": "172.16.204.220",
"sdp-ip-address": [
"172.16.204.220"
],
"service-match-criteria": {
"match-criterion": [
{
"index": 1,
"match-type": [
{
"type": "ietf-network-slice-service:vlan",
"value": [
"101"
]
},
{
"type": "ietf-network-slice-service:destination-ip-prefix",
"value": [
"172.16.104.221/24"
]
},
{
"type": "ietf-network-slice-service:destination-tcp-port",
"value": [
"10500"
]
},
{
"type": "ietf-network-slice-service:source-ip-prefix",
"value": [
"172.1.101.22/24"
]
},
{
"type": "ietf-network-slice-service:source-tcp-port",
"value": [
"10200"
]
}
],
"target-connection-group-id": "line1"
}
]
},
"attachment-circuits": {
"attachment-circuit": [
{
"id": "AC POP to VM1",
"description": "AC VM1 connected to POP",
"ac-node-id": "172.16.204.220",
"ac-tp-id": "200"
}
]
}
},
{
"id": "2",
"node-id": "172.16.61.10",
"sdp-ip-address": [
"172.16.61.10"
],
"service-match-criteria": {
"match-criterion": [
{
"index": 1,
"match-type": [
{
"type": "ietf-network-slice-service:vlan",
"value": [
"21"
]
},
{
"type": "ietf-network-slice-service:source-ip-prefix",
"value": [
"172.16.104.221/24"
]
},
{
"type": "ietf-network-slice-service:source-tcp-port",
"value": [
"10500"
]
},
{
"type": "ietf-network-slice-service:destination-ip-prefix",
"value": [
"172.1.101.22/24"
]
},
{
"type": "ietf-network-slice-service:destination-tcp-port",
"value": [
"10200"
]
}
],
"target-connection-group-id": "line1"
}
]
},
"attachment-circuits": {
"attachment-circuit": [
{
"id": "AC ONT",
"description": "AC connected to PC1",
"ac-node-id": "172.16.61.10",
"ac-tp-id": "200"
}
]
}
}
]
},
"connection-groups": {
"connection-group": [
{
"id": "line1",
"connectivity-type": "point-to-point",
"connectivity-construct": [
{
"id": 1,
"p2p-sender-sdp": "1",
"p2p-receiver-sdp": "2",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
{
"type": "ietf-network-slice-service:destination-tcp-port",
"value": [
"10500"
]
"metric-type": "ietf-network-slice-service:one-way-delay-maximum",
"metric-unit": "milliseconds",
"bound": "10"
},
{
"type": "ietf-network-slice-service:source-ip-prefix",
"value": [
"172.1.101.22/24"
]
"metric-type": "ietf-network-slice-service:one-way-bandwidth",
"metric-unit": "Mbps",
"bound": "5000"
},
{
"type": "ietf-network-slice-service:source-tcp-port",
"value": [
"10200"
]
"metric-type": "ietf-network-slice-service:two-way-packet-loss",
"metric-unit": "percentage",
"percentile-value": "0.001"
}
],
"target-connection-group-id": "line1"
]
}
]
}
},
"attachment-circuits": {
"attachment-circuit": [
{
"id": "AC POP to VM1",
"description": "AC VM1 connected to POP",
"ac-node-id": "172.16.204.220",
"ac-tp-id": "200"
}
]
}
},
{
"id": "2",
"node-id": "172.16.61.10",
"sdp-ip-address": [
"172.16.61.10"
],
"service-match-criteria": {
"match-criterion": [
{
"index": 1,
"match-type": [
{
"type": "ietf-network-slice-service:vlan",
"value": [
"21"
]
},
{
"id": 2,
"p2p-sender-sdp": "2",
"p2p-receiver-sdp": "1",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
{
"type": "ietf-network-slice-service:source-ip-prefix",
"value": [
"172.16.104.221/24"
]
"metric-type": "ietf-network-slice-service:one-way-delay-maximum",
"metric-unit": "milliseconds",
"bound": "20"
},
{
"type": "ietf-network-slice-service:source-tcp-port",
"value": [
"10500"
]
"metric-type": "ietf-network-slice-service:one-way-bandwidth",
"metric-unit": "Mbps",
"bound": "1000"
},
{
"type": "ietf-network-slice-service:destination-ip-prefix",
"value": [
"172.1.101.22/24"
]
},
{
"type": "ietf-network-slice-service:destination-tcp-port",
"value": [
"10200"
]
"metric-type": "ietf-network-slice-service:two-way-packet-loss",
"metric-unit": "percentage",
"percentile-value": "0.001"
}
],
"target-connection-group-id": "line1"
}
]
},
"attachment-circuits": {
"attachment-circuit": [
{
"id": "AC ONT",
"description": "AC connected to PC1",
"ac-node-id": "172.16.61.10",
"ac-tp-id": "200"
}
]
}
}
]
},
"connection-groups": {
"connection-group": [
{
"id": "line1",
"connectivity-type": "point-to-point",
"connectivity-construct": [
{
"id": 1,
"p2p-sender-sdp": "1",
"p2p-receiver-sdp": "2",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
{
"metric-type": "ietf-network-slice-service:one-way-delay-maximum",
"metric-unit": "milliseconds",
"bound": "10"
},
{
"metric-type": "ietf-network-slice-service:one-way-bandwidth",
"metric-unit": "Mbps",
"bound": "5000"
},
{
"metric-type": "ietf-network-slice-service:two-way-packet-loss",
"metric-unit": "percentage",
"percentile-value": "0.001"
}
]
}
}
},
{
"id": 2,
"p2p-sender-sdp": "2",
"p2p-receiver-sdp": "1",
"service-slo-sle-policy": {
"slo-policy": {
"metric-bound": [
{
"metric-type": "ietf-network-slice-service:one-way-delay-maximum",
"metric-unit": "milliseconds",
"bound": "20"
},
{
"metric-type": "ietf-network-slice-service:one-way-bandwidth",
"metric-unit": "Mbps",
"bound": "1000"
},
{
"metric-type": "ietf-network-slice-service:two-way-packet-loss",
"metric-unit": "percentage",
"percentile-value": "0.001"
}
]
}
]
}
}
]
}
]
}
}
]
}
]
}
]
}
}
]
}
\ No newline at end of file
......@@ -13,13 +13,29 @@
# limitations under the License.
import json
from typing import Optional
from common.proto.context_pb2 import SliceList
from common.proto.context_pb2 import ConfigRule, ServiceConfig, SliceList
from context.client.ContextClient import ContextClient
from nbi.service.rest_server.nbi_plugins.ietf_network_slice.ietf_slice_handler import (
IETFSliceHandler,
)
def get_custom_config_rule(
service_config: ServiceConfig, resource_key: str
) -> Optional[ConfigRule]:
for cr in service_config.config_rules:
if (
cr.WhichOneof("config_rule") == "custom"
and cr.custom.resource_key == resource_key
):
return cr
RUNNING_RESOURCE_KEY = "running_ietf_slice"
CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice"
context_client = ContextClient()
with open("nbi/tests/data/slice/post_network_slice1.json", mode="r") as f:
......@@ -51,8 +67,12 @@ def test_create_slice():
global slice_1
slice_1 = IETFSliceHandler.create_slice_service(post_slice_request)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
assert ietf_data == post_slice_request
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
assert candidate_ietf_data["network-slice-services"] == post_slice_request
assert slice_1.slice_endpoint_ids[0].device_id.device_uuid.uuid == "172.16.204.220"
assert slice_1.slice_endpoint_ids[1].device_id.device_uuid.uuid == "172.16.61.10"
assert slice_1.slice_id.slice_uuid.uuid == "slice1"
......@@ -64,8 +84,12 @@ def test_create_sdp(monkeypatch):
monkeypatch.setattr(context_client, "SelectSlice", select_slice)
slice_1 = IETFSliceHandler.create_sdp(post_sdp_request, "slice1", context_client)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_sdps = slice_service["sdps"]["sdp"]
assert len(slice_sdps) == 3
......@@ -79,8 +103,12 @@ def test_create_connection_group(monkeypatch):
slice_1 = IETFSliceHandler.create_connection_group(
post_connection_group_request, "slice1", context_client
)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_connection_groups = slice_service["connection-groups"]["connection-group"]
......@@ -97,16 +125,22 @@ def test_create_match_criteria(monkeypatch):
slice_1 = IETFSliceHandler.create_match_criteria(
post_match_criteria_request, "slice1", "1", context_client
)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_sdps = slice_service["sdps"]["sdp"]
sdp1_match_criteria = slice_sdps[0]["service-match-criteria"]["match-criterion"]
slice_1 = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running("slice1", context_client)
assert len(sdp1_match_criteria) == 2
assert sdp1_match_criteria[0]["target-connection-group-id"] == "line1"
assert sdp1_match_criteria[1]["target-connection-group-id"] == "line2"
assert slice_1.slice_endpoint_ids[0].device_id.device_uuid.uuid == "172.16.61.11"
assert slice_1.slice_endpoint_ids[1].device_id.device_uuid.uuid == "172.16.204.220"
assert slice_1.slice_endpoint_ids[0].device_id.device_uuid.uuid == "172.16.204.220"
assert slice_1.slice_endpoint_ids[1].device_id.device_uuid.uuid == "172.16.61.11"
def test_delete_sdp(monkeypatch):
......@@ -115,8 +149,12 @@ def test_delete_sdp(monkeypatch):
monkeypatch.setattr(context_client, "SelectSlice", select_slice)
slice_1 = IETFSliceHandler.delete_sdp("slice1", "3", context_client)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_sdps = slice_service["sdps"]["sdp"]
assert len(slice_sdps) == 2
......@@ -127,12 +165,21 @@ def test_delete_connection_group(monkeypatch):
global slice_1
monkeypatch.setattr(context_client, "SelectSlice", select_slice)
running_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, RUNNING_RESOURCE_KEY
).custom.resource_value
)
slice_1 = IETFSliceHandler.delete_connection_group(
"slice1", "line2", context_client
)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_connection_groups = slice_service["connection-groups"]["connection-group"]
assert len(slice_connection_groups) == 1
......@@ -145,8 +192,12 @@ def test_delete_match_criteria(monkeypatch):
monkeypatch.setattr(context_client, "SelectSlice", select_slice)
slice_1 = IETFSliceHandler.delete_match_criteria("slice1", "1", 2, context_client)
ietf_data = json.loads(slice_1.slice_config.config_rules[0].custom.resource_value)
slice_services = ietf_data["network-slice-services"]["slice-service"]
candidate_ietf_data = json.loads(
get_custom_config_rule(
slice_1.slice_config, CANDIDATE_RESOURCE_KEY
).custom.resource_value
)
slice_services = candidate_ietf_data["network-slice-services"]["slice-service"]
slice_service = slice_services[0]
slice_sdps = slice_service["sdps"]["sdp"]
sdp1_match_criteria = slice_sdps[0]["service-match-criteria"]["match-criterion"]
......
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