Commit efabee0e authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Device - IETF ACTN Driver:

- Intermediate backup
parent b03b81da
Loading
Loading
Loading
Loading
+14 −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.
+80 −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 enum
from typing import Dict

OSU_TUNNEL_URL = '/restconf/data/ietf-te:tunnel'

class EndpointProtectionRoleEnum(enum.Enum):
    WORK = 'work'

class LspProtectionTypeEnum(enum.Enum):
    UNPROTECTED = 'ietf-te-types:lsp-protection-unprotected'

class LspRestorationTypeEnum(enum.Enum):
    NOT_APPLICABLE = 'ietf-te-types:lsp-restoration-not-applicable'

class TunnelAdminStateEnum(enum.Enum):
    UP = 'ietf-te-types:tunnel-admin-state-up'

class OduTypeEnum(enum.Enum):
    OSUFLEX = 'osuflex'

def compose_osu_tunnel_endpoint(
    node_id : str, tp_id : str, ttp_channel_name : str,
    protection_role : EndpointProtectionRoleEnum = EndpointProtectionRoleEnum.WORK
) -> Dict:
    return {
        'node-id': node_id, 'tp-id': tp_id, 'ttp-channel-name': ttp_channel_name,
        'protection-role': protection_role.value
    }

def compose_osu_tunnel_te_bandwidth_odu(odu_type : OduTypeEnum, number : int) -> Dict:
    return {'layer': 'odu', 'odu-type': odu_type.value, 'number': number}

def compose_osu_tunnel_protection(
    type_ : LspProtectionTypeEnum = LspProtectionTypeEnum.UNPROTECTED, reversion_disable : bool = True
) -> Dict:
    return {'protection-type': type_.value, 'protection-reversion-disable': reversion_disable}

def compose_osu_tunnel_restoration(
    type_ : LspRestorationTypeEnum = LspRestorationTypeEnum.NOT_APPLICABLE, restoration_lock : bool = False
) -> Dict:
    return {'restoration-type': type_.value, 'restoration-lock': restoration_lock}

def compose_osu_tunnel(
    name : str,
    src_node_id : str, src_tp_id : str, src_ttp_channel_name : str,
    dst_node_id : str, dst_tp_id : str, dst_ttp_channel_name : str,
    odu_type : OduTypeEnum, osuflex_number : int,
    delay : int, bidirectional : bool = True,
    admin_state : TunnelAdminStateEnum = TunnelAdminStateEnum.UP
) -> Dict:
    return {'ietf-te:tunnel': [{
        'name': name.lower(),
        'title': name.upper(),
        'admin-state': admin_state.value,
        'delay': delay,
        'te-bandwidth': compose_osu_tunnel_te_bandwidth_odu(odu_type, osuflex_number),
        'bidirectional': bidirectional,
        'source-endpoints': {'source-endpoint': [
            compose_osu_tunnel_endpoint(src_node_id, src_tp_id, src_ttp_channel_name),
        ]},
        'destination-endpoints': {'destination-endpoint': [
            compose_osu_tunnel_endpoint(dst_node_id, dst_tp_id, dst_ttp_channel_name),
        ]},
        'restoration': compose_osu_tunnel_restoration(),
        'protection': compose_osu_tunnel_protection(),
    }]}
+48 −46
Original line number Diff line number Diff line
@@ -12,12 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import logging, requests, threading
import json, logging, requests, threading
from requests.auth import HTTPBasicAuth
from typing import Any, Iterator, List, Optional, Tuple, Union
from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
from common.type_checkers.Checkers import chk_string, chk_type
from device.service.driver_api._Driver import _Driver
from device.service.drivers.ietf_actn.Tools import create_resource, delete_resource, get_resource
from . import ALL_RESOURCE_KEYS
#from .Tools import create_connectivity_service, find_key, config_getter, delete_connectivity_service

@@ -26,33 +27,40 @@ LOGGER = logging.getLogger(__name__)
DRIVER_NAME = 'ietf_actn'
METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})

DEFAULT_BASE_URL = '/restconf/data'
DEFAULT_TIMEOUT = 120

class IetfActnDriver(_Driver):
    def __init__(self, address: str, port: int, **settings) -> None:
        super().__init__(DRIVER_NAME, address, port, **settings)
        self.__lock = threading.Lock()
        self.__started = threading.Event()
        self.__terminate = threading.Event()

        username = self.settings.get('username')
        password = self.settings.get('password')
        self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None

        scheme = self.settings.get('scheme', 'http')
        self.__base_url = '{:s}://{:s}:{:d}'.format(scheme, self.address, int(self.port))
        self.__timeout = int(self.settings.get('timeout', 120))
        base_url = self.settings.get('base_url', DEFAULT_BASE_URL)
        self.__base_url = '{:s}://{:s}:{:d}{:s}'.format(scheme, address, int(port), base_url)

        self.__timeout = int(self.settings.get('timeout', DEFAULT_TIMEOUT))

    def Connect(self) -> bool:
        #url = self.__base_url + '/restconf/data/tapi-common:context'
        #with self.__lock:
        #    if self.__started.is_set(): return True
        #    try:
        #        requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth)
        #    except requests.exceptions.Timeout:
        #        LOGGER.exception('Timeout connecting {:s}'.format(str(self.__base_url)))
        #        return False
        #    except Exception:  # pylint: disable=broad-except
        #        LOGGER.exception('Exception connecting {:s}'.format(str(self.__base_url)))
        #        return False
        #    else:
        #        self.__started.set()
        url = self.__base_url + '/tapi-common:context'
        with self.__lock:
            if self.__started.is_set(): return True
            try:
                requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth)
            except requests.exceptions.Timeout:
                LOGGER.exception('Timeout connecting {:s}'.format(str(self.__base_url)))
                return False
            except Exception:  # pylint: disable=broad-except
                LOGGER.exception('Exception connecting {:s}'.format(str(self.__base_url)))
                return False
            else:
                self.__started.set()
                return True

    def Disconnect(self) -> bool:
@@ -69,13 +77,14 @@ class IetfActnDriver(_Driver):
    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
        chk_type('resources', resource_keys, list)
        results = []
        #with self.__lock:
        #    if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
        #    for i, resource_key in enumerate(resource_keys):
        #        str_resource_name = 'resource_key[#{:d}]'.format(i)
        #        chk_string(str_resource_name, resource_key, allow_empty=False)
        #        results.extend(config_getter(
        #            self.__base_url, resource_key, timeout=self.__timeout, auth=self.__auth))
        with self.__lock:
            if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
            for i, resource_key in enumerate(resource_keys):
                chk_string('resource_key[#{:d}]'.format(i), resource_key, allow_empty=False)
                results.extend(get_resource(
                    self.__base_url, resource_key,
                    timeout=self.__timeout, auth=self.__auth
                ))
        return results

    @metered_subclass_method(METRICS_POOL)
@@ -83,35 +92,28 @@ class IetfActnDriver(_Driver):
        results = []
        if len(resources) == 0:
            return results
        #with self.__lock:
        #    for resource in resources:
        #        LOGGER.info('resource = {:s}'.format(str(resource)))
        #
        #        uuid = find_key(resource, 'uuid')
        #        input_sip = find_key(resource, 'input_sip_uuid')
        #        output_sip = find_key(resource, 'output_sip_uuid')
        #        capacity_value = find_key(resource, 'capacity_value')
        #        capacity_unit = find_key(resource, 'capacity_unit')
        #        layer_protocol_name = find_key(resource, 'layer_protocol_name')
        #        layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier')
        #        direction = find_key(resource, 'direction')
        #
        #        data = create_connectivity_service(
        #            self.__base_url, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit,
        #            layer_protocol_name, layer_protocol_qualifier, timeout=self.__timeout, auth=self.__auth)
        #        results.extend(data)
        with self.__lock:
            for resource_key, resource_value in resources:
                LOGGER.info('resource: key({:s}) => value({:s})'.format(str(resource_key), str(resource_value)))
                if isinstance(value, str): value = json.loads(value)
                results.extend(create_resource(
                    self.__base_url, resource_key, resource_value,
                    timeout=self.__timeout, auth=self.__auth
                ))
        return results

    @metered_subclass_method(METRICS_POOL)
    def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
        results = []
        if len(resources) == 0: return results
        #with self.__lock:
        #    for resource in resources:
        #        LOGGER.info('resource = {:s}'.format(str(resource)))
        #        uuid = find_key(resource, 'uuid')
        #        results.extend(delete_connectivity_service(
        #            self.__base_url, uuid, timeout=self.__timeout, auth=self.__auth))
        with self.__lock:
            for resource_key, resource_value in resources:
                LOGGER.info('resource: key({:s}) => value({:s})'.format(str(resource_key), str(resource_value)))
                if isinstance(value, str): value = json.loads(value)
                results.extend(delete_resource(
                    self.__base_url, resource_key, resource_value,
                    timeout=self.__timeout, auth=self.__auth
                ))
        return results

    @metered_subclass_method(METRICS_POOL)
+25 −11
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@

import json, logging, operator, requests
from requests.auth import HTTPBasicAuth
from typing import Optional
from typing import Dict, Optional
from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES

LOGGER = logging.getLogger(__name__)
@@ -29,11 +29,11 @@ HTTP_OK_CODES = {
def find_key(resource, key):
    return json.loads(resource[1])[key]


def config_getter(
    root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
def get_resource(
    base_url : str, resource_key : str,
    auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
):
    url = '{:s}/restconf/data/tapi-common:context'.format(root_url)
    url = '{:s}/restconf/data/tapi-common:context'.format(base_url)
    result = []
    try:
        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
@@ -123,13 +123,22 @@ def config_getter(

    return result

def create_connectivity_service(
    root_url, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, layer_protocol_name,
    layer_protocol_qualifier,
def create_resource(
    base_url : str, resource_key : str, resource_value : Dict,
    auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
):

    url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(root_url)
    uuid = find_key(resource, 'uuid')
    input_sip = find_key(resource, 'input_sip_uuid')
    output_sip = find_key(resource, 'output_sip_uuid')
    capacity_value = find_key(resource, 'capacity_value')
    capacity_unit = find_key(resource, 'capacity_unit')
    layer_protocol_name = find_key(resource, 'layer_protocol_name')
    layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier')
    direction = find_key(resource, 'direction')


    url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(base_url)
    headers = {'content-type': 'application/json'}
    data = {
        'tapi-connectivity:connectivity-service': [
@@ -181,9 +190,14 @@ def create_connectivity_service(
        results.append(response.status_code in HTTP_OK_CODES)
    return results

def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None):
def delete_resource(
    base_url : str, resource_key : str, resource_value : Dict,
    auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
):
    uuid = find_key(resource, 'uuid')

    url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}'
    url = url.format(root_url, uuid)
    url = url.format(base_url, uuid)
    results = []
    try:
        response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth)