diff --git a/src/device/service/drivers/ietf_actn/ComposerOsuTunnel.py b/src/device/service/drivers/ietf_actn/ComposerOsuTunnel.py deleted file mode 100644 index ad369482e67d56a8cccf64db15e16f1be0effa4c..0000000000000000000000000000000000000000 --- a/src/device/service/drivers/ietf_actn/ComposerOsuTunnel.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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(), - }]} diff --git a/src/device/service/drivers/ietf_actn/Tools.py b/src/device/service/drivers/ietf_actn/Tools.py index 1b89315b1820b77f36e45938c6a7c77d01e2b5f7..ef60446e7c2fc81b5b258fa646230f8062442e23 100644 --- a/src/device/service/drivers/ietf_actn/Tools.py +++ b/src/device/service/drivers/ietf_actn/Tools.py @@ -140,40 +140,7 @@ def create_resource( url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(base_url) headers = {'content-type': 'application/json'} - data = { - 'tapi-connectivity:connectivity-service': [ - { - 'uuid': uuid, - 'connectivity-constraint': { - 'requested-capacity': { - 'total-size': { - 'value': capacity_value, - 'unit': capacity_unit - } - }, - 'connectivity-direction': direction - }, - 'end-point': [ - { - 'service-interface-point': { - 'service-interface-point-uuid': input_sip - }, - 'layer-protocol-name': layer_protocol_name, - 'layer-protocol-qualifier': layer_protocol_qualifier, - 'local-id': input_sip - }, - { - 'service-interface-point': { - 'service-interface-point-uuid': output_sip - }, - 'layer-protocol-name': layer_protocol_name, - 'layer-protocol-qualifier': layer_protocol_qualifier, - 'local-id': output_sip - } - ] - } - ] - } + data = compose_... results = [] try: LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data))) @@ -194,9 +161,9 @@ 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') + uuid = find_key(resource_value, 'uuid') - url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}' + url = '{:s}/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}' url = url.format(base_url, uuid) results = [] try: diff --git a/src/device/service/drivers/ietf_actn/examples/eth_svc_1.json b/src/device/service/drivers/ietf_actn/examples/eth_svc_1.json new file mode 100644 index 0000000000000000000000000000000000000000..840092429c28c42077d502ebb3860e4c99113e72 --- /dev/null +++ b/src/device/service/drivers/ietf_actn/examples/eth_svc_1.json @@ -0,0 +1,91 @@ +{ + "ietf-eth-tran-service:etht-svc": { + "etht-svc-instances": [ + { + "etht-svc-name": "etht_service_1", + "etht-svc-title": "ETHT_SVC_1", + "etht-svc-type": "op-mp2mp-svc?", + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "protection-role": "work?", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": false, + "is-terminal": true, + "static-route-list": [ + { + "destination": "128.32.10.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + }, + { + "destination": "128.32.20.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 21 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "protection-role": "work?", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": false, + "is-terminal": true, + "static-route-list": [ + { + "destination": "172.1.101.22", + "destination-mask": 24, + "next-hop": "172.10.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 101 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "svc-tunnel": [ + { + "tunnel-name": "osu_tunnel_1" + } + ], + "optimizations": { + "optimization-metric": [ + { + "metric-role": "work?", + "metric-type": "ietf-te-types:path-metric-te" + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/src/device/service/drivers/ietf_actn/examples/eth_svc_2.json b/src/device/service/drivers/ietf_actn/examples/eth_svc_2.json new file mode 100644 index 0000000000000000000000000000000000000000..74ca61d0399e4a2912ac280afc2eef9a50a235cf --- /dev/null +++ b/src/device/service/drivers/ietf_actn/examples/eth_svc_2.json @@ -0,0 +1,91 @@ +{ + "ietf-eth-tran-service:etht-svc": { + "etht-svc-instances": [ + { + "etht-svc-name": "etht_service_2", + "etht-svc-title": "ETHT_SVC_2", + "etht-svc-type": "op-p2mp-svc?", + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "protection-role": "work?", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": false, + "is-terminal": true, + "static-route-list": [ + { + "destination": "128.32.10.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + }, + { + "destination": "128.32.20.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 31 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "protection-role": "work?", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": false, + "is-terminal": true, + "static-route-list": [ + { + "destination": "172.1.101.22", + "destination-mask": 24, + "next-hop": "172.10.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 201 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "svc-tunnel": [ + { + "tunnel-name": "osu_tunnel_2" + } + ], + "optimizations": { + "optimization-metric": [ + { + "metric-role": "work?", + "metric-type": "ietf-te-types:path-metric-te" + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/src/device/service/drivers/ietf_actn/examples/osu_tunnel_1.json b/src/device/service/drivers/ietf_actn/examples/osu_tunnel_1.json new file mode 100644 index 0000000000000000000000000000000000000000..728450b92883ed13916847577bbd0948607ff943 --- /dev/null +++ b/src/device/service/drivers/ietf_actn/examples/osu_tunnel_1.json @@ -0,0 +1,44 @@ +{ + "ietf-te:tunnel": [ + { + "name": "osu_tunnel_1", + "title": "OSU_TUNNEL_1", + "admin-state": "ietf-te-types:tunnel-admin-state-up", + "delay": 20, + "te-bandwidth": { + "layer": "odu", + "odu-type": "osuflex", + "number": 1 + }, + "bidirectional": true, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:3-osuflex:1?", + "protection-role": "work" + } + ] + }, + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:1-osuflex:2?", + "protection-role": "work" + } + ] + }, + "restoration": { + "restoration-type": "ietf-te-types:lsp-restoration-not-applicable", + "restoration-lock": false + }, + "protection": { + "protection-type": "ietf-te-types:lsp-protection-unprotected", + "protection-reversion-disable": true + } + } + ] +} \ No newline at end of file diff --git a/src/device/service/drivers/ietf_actn/examples/osu_tunnel_2.json b/src/device/service/drivers/ietf_actn/examples/osu_tunnel_2.json new file mode 100644 index 0000000000000000000000000000000000000000..4e6966b8f53ab9ef87f930e2c7f6d9db88365ff9 --- /dev/null +++ b/src/device/service/drivers/ietf_actn/examples/osu_tunnel_2.json @@ -0,0 +1,44 @@ +{ + "ietf-te:tunnel": [ + { + "name": "osu_tunnel_2", + "title": "OSU_TUNNEL_2", + "admin-state": "ietf-te-types:tunnel-admin-state-up", + "delay": 20, + "te-bandwidth": { + "layer": "odu", + "odu-type": "osuflex", + "number": 1 + }, + "bidirectional": true, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:3-osuflex:1?", + "protection-role": "work" + } + ] + }, + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:1-osuflex:2?", + "protection-role": "work" + } + ] + }, + "restoration": { + "restoration-type": "ietf-te-types:lsp-restoration-not-applicable", + "restoration-lock": false + }, + "protection": { + "protection-type": "ietf-te-types:lsp-protection-unprotected", + "protection-reversion-disable": true + } + } + ] +} \ No newline at end of file diff --git a/src/device/service/drivers/ietf_actn/handlers/EthService.py b/src/device/service/drivers/ietf_actn/handlers/EthService.py new file mode 100644 index 0000000000000000000000000000000000000000..0d923b16cd7bcc9c0cb1c01e38c66336a1c78cd1 --- /dev/null +++ b/src/device/service/drivers/ietf_actn/handlers/EthService.py @@ -0,0 +1,106 @@ +# 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, List, Tuple + +OSU_TUNNEL_URL = '/restconf/data/ietf-te:tunnel' + +class BandwidthProfileTypeEnum(enum.Enum): + MEF_10_BWP = 'ietf-eth-tran-types:mef-10-bwp' + +class EndpointLayerSpecificAccessTypeEnum(enum.Enum): + PORT = 'port' + +class EndpointProtectionRoleEnum(enum.Enum): + WORK = 'work' + +class OptimizationMetricRole(enum.Enum): + WORK = 'work' + +class OptimizationMetricType(enum.Enum): + PATH_METRIC_TE = 'ietf-te-types:path-metric-te' + +class OuterTagTypeEnum(enum.Enum): + CLASSIFY_C_VLAN = 'ietf-eth-tran-types:classify-c-vlan' + +class ServiceClassificationTypeEnum(enum.Enum): + VLAN_CLASSIFICATION = 'ietf-eth-tran-type:vlan-classification' + +class ServiceTypeEnum(enum.Enum): + MP2MP = 'op-mp2mp-svc' + P2MP = 'op-p2mp-svc' + +def compose_outer_tag(tag_type : OuterTagTypeEnum, vlan_value : int) -> Dict: + return {'tag-type': tag_type.value, 'vlan-value': vlan_value} + +def compose_ingress_egress_bandwidth_profile() -> Dict: + return { + 'bandwidth-profile-type': BandwidthProfileTypeEnum.MEF_10_BWP.value, + 'CIR': 10_000_000, + 'EIR': 10_000_000, + } + +def compose_layer_specific_access_type() -> Dict: + return {'access-type': EndpointLayerSpecificAccessTypeEnum.PORT.value} + +def compose_static_route(prefix : str, mask : int, next_hop : str) -> Dict: + return {'destination': prefix, 'destination-mask': mask, 'next-hop': next_hop} + +def compose_static_route_list(static_routes : List[Tuple[str, int, str]]) -> List[Dict]: + return [ + compose_static_route(prefix, mask, next_hop) + for prefix, mask, next_hop in static_routes + ] + +def compose_etht_service_endpoint( + node_id : str, tp_id : str, vlan_value : int, static_routes : List[Tuple[str, int, str]] = list() +) -> Dict: + return { + 'node-id' : node_id, + 'tp-id' : tp_id, + 'protection-role' : EndpointProtectionRoleEnum.WORK.value, + 'layer-specific' : compose_layer_specific_access_type, + 'is-extendable' : False, + 'is-terminal' : True, + 'static-route-list' : compose_static_route_list(static_routes), + 'outer-tag' : compose_outer_tag(OuterTagTypeEnum.CLASSIFY_C_VLAN, vlan_value), + 'service-classification-type' : ServiceClassificationTypeEnum.VLAN_CLASSIFICATION.value, + 'ingress-egress-bandwidth-profile': compose_ingress_egress_bandwidth_profile(), + } + +def compose_optimizations() -> Dict: + return {'optimization-metric': [{ + 'metric-role': OptimizationMetricRole.WORK.value, + 'metric-type': OptimizationMetricType.PATH_METRIC_TE.value, + }]} + +def compose_etht_service( + name : str, service_type : ServiceTypeEnum, osu_tunnel_name : str, + src_node_id : str, src_tp_id : str, src_vlan_tag : int, dst_node_id : str, dst_tp_id : str, dst_vlan_tag : int, + src_static_routes : List[Tuple[str, int, str]] = list(), dst_static_routes : List[Tuple[str, int, str]] = list() +) -> Dict: + return {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': [{ + 'etht-svc-name' : name.lower(), + 'etht-svc-title': name.upper(), + 'etht-svc-type' : service_type.value, + 'source-endpoints': {'source-endpoint': [ + compose_etht_service_endpoint(src_node_id, src_tp_id, src_vlan_tag, src_static_routes), + ]}, + 'destination-endpoints': {'destination-endpoint': [ + compose_etht_service_endpoint(dst_node_id, dst_tp_id, dst_vlan_tag, dst_static_routes), + ]}, + 'svc-tunnel': [{'tunnel-name': osu_tunnel_name}], + 'optimizations': compose_optimizations(), + }]}} diff --git a/src/device/service/drivers/ietf_actn/handlers/OsuTunnel.py b/src/device/service/drivers/ietf_actn/handlers/OsuTunnel.py new file mode 100644 index 0000000000000000000000000000000000000000..a15f73eab7e550d3ba43447ba260cee19daf7fff --- /dev/null +++ b/src/device/service/drivers/ietf_actn/handlers/OsuTunnel.py @@ -0,0 +1,236 @@ +# 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, json, logging, operator, requests +from requests.auth import HTTPBasicAuth +from typing import Dict, List, Optional +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from .Tools import HTTP_OK_CODES + +LOGGER = logging.getLogger(__name__) + +BASE_URL_OSU_TUNNEL = '{:s}/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(), + }]} + +class OsuTunnel: + def __init__(self, base_url : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None) -> None: + self._base_url = base_url + self._auth = auth + self._timeout = timeout + + def get(self, resource_key : str) -> None: + url = '{:s}/restconf/data/tapi-common:context'.format(base_url) + result = [] + try: + response = requests.get(url, timeout=timeout, verify=False, auth=auth) + except requests.exceptions.Timeout: + LOGGER.exception('Timeout connecting {:s}'.format(url)) + return result + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception retrieving {:s}'.format(resource_key)) + result.append((resource_key, e)) + return result + + try: + context = json.loads(response.content) + except Exception as e: # pylint: disable=broad-except + LOGGER.warning('Unable to decode reply: {:s}'.format(str(response.content))) + result.append((resource_key, e)) + return result + + if resource_key == RESOURCE_ENDPOINTS: + if 'tapi-common:context' in context: + context = context['tapi-common:context'] + elif 'context' in context: + context = context['context'] + + for sip in context['service-interface-point']: + layer_protocol_name = sip.get('layer-protocol-name', '?') + supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) + supportable_spectrum = supportable_spectrum.get('mc-pool', {}) + supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) + supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} + grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') + granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') + direction = sip.get('direction', '?') + + endpoint_type = [layer_protocol_name, grid_type, granularity, direction] + str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type)) + sip_uuid = sip['uuid'] + + sip_names = sip.get('name', []) + sip_name = next(iter([ + sip_name['value'] + for sip_name in sip_names + if sip_name['value-name'] == 'local-name' + ]), sip_uuid) + + endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip_uuid) + endpoint_data = {'uuid': sip_uuid, 'name': sip_name, 'type': str_endpoint_type} + result.append((endpoint_url, endpoint_data)) + + elif resource_key == RESOURCE_SERVICES: + if 'tapi-common:context' in context: + context = context['tapi-common:context'] + elif 'context' in context: + context = context['context'] + + if 'tapi-connectivity:connectivity-context' in context: + context = context['tapi-connectivity:connectivity-context'] + elif 'connectivity-context' in context: + context = context['connectivity-context'] + + for conn_svc in context['connectivity-service']: + service_uuid = conn_svc['uuid'] + constraints = conn_svc.get('connectivity-constraint', {}) + total_req_cap = constraints.get('requested-capacity', {}).get('total-size', {}) + + service_url = '/services/service[{:s}]'.format(service_uuid) + service_data = { + 'uuid': service_uuid, + 'direction': constraints.get('connectivity-direction', 'UNIDIRECTIONAL'), + 'capacity_unit': total_req_cap.get('unit', '<UNDEFINED>'), + 'capacity_value': total_req_cap.get('value', '<UNDEFINED>'), + } + + for i,endpoint in enumerate(conn_svc.get('end-point', [])): + layer_protocol_name = endpoint.get('layer-protocol-name') + if layer_protocol_name is not None: + service_data['layer_protocol_name'] = layer_protocol_name + + layer_protocol_qualifier = endpoint.get('layer-protocol-qualifier') + if layer_protocol_qualifier is not None: + service_data['layer_protocol_qualifier'] = layer_protocol_qualifier + + sip = endpoint['service-interface-point']['service-interface-point-uuid'] + service_data['input_sip' if i == 0 else 'output_sip'] = sip + + result.append((service_url, service_data)) + + return result + + def update(self, resource_value : Dict) -> None: + name = resource_value['name' ] + src_node_id = resource_value['src_node_id' ] + src_tp_id = resource_value['src_tp_id' ] + src_ttp_channel_name = resource_value['src_ttp_channel_name'] + dst_node_id = resource_value['dst_node_id' ] + dst_tp_id = resource_value['dst_tp_id' ] + dst_ttp_channel_name = resource_value['dst_ttp_channel_name'] + odu_type = resource_value.get('odu_type', OduTypeEnum.OSUFLEX.value) + osuflex_number = resource_value.get('osuflex_number', 1 ) + delay = resource_value.get('delay', 20 ) + bidirectional = resource_value.get('bidirectional', True ) + + odu_type = OduTypeEnum._value2member_map_[odu_type] + + headers = {'content-type': 'application/json'} + data = compose_osu_tunnel( + name, src_node_id, src_tp_id, src_ttp_channel_name, dst_node_id, dst_tp_id, dst_ttp_channel_name, + odu_type, osuflex_number, delay, bidirectional=bidirectional + ) + + results = [] + try: + LOGGER.info('OSU Tunnel {:s}: {:s}'.format(str(name), str(data))) + response = requests.post( + self._base_url, data=json.dumps(data), timeout=self._timeout, + headers=headers, verify=False, auth=self._auth + ) + LOGGER.info('Response: {:s}'.format(str(response))) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception creating OsuTunnel(name={:s}, data={:s})'.format(str(name), str(data))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not create OsuTunnel(name={:s}, data={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(name), str(data), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results + + def delete(self, osu_tunnel_name : str) -> List[]: + url = '{:s}[name={:s}]'.format(self._base_url, osu_tunnel_name) + results = [] + try: + response = requests.delete(url=url, timeout=self._timeout, verify=False, auth=self._auth) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception deleting OsuTunnel(name={:s})'.format(str(osu_tunnel_name))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not delete OsuTunnel(name={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(osu_tunnel_name), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results diff --git a/src/device/service/drivers/ietf_actn/handlers/Tools.py b/src/device/service/drivers/ietf_actn/handlers/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..c14c65afab4001fa5c3935d1a7ef4893d43342bc --- /dev/null +++ b/src/device/service/drivers/ietf_actn/handlers/Tools.py @@ -0,0 +1,20 @@ +# 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. + +HTTP_OK_CODES = { + 200, # OK + 201, # Created + 202, # Accepted + 204, # No Content +} diff --git a/src/device/service/drivers/ietf_actn/ComposerEthService.py b/src/device/service/drivers/ietf_actn/handlers/__init__.py similarity index 99% rename from src/device/service/drivers/ietf_actn/ComposerEthService.py rename to src/device/service/drivers/ietf_actn/handlers/__init__.py index 1549d9811aa5d1c193a44ad45d0d7773236c0612..38d04994fb0fa1951fb465bc127eb72659dc2eaf 100644 --- a/src/device/service/drivers/ietf_actn/ComposerEthService.py +++ b/src/device/service/drivers/ietf_actn/handlers/__init__.py @@ -11,4 +11,3 @@ # 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. -