+++ /dev/null
-# -*- coding: utf-8 -*-
-##
-# Copyright 2018 Telefonica
-# All Rights Reserved.
-#
-# Contributors: Oscar Gonzalez de Dios, Manuel Lopez Bravo, Guillermo Pajares Martin
-# 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.
-#
-# This work has been performed in the context of the Metro-Haul project -
-# funded by the European Commission under Grant number 761727 through the
-# Horizon 2020 program.
-##
-"""The SDN/WIM connector is responsible for establishing wide area network
-connectivity.
-
-This SDN/WIM connector implements the standard IETF RFC 8466 "A YANG Data
- Model for Layer 2 Virtual Private Network (L2VPN) Service Delivery"
-
-It receives the endpoints and the necessary details to request
-the Layer 2 service.
-"""
-import requests
-import uuid
-import logging
-from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError
-"""CHeck layer where we move it"""
-
-
-class WimconnectorIETFL2VPN(SdnConnectorBase):
-
- def __init__(self, wim, wim_account, config=None, logger=None):
- """IETF L2VPM WIM connector
-
- Arguments: (To be completed)
- wim (dict): WIM record, as stored in the database
- wim_account (dict): WIM account record, as stored in the database
- """
- self.logger = logging.getLogger('openmano.sdnconn.ietfl2vpn')
- super().__init__(wim, wim_account, config, logger)
- self.headers = {'Content-Type': 'application/json'}
- self.mappings = {m['service_endpoint_id']: m
- for m in self.service_endpoint_mapping}
- self.user = wim_account.get("user")
- self.passwd = wim_account.get("passwordd")
- if self.user and self.passwd is not None:
- self.auth = (self.user, self.passwd)
- else:
- self.auth = None
- self.logger.info("IETFL2VPN Connector Initialized.")
-
- def check_credentials(self):
- endpoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
- try:
- response = requests.get(endpoint, auth=self.auth)
- http_code = response.status_code
- except requests.exceptions.RequestException as e:
- raise SdnConnectorError(e.message, http_code=503)
-
- if http_code != 200:
- raise SdnConnectorError("Failed while authenticating", http_code=http_code)
- self.logger.info("Credentials checked")
-
- def get_connectivity_service_status(self, service_uuid, conn_info=None):
- """Monitor the status of the connectivity service stablished
-
- Arguments:
- service_uuid: Connectivity service unique identifier
-
- Returns:
- Examples::
- {'sdn_status': 'ACTIVE'}
- {'sdn_status': 'INACTIVE'}
- {'sdn_status': 'DOWN'}
- {'sdn_status': 'ERROR'}
- """
- try:
- self.logger.info("Sending get connectivity service stuatus")
- servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
- self.wim["wim_url"], service_uuid)
- response = requests.get(servicepoint, auth=self.auth)
- if response.status_code != requests.codes.ok:
- raise SdnConnectorError("Unable to obtain connectivity servcice status", http_code=response.status_code)
- service_status = {'sdn_status': 'ACTIVE'}
- return service_status
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request Timeout", http_code=408)
-
- def search_mapp(self, connection_point):
- id = connection_point['service_endpoint_id']
- if id not in self.mappings:
- raise SdnConnectorError("Endpoint {} not located".format(str(id)))
- else:
- return self.mappings[id]
-
- def create_connectivity_service(self, service_type, connection_points, **kwargs):
- """Stablish WAN connectivity between the endpoints
-
- Arguments:
- service_type (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2),
- ``L3``.
- connection_points (list): each point corresponds to
- an entry point from the DC to the transport network. One
- connection point serves to identify the specific access and
- some other service parameters, such as encapsulation type.
- Represented by a dict as follows::
-
- {
- "service_endpoint_id": ..., (str[uuid])
- "service_endpoint_encapsulation_type": ...,
- (enum: none, dot1q, ...)
- "service_endpoint_encapsulation_info": {
- ... (dict)
- "vlan": ..., (int, present if encapsulation is dot1q)
- "vni": ... (int, present if encapsulation is vxlan),
- "peers": [(ipv4_1), (ipv4_2)]
- (present if encapsulation is vxlan)
- }
- }
-
- The service endpoint ID should be previously informed to the WIM
- engine in the RO when the WIM port mapping is registered.
-
- Keyword Arguments:
- bandwidth (int): value in kilobytes
- latency (int): value in milliseconds
-
- Other QoS might be passed as keyword arguments.
-
- Returns:
- tuple: ``(service_id, conn_info)`` containing:
- - *service_uuid* (str): UUID of the established connectivity
- service
- - *conn_info* (dict or None): Information to be stored at the
- database (or ``None``). This information will be provided to
- the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
- **MUST** be JSON/YAML-serializable (plain data structures).
-
- Raises:
- SdnConnectorException: In case of error.
- """
- if service_type == "ELINE":
- if len(connection_points) > 2:
- raise SdnConnectorError('Connections between more than 2 endpoints are not supported')
- if len(connection_points) < 2:
- raise SdnConnectorError('Connections must be of at least 2 endpoints')
- """ First step, create the vpn service """
- uuid_l2vpn = str(uuid.uuid4())
- vpn_service = {}
- vpn_service["vpn-id"] = uuid_l2vpn
- vpn_service["vpn-scv-type"] = "vpws"
- vpn_service["svc-topo"] = "any-to-any"
- vpn_service["customer-name"] = "osm"
- vpn_service_list = []
- vpn_service_list.append(vpn_service)
- vpn_service_l = {"ietf-l2vpn-svc:vpn-service": vpn_service_list}
- response_service_creation = None
- conn_info = []
- self.logger.info("Sending vpn-service :{}".format(vpn_service_l))
- try:
- endpoint_service_creation = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
- self.wim["wim_url"])
- response_service_creation = requests.post(endpoint_service_creation, headers=self.headers,
- json=vpn_service_l, auth=self.auth)
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request to create service Timeout", http_code=408)
- if response_service_creation.status_code == 409:
- raise SdnConnectorError("Service already exists", http_code=response_service_creation.status_code)
- elif response_service_creation.status_code != requests.codes.created:
- raise SdnConnectorError("Request to create service not accepted",
- http_code=response_service_creation.status_code)
- """ Second step, create the connections and vpn attachments """
- for connection_point in connection_points:
- connection_point_wan_info = self.search_mapp(connection_point)
- site_network_access = {}
- connection = {}
- if connection_point["service_endpoint_encapsulation_type"] != "none":
- if connection_point["service_endpoint_encapsulation_type"] == "dot1q":
- """ The connection is a VLAN """
- connection["encapsulation-type"] = "dot1q-vlan-tagged"
- tagged = {}
- tagged_interf = {}
- service_endpoint_encapsulation_info = connection_point["service_endpoint_encapsulation_info"]
- if service_endpoint_encapsulation_info["vlan"] is None:
- raise SdnConnectorError("VLAN must be provided")
- tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info["vlan"]
- tagged["dot1q-vlan-tagged"] = tagged_interf
- connection["tagged-interface"] = tagged
- else:
- raise NotImplementedError("Encapsulation type not implemented")
- site_network_access["connection"] = connection
- self.logger.info("Sending connection:{}".format(connection))
- vpn_attach = {}
- vpn_attach["vpn-id"] = uuid_l2vpn
- vpn_attach["site-role"] = vpn_service["svc-topo"]+"-role"
- site_network_access["vpn-attachment"] = vpn_attach
- self.logger.info("Sending vpn-attachement :{}".format(vpn_attach))
- uuid_sna = str(uuid.uuid4())
- site_network_access["network-access-id"] = uuid_sna
- site_network_access["bearer"] = connection_point_wan_info["service_mapping_info"]["bearer"]
- site_network_accesses = {}
- site_network_access_list = []
- site_network_access_list.append(site_network_access)
- site_network_accesses["ietf-l2vpn-svc:site-network-access"] = site_network_access_list
- conn_info_d = {}
- conn_info_d["site"] = connection_point_wan_info["service_mapping_info"]["site-id"]
- conn_info_d["site-network-access-id"] = site_network_access["network-access-id"]
- conn_info_d["mapping"] = None
- conn_info.append(conn_info_d)
- try:
- endpoint_site_network_access_creation = \
- "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(
- self.wim["wim_url"], connection_point_wan_info["service_mapping_info"]["site-id"])
- response_endpoint_site_network_access_creation = requests.post(
- endpoint_site_network_access_creation,
- headers=self.headers,
- json=site_network_accesses,
- auth=self.auth)
-
- if response_endpoint_site_network_access_creation.status_code == 409:
- self.delete_connectivity_service(vpn_service["vpn-id"])
- raise SdnConnectorError("Site_Network_Access with ID '{}' already exists".format(
- site_network_access["network-access-id"]),
- http_code=response_endpoint_site_network_access_creation.status_code)
-
- elif response_endpoint_site_network_access_creation.status_code == 400:
- self.delete_connectivity_service(vpn_service["vpn-id"])
- raise SdnConnectorError("Site {} does not exist".format(
- connection_point_wan_info["service_mapping_info"]["site-id"]),
- http_code=response_endpoint_site_network_access_creation.status_code)
-
- elif response_endpoint_site_network_access_creation.status_code != requests.codes.created and \
- response_endpoint_site_network_access_creation.status_code != requests.codes.no_content:
- self.delete_connectivity_service(vpn_service["vpn-id"])
- raise SdnConnectorError("Request no accepted",
- http_code=response_endpoint_site_network_access_creation.status_code)
-
- except requests.exceptions.ConnectionError:
- self.delete_connectivity_service(vpn_service["vpn-id"])
- raise SdnConnectorError("Request Timeout", http_code=408)
- return uuid_l2vpn, conn_info
-
- else:
- raise NotImplementedError
-
- def delete_connectivity_service(self, service_uuid, conn_info=None):
- """Disconnect multi-site endpoints previously connected
-
- This method should receive as the first argument the UUID generated by
- the ``create_connectivity_service``
- """
- try:
- self.logger.info("Sending delete")
- servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
- self.wim["wim_url"], service_uuid)
- response = requests.delete(servicepoint, auth=self.auth)
- if response.status_code != requests.codes.no_content:
- raise SdnConnectorError("Error in the request", http_code=response.status_code)
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request Timeout", http_code=408)
-
- def edit_connectivity_service(self, service_uuid, conn_info=None,
- connection_points=None, **kwargs):
- """Change an existing connectivity service, see
- ``create_connectivity_service``"""
-
- # sites = {"sites": {}}
- # site_list = []
- vpn_service = {}
- vpn_service["svc-topo"] = "any-to-any"
- counter = 0
- for connection_point in connection_points:
- site_network_access = {}
- connection_point_wan_info = self.search_mapp(connection_point)
- params_site = {}
- params_site["site-id"] = connection_point_wan_info["service_mapping_info"]["site-id"]
- params_site["site-vpn-flavor"] = "site-vpn-flavor-single"
- device_site = {}
- device_site["device-id"] = connection_point_wan_info["device-id"]
- params_site["devices"] = device_site
- # network_access = {}
- connection = {}
- if connection_point["service_endpoint_encapsulation_type"] != "none":
- if connection_point["service_endpoint_encapsulation_type"] == "dot1q":
- """ The connection is a VLAN """
- connection["encapsulation-type"] = "dot1q-vlan-tagged"
- tagged = {}
- tagged_interf = {}
- service_endpoint_encapsulation_info = connection_point["service_endpoint_encapsulation_info"]
- if service_endpoint_encapsulation_info["vlan"] is None:
- raise SdnConnectorError("VLAN must be provided")
- tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info["vlan"]
- tagged["dot1q-vlan-tagged"] = tagged_interf
- connection["tagged-interface"] = tagged
- else:
- raise NotImplementedError("Encapsulation type not implemented")
- site_network_access["connection"] = connection
- vpn_attach = {}
- vpn_attach["vpn-id"] = service_uuid
- vpn_attach["site-role"] = vpn_service["svc-topo"]+"-role"
- site_network_access["vpn-attachment"] = vpn_attach
- uuid_sna = conn_info[counter]["site-network-access-id"]
- site_network_access["network-access-id"] = uuid_sna
- site_network_access["bearer"] = connection_point_wan_info["service_mapping_info"]["bearer"]
- site_network_accesses = {}
- site_network_access_list = []
- site_network_access_list.append(site_network_access)
- site_network_accesses["ietf-l2vpn-svc:site-network-access"] = site_network_access_list
- try:
- endpoint_site_network_access_edit = \
- "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/sites/site={}/site-network-accesses/".format(
- self.wim["wim_url"], connection_point_wan_info["service_mapping_info"]["site-id"])
- response_endpoint_site_network_access_creation = requests.put(endpoint_site_network_access_edit,
- headers=self.headers,
- json=site_network_accesses,
- auth=self.auth)
- if response_endpoint_site_network_access_creation.status_code == 400:
- raise SdnConnectorError("Service does not exist",
- http_code=response_endpoint_site_network_access_creation.status_code)
- elif response_endpoint_site_network_access_creation.status_code != 201 and \
- response_endpoint_site_network_access_creation.status_code != 204:
- raise SdnConnectorError("Request no accepted",
- http_code=response_endpoint_site_network_access_creation.status_code)
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request Timeout", http_code=408)
- counter += 1
- return None
-
- def clear_all_connectivity_services(self):
- """Delete all WAN Links corresponding to a WIM"""
- try:
- self.logger.info("Sending clear all connectivity services")
- servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
- response = requests.delete(servicepoint, auth=self.auth)
- if response.status_code != requests.codes.no_content:
- raise SdnConnectorError("Unable to clear all connectivity services", http_code=response.status_code)
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request Timeout", http_code=408)
-
- def get_all_active_connectivity_services(self):
- """Provide information about all active connections provisioned by a
- WIM
- """
- try:
- self.logger.info("Sending get all connectivity services")
- servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(self.wim["wim_url"])
- response = requests.get(servicepoint, auth=self.auth)
- if response.status_code != requests.codes.ok:
- raise SdnConnectorError("Unable to get all connectivity services", http_code=response.status_code)
- return response
- except requests.exceptions.ConnectionError:
- raise SdnConnectorError("Request Timeout", http_code=408)