Commit f34fff62 authored by Carlos Natalino's avatar Carlos Natalino
Browse files

Merge branch 'develop' into feat/opt-cybersecurity

parents ac50ef03 71df8f8a
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -494,11 +494,11 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]]; then
    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
    echo

    # Dashboard: Device ConfigureDevice Details
    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_config_exec_details.json' \
    # Dashboard: Device Execution Details
    curl -X POST -H "Content-Type: application/json" -d '@src/webui/grafana_prom_device_exec_details.json' \
        ${GRAFANA_URL_UPDATED}/api/dashboards/db
    echo
    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-confdev"
    DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tfs-dev-exec"
    DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id')
    curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID}
    echo
+125 −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, random, uuid
from typing import Dict, Tuple
from compute.service.rest_server.nbi_plugins.ietf_network_slice.bindings.network_slice_services import (
    NetworkSliceServices
)

# R1 emulated devices
# Port 13-0 is Optical
# Port 13-1 is Copper
R1_UUID = "ed2388eb-5fb9-5888-a4f4-160267d3e19b"
R1_PORT_13_0_UUID_OPTICAL = "20440915-1a6c-5e7b-a80f-b0e0e51f066d"
R1_PORT_13_1_UUID_COPPER = "ff900d5d-2ac0-576c-9628-a2d016681f9d"

# R2 emulated devices
# Port 13-0 is Optical
# Port 13-1 is Copper
R2_UUID = "49ce0312-1274-523b-97b8-24d0eca2d72d"
R2_PORT_13_0_UUID_OPTICAL = "214618cb-b63b-5e66-84c2-45c1c016e5f0"
R2_PORT_13_1_UUID_COPPER = "4e0f7fb4-5d22-56ad-a00e-20bffb4860f9"

# R3 emulated devices
# Port 13-0 is Optical
# Port 13-1 is Copper
R3_UUID = "3bc8e994-a3b9-5f60-9c77-6608b1d08313"
R3_PORT_13_0_UUID_OPTICAL = "da5196f5-d651-5def-ada6-50ed6430279d"
R3_PORT_13_1_UUID_COPPER = "43d221fa-5701-5740-a129-502131f5bda2"

# R4 emulated devices
# Port 13-0 is Optical
# Port 13-1 is Copper
R4_UUID = "b43e6361-2573-509d-9a88-1793e751b10d"
R4_PORT_13_0_UUID_OPTICAL = "241b74a7-8677-595c-ad65-cc9093c1e341"
R4_PORT_13_1_UUID_COPPER = "c57abf46-caaf-5954-90cc-1fec0a69330e"

node_dict = {R1_PORT_13_1_UUID_COPPER: R1_UUID,
             R2_PORT_13_1_UUID_COPPER: R2_UUID,
             R3_PORT_13_1_UUID_COPPER: R3_UUID,
             R4_PORT_13_1_UUID_COPPER: R4_UUID}
list_endpoints = [R1_PORT_13_1_UUID_COPPER,
                  R2_PORT_13_1_UUID_COPPER,
                  R3_PORT_13_1_UUID_COPPER,
                  R4_PORT_13_1_UUID_COPPER]

list_availability= [99, 99.9, 99.99, 99.999, 99.9999]
list_bw = [10, 40, 50, 100, 150, 200, 400]
list_owner = ["Telefonica", "CTTC", "Telenor", "ADVA", "Ubitech", "ATOS"]

URL_POST = "/restconf/data/ietf-network-slice-service:ietf-nss/network-slice-services"
URL_DELETE = "/restconf/data/ietf-network-slice-service:ietf-nss/network-slice-services/slice-service="

def generate_request(seed: str) -> Tuple[Dict, str]:

    ns = NetworkSliceServices()

    # Slice 1
    suuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, str(seed)))
    slice1 = ns.slice_service[suuid]
    slice1.service_description = "Test slice for OFC 2023 demo"
    slice1.status().admin_status().status = "Planned"  # TODO not yet mapped

    # SDPS: R1 optical to R3 optical
    sdps1 = slice1.sdps().sdp
    while True:
        ep1_uuid = random.choice(list_endpoints)
        ep2_uuid = random.choice(list_endpoints)
        if ep1_uuid != ep2_uuid:
            break

    sdps1[ep1_uuid].node_id = node_dict.get(ep1_uuid)
    sdps1[ep2_uuid].node_id = node_dict.get(ep2_uuid)

    # Connectivity group: Connection construct and 2 sla constrains:
    #   - Bandwidth
    #   - Availability
    cg_uuid = str(uuid.uuid4())
    cg = slice1.connection_groups().connection_group
    cg1 = cg[cg_uuid]

    cc1 = cg1.connectivity_construct[0]
    cc1.cc_id = 5
    p2p = cc1.connectivity_construct_type.p2p()
    p2p.p2p_sender_sdp = ep1_uuid
    p2p.p2p_receiver_sdp = ep2_uuid

    slo_custom = cc1.slo_sle_policy.custom()
    metric_bounds = slo_custom.service_slo_sle_policy().metric_bounds().metric_bound

    # SLO Bandwidth
    slo_bandwidth = metric_bounds["service-slo-two-way-bandwidth"]
    slo_bandwidth.value_description = "Guaranteed bandwidth"
    slo_bandwidth.bound = int(random.choice(list_bw))
    slo_bandwidth.metric_unit = "Gbps"

    # SLO Availability
    slo_availability = metric_bounds["service-slo-availability"]
    slo_availability.value_description = "Guaranteed availability"
    slo_availability.metric_unit = "percentage"
    slo_availability.bound = random.choice(list_availability)

    json_request = {"data": ns.to_json()}

    #Last, add name and owner manually
    list_name_owner = [{"tag-type": "owner", "value": random.choice(list_owner)}]
    json_request["data"]["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"] = list_name_owner

    return (json_request, suuid)


if __name__ == "__main__":
    request = generate_request(123)
    print(json.dumps(request[0], sort_keys=True, indent=4))
+70 −11
Original line number Diff line number Diff line
@@ -37,8 +37,8 @@ LOGGER = logging.getLogger(__name__)

METRICS_POOL = MetricsPool('Device', 'RPC')

METRICS_POOL_DETAILS = MetricsPool('Device', 'exec_details', labels={
    'step_name': '',
METRICS_POOL_DETAILS = MetricsPool('Device', 'execution', labels={
    'driver': '', 'operation': '', 'step': '',
})

class DeviceServiceServicerImpl(DeviceServiceServicer):
@@ -51,11 +51,15 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def AddDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
        t0 = time.time()

        device_uuid = request.device_id.device_uuid.uuid

        connection_config_rules = check_connect_rules(request.device_config)
        check_no_endpoints(request.device_endpoints)

        t1 = time.time()

        context_client = ContextClient()
        device = get_device(context_client, device_uuid, rw_copy=True)
        if device is None:
@@ -73,10 +77,15 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
        # update device_uuid to honor UUID provided by Context
        device_uuid = device.device_id.device_uuid.uuid

        t2 = time.time()

        self.mutex_queues.wait_my_turn(device_uuid)
        t3 = time.time()
        try:
            driver : _Driver = get_driver(self.driver_instance_cache, device)

            t4 = time.time()

            errors = []

            # Sub-devices and sub-links are exposed by intermediate controllers or represent mgmt links.
@@ -86,13 +95,23 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
            new_sub_links : Dict[str, Link] = dict()

            if len(device.device_endpoints) == 0:
                t5 = time.time()
                # created from request, populate endpoints using driver
                errors.extend(populate_endpoints(
                    device, driver, self.monitoring_loops, new_sub_devices, new_sub_links))
                t6 = time.time()
                t_pop_endpoints = t6 - t5
            else:
                t_pop_endpoints = None

            if len(device.device_config.config_rules) == len(connection_config_rules):
                # created from request, populate config rules using driver
                t7 = time.time()
                errors.extend(populate_config_rules(device, driver))
                t8 = time.time()
                t_pop_config_rules = t8 - t7
            else:
                t_pop_config_rules = None

            # TODO: populate components

@@ -100,22 +119,60 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
                for error in errors: LOGGER.error(error)
                raise OperationFailedException('AddDevice', extra_details=errors)

            t9 = time.time()

            device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED
            device_id = context_client.SetDevice(device)

            t10 = time.time()

            for sub_device in new_sub_devices.values():
                context_client.SetDevice(sub_device)

            t11 = time.time()

            for sub_links in new_sub_links.values():
                context_client.SetLink(sub_links)

            t12 = time.time()

            # Update endpoint monitoring resources with UUIDs
            device_with_uuids = get_device(
                context_client, device_id.device_uuid.uuid, rw_copy=False, include_endpoints=True,
                include_components=False, include_config_rules=False)
            populate_endpoint_monitoring_resources(device_with_uuids, self.monitoring_loops)

            t13 = time.time()

            context_client.close()

            t14 = time.time()

            metrics_labels = dict(driver=driver.name, operation='add_device')

            histogram_duration : Histogram = METRICS_POOL_DETAILS.get_or_create(
                'details', MetricTypeEnum.HISTOGRAM_DURATION)
            histogram_duration.labels(step='total'              , **metrics_labels).observe(t14-t0)
            histogram_duration.labels(step='execution'          , **metrics_labels).observe(t14-t3)
            histogram_duration.labels(step='endpoint_checks'    , **metrics_labels).observe(t1-t0)
            histogram_duration.labels(step='get_device'         , **metrics_labels).observe(t2-t1)
            histogram_duration.labels(step='wait_queue'         , **metrics_labels).observe(t3-t2)
            histogram_duration.labels(step='get_driver'         , **metrics_labels).observe(t4-t3)
            histogram_duration.labels(step='set_device'         , **metrics_labels).observe(t10-t9)
            histogram_duration.labels(step='populate_monit_rsrc', **metrics_labels).observe(t13-t12)

            if t_pop_endpoints is not None:
                histogram_duration.labels(step='populate_endpoints', **metrics_labels).observe(t_pop_endpoints)

            if t_pop_config_rules is not None:
                histogram_duration.labels(step='populate_config_rules', **metrics_labels).observe(t_pop_config_rules)

            if len(new_sub_devices) > 0:
                histogram_duration.labels(step='set_sub_devices', **metrics_labels).observe(t11-t10)

            if len(new_sub_links) > 0:
                histogram_duration.labels(step='set_sub_links', **metrics_labels).observe(t12-t11)

            return device_id
        finally:
            self.mutex_queues.signal_done(device_uuid)
@@ -195,16 +252,18 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):

            t9 = time.time()

            metrics_labels = dict(driver=driver.name, operation='configure_device')

            histogram_duration : Histogram = METRICS_POOL_DETAILS.get_or_create(
                'ConfigureDevice', MetricTypeEnum.HISTOGRAM_DURATION)
            histogram_duration.labels(step_name='total'            ).observe(t9-t0)
            histogram_duration.labels(step_name='wait_queue'       ).observe(t1-t0)
            histogram_duration.labels(step_name='execution'        ).observe(t9-t1)
            histogram_duration.labels(step_name='get_device'       ).observe(t3-t2)
            histogram_duration.labels(step_name='split_rules'      ).observe(t5-t4)
            histogram_duration.labels(step_name='configure_rules'  ).observe(t6-t5)
            histogram_duration.labels(step_name='deconfigure_rules').observe(t7-t6)
            histogram_duration.labels(step_name='set_device'       ).observe(t9-t8)
                'details', MetricTypeEnum.HISTOGRAM_DURATION)
            histogram_duration.labels(step='total'            , **metrics_labels).observe(t9-t0)
            histogram_duration.labels(step='wait_queue'       , **metrics_labels).observe(t1-t0)
            histogram_duration.labels(step='execution'        , **metrics_labels).observe(t9-t1)
            histogram_duration.labels(step='get_device'       , **metrics_labels).observe(t3-t2)
            histogram_duration.labels(step='split_rules'      , **metrics_labels).observe(t5-t4)
            histogram_duration.labels(step='configure_rules'  , **metrics_labels).observe(t6-t5)
            histogram_duration.labels(step='deconfigure_rules', **metrics_labels).observe(t7-t6)
            histogram_duration.labels(step='set_device'       , **metrics_labels).observe(t9-t8)

            return device_id
        finally:
+17 −2
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ RESOURCE_ACL = '__acl__'


class _Driver:
    def __init__(self, address: str, port: int, **settings) -> None:
    def __init__(self, name : str, address: str, port: int, **settings) -> None:
        """ Initialize Driver.
            Parameters:
                address : str
@@ -37,7 +37,22 @@ class _Driver:
                **settings
                    Extra settings required by the driver.
        """
        raise NotImplementedError()
        self._name = name
        self._address = address
        self._port = port
        self._settings = settings

    @property
    def name(self): return self._name

    @property
    def address(self): return self._address

    @property
    def port(self): return self._port

    @property
    def settings(self): return self._settings

    def Connect(self) -> bool:
        """ Connect to the Device.
+5 −3
Original line number Diff line number Diff line
@@ -31,16 +31,18 @@ LOGGER = logging.getLogger(__name__)

RE_GET_ENDPOINT_FROM_INTERFACE = re.compile(r'^\/interface\[([^\]]+)\].*')

METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'emulated'})
DRIVER_NAME = 'emulated'
METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})

class EmulatedDriver(_Driver):
    def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called
    def __init__(self, address : str, port : int, **settings) -> None:
        super().__init__(DRIVER_NAME, address, port, **settings)
        self.__lock = threading.Lock()
        self.__initial = TreeNode('.')
        self.__running = TreeNode('.')
        self.__subscriptions = TreeNode('.')

        endpoints = settings.get('endpoints', [])
        endpoints = self.settings.get('endpoints', [])
        endpoint_resources = []
        for endpoint in endpoints:
            endpoint_resource = compose_resource_endpoint(endpoint)
Loading