+++ /dev/null
-# -*- coding: utf-8 -*-
-
-##
-# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
-# All Rights Reserved.
-#
-# 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 is the thread for the http server North API.
-Two thread will be launched, with normal and administrative permissions.
-"""
-import yaml
-from uuid import uuid4
-from http import HTTPStatus
-
-__author__ = "Alfonso Tierno"
-__date__ = "2019-10-22"
-__version__ = "0.1"
-version_date = "Oct 2019"
-
-
-class SdnException(Exception):
- def __init__(self, message, http_code=HTTPStatus.BAD_REQUEST.value):
- self.http_code = http_code
- Exception.__init__(self, message)
-
-
-class Sdn:
-
- def __init__(self, db, plugins):
- self.db = db
- self.plugins = plugins
-
- def start_service(self):
- pass # TODO py3 needed to load wims and plugins
-
- def stop_service(self):
- pass # nothing needed
-
- def show_network(self, uuid):
- pass
-
- def delete_network(self, uuid):
- pass
-
- def new_network(self, network):
- pass
-
- def get_openflow_rules(self, network_id=None):
- """
- Get openflow id from DB
- :param network_id: Network id, if none all networks will be retrieved
- :return: Return a list with Openflow rules per net
- """
- # ignore input data
- if not network_id:
-
- where_ = {}
- else:
- where_ = {"net_id": network_id}
- result, content = self.db.get_table(
- SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
- WHERE=where_, FROM='of_flows')
-
- if result < 0:
- raise SdnException(str(content), -result)
- return content
-
- def edit_openflow_rules(self, network_id=None):
- """
- To make actions over the net. The action is to reinstall the openflow rules
- network_id can be 'all'
- :param network_id: Network id, if none all networks will be retrieved
- :return : Number of nets updated
- """
-
- # ignore input data
- if not network_id:
- where_ = {}
- else:
- where_ = {"uuid": network_id}
- result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
-
- if result < 0:
- raise SdnException(str(content), -result)
-
- for net in content:
- if net["type"] != "ptp" and net["type"] != "data":
- result -= 1
- continue
-
- try:
- self.net_update_ofc_thread(net['uuid'])
- except SdnException as e:
- raise SdnException("Error updating network'{}' {}".format(net['uuid'], e),
- HTTPStatus.INTERNAL_SERVER_ERROR.value)
- except Exception as e:
- raise SdnException("Error updating network '{}' {}".format(net['uuid'], e),
- HTTPStatus.INTERNAL_SERVER_ERROR.value)
-
- return result
-
- def delete_openflow_rules(self, ofc_id=None):
- """
- To make actions over the net. The action is to delete ALL openflow rules
- :return: return operation result
- """
-
- if not ofc_id:
- if 'Default' in self.config['ofcs_thread']:
- r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
- else:
- raise SdnException("Default Openflow controller not not running", HTTPStatus.NOT_FOUND.value)
-
- elif ofc_id in self.config['ofcs_thread']:
- r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
-
- # ignore input data
- if r < 0:
- raise SdnException(str(c), -r)
- else:
- raise SdnException("Openflow controller not found with ofc_id={}".format(ofc_id),
- HTTPStatus.NOT_FOUND.value)
- return r
-
- def get_openflow_ports(self, ofc_id=None):
- """
- Obtain switch ports names of openflow controller
- :return: Return flow ports in DB
- """
- if not ofc_id:
- if 'Default' in self.config['ofcs_thread']:
- conn = self.config['ofcs_thread']['Default'].OF_connector
- else:
- raise SdnException("Default Openflow controller not not running", HTTPStatus.NOT_FOUND.value)
-
- elif ofc_id in self.config['ofcs_thread']:
- conn = self.config['ofcs_thread'][ofc_id].OF_connector
- else:
- raise SdnException("Openflow controller not found with ofc_id={}".format(ofc_id),
- HTTPStatus.NOT_FOUND.value)
- return conn.pp2ofi
-
- def new_of_controller(self, ofc_data):
- """
- Create a new openflow controller into DB
- :param ofc_data: Dict openflow controller data
- :return: openflow controller uuid
- """
- db_wim = {
- "uuid": str(uuid4()),
- "name": ofc_data["name"],
- "description": ofc_data.get("description"),
- "type": ofc_data["type"],
- "wim_url": ofc_data.get("url"),
- }
- if not db_wim["wim_url"]:
- if not ofc_data.get("ip") or not ofc_data.get("port"):
- raise SdnException("Provide either 'url' or both 'ip' and 'port'")
- db_wim["wim_url"] = "{}:{}".format(ofc_data["ip"], ofc_data["port"])
-
- db_wim_account = {
- "uuid": str(uuid4()),
- "name": ofc_data["name"],
- "wim_id": db_wim["uuid"],
- "sdn": "true",
- "user": ofc_data.get("user"),
- "password": ofc_data.get("password"),
- }
- db_wim_account_config = ofc_data.get("config", {})
- if ofc_data.get("dpid"):
- db_wim_account_config["dpid"] = ofc_data["dpid"]
- if ofc_data.get("version"):
- db_wim_account_config["version"] = ofc_data["version"]
-
- db_wim_account["config"] = yaml.safe_dump(db_wim_account_config, default_flow_style=True, width=256)
-
- db_tables = [
- {"wims": db_wim},
- {"wim_accounts": db_wim_account},
- ]
- uuid_list = [db_wim["uuid"], db_wim_account["uuid"]]
- self.db.new_rows(db_tables, uuid_list)
- return db_wim_account["uuid"]
-
- def edit_of_controller(self, of_id, ofc_data):
- """
- Edit an openflow controller entry from DB
- :return:
- """
- if not ofc_data:
- raise SdnException("No data received during uptade OF contorller",
- http_code=HTTPStatus.INTERNAL_SERVER_ERROR.value)
-
- # get database wim_accounts
- wim_account = self._get_of_controller(of_id)
-
- db_wim_update = {x: ofc_data[x] for x in ("name", "description", "type", "wim_url") if x in ofc_data}
- db_wim_account_update = {x: ofc_data[x] for x in ("name", "user", "password") if x in ofc_data}
- db_wim_account_config = ofc_data.get("config", {})
-
- if ofc_data.get("ip") or ofc_data.get("port"):
- if not ofc_data.get("ip") or not ofc_data.get("port"):
- raise SdnException("Provide or both 'ip' and 'port'")
- db_wim_update["wim_url"] = "{}:{}".format(ofc_data["ip"], ofc_data["port"])
-
- if ofc_data.get("dpid"):
- db_wim_account_config["dpid"] = ofc_data["dpid"]
- if ofc_data.get("version"):
- db_wim_account_config["version"] = ofc_data["version"]
-
- if db_wim_account_config:
- db_wim_account_update["config"] = yaml.load(wim_account["config"], Loader=yaml.Loader) or {}
- db_wim_account_update["config"].update(db_wim_account_config)
- db_wim_account_update["config"] = yaml.safe_dump(db_wim_account_update["config"], default_flow_style=True,
- width=256)
-
- if db_wim_account_update:
- self.db.update_rows('wim_accounts', db_wim_account_update, WHERE={'uuid': of_id})
- if db_wim_update:
- self.db.update_rows('wims', db_wim_update, WHERE={'uuid': wim_account["wim_id"]})
-
- def _get_of_controller(self, of_id):
- wim_accounts = self.db.get_rows(FROM='wim_accounts', WHERE={"uuid": of_id, "sdn": "true"})
-
- if not wim_accounts:
- raise SdnException("Cannot find sdn controller with id='{}'".format(of_id),
- http_code=HTTPStatus.NOT_FOUND.value)
- elif len(wim_accounts) > 1:
- raise SdnException("Found more than one sdn controller with id='{}'".format(of_id),
- http_code=HTTPStatus.CONFLICT.value)
- return wim_accounts[0]
-
- def delete_of_controller(self, of_id):
- """
- Delete an openflow controller from DB.
- :param of_id: openflow controller dpid
- :return:
- """
- wim_account = self._get_of_controller(of_id)
- self.db.delete_row(FROM='wim_accounts', WHERE={"uuid": of_id})
- self.db.delete_row(FROM='wims', WHERE={"uuid": wim_account["wim_id"]})
- return of_id
-
- @staticmethod
- def _format_of_controller(wim_account, wim=None):
- of_data = {x: wim_account[x] for x in ("uuid", "name", "user")}
- if isinstance(wim_account["config"], str):
- config = yaml.load(wim_account["config"], Loader=yaml.Loader)
- of_data["dpid"] = config.get("switch_id") or config.get("dpid")
- of_data["version"] = config.get("version")
- if wim:
- of_data["url"] = wim["wim_url"]
- of_data["type"] = wim["type"]
- return of_data
-
- def show_of_controller(self, of_id):
- """
- Show an openflow controller by dpid from DB.
- :param db_filter: List with where query parameters
- :return:
- """
- wim_account = self._get_of_controller(of_id)
- wims = self.db.get_rows(FROM='wims', WHERE={"uuid": wim_account["wim_id"]})
- return self._format_of_controller(wim_account, wims[0])
-
- def get_of_controllers(self, filter=None):
- """
- Show an openflow controllers from DB.
- :return:
- """
- filter = filter or {}
- filter["sdn"] = "true"
- wim_accounts = self.db.get_rows(FROM='wim_accounts', WHERE=filter)
- return [self._format_of_controller(w) for w in wim_accounts]
-
- def set_of_port_mapping(self, maps, sdn_id, switch_dpid, vim_id):
- """
- Create new port mapping entry
- :param of_maps: List with port mapping information
- # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
- "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
- :param sdn_id: ofc id
- :param switch_dpid: switch dpid
- :param vim_id: datacenter
- :return:
- """
- # get wim from wim_account
- wim_account = self._get_of_controller(sdn_id)
- wim_id = wim_account["wim_id"]
- db_wim_port_mappings = []
- for map in maps:
- _switch_dpid = map.get("switch_id") or map.get("switch_dpid") or switch_dpid
- new_map = {
- 'wim_id': wim_id,
- 'switch_dpid': _switch_dpid,
- "switch_port": map.get("switch_port"),
- 'datacenter_id': vim_id,
- "device_id": map.get("compute_node"),
- "service_endpoint_id": _switch_dpid + "-" + str(uuid4())
- }
- if map.get("pci"):
- new_map["device_interface_id"] = map["pci"].lower()
- config = {}
- if map.get("switch_mac"):
- config["switch_mac"] = map["switch_mac"]
- if config:
- new_map["service_mapping_info"] = yaml.safe_dump(config, default_flow_style=True, width=256)
- db_wim_port_mappings.append(new_map)
-
- db_tables = [
- {"wim_port_mappings": db_wim_port_mappings},
- ]
- self.db.new_rows(db_tables, [])
- return db_wim_port_mappings
-
- def clear_of_port_mapping(self, db_filter=None):
- """
- Clear port mapping filtering using db_filter dict
- :param db_filter: Parameter to filter during remove process
- :return:
- """
- return self.db.delete_row(FROM='wim_port_mappings', WHERE=db_filter)
-
- def get_of_port_mappings(self, db_filter=None):
- """
- Retrive port mapping from DB
- :param db_filter:
- :return:
- """
- maps = self.db.get_rows(WHERE=db_filter, FROM='wim_port_mappings')
- for map in maps:
- if map.get("service_mapping_info"):
- map["service_mapping_info"] = yaml.load(map["service_mapping_info"], Loader=yaml.Loader)
- else:
- map["service_mapping_info"] = {}
- return maps
-
- def get_ports(self, instance_wim_net_id):
- # get wim_id
- instance_wim_net = self.db.get_rows(FROM='instance_wim_nets', WHERE={"uuid": instance_wim_net_id})
- wim_id = instance_wim_net[0]["wim_id"]
- switch_ports = []
- ports = self.db.get_rows(FROM='instance_interfaces', WHERE={"instance_wim_net_id": instance_wim_net_id})
- maps = self.get_of_port_mappings(db_filter={"wim_id": wim_id})
- for port in ports:
- map_ = next((x for x in maps if x.get("device_id") == port["compute_node"] and
- x.get("device_interface_id") == port["pci"]), None)
- if map_:
- switch_port = {'switch_dpid': map_.get('switch_dpid') or map_.get('switch_id'),
- 'switch_port': map_.get('switch_port')}
- if switch_port not in switch_ports:
- switch_ports.append(switch_port)
- return switch_ports
-