blob: 91cc9b2c17fa94a12d8a1d9d0f4a87423b89cf25 [file] [log] [blame]
# -*- 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():
running_info = {} # TODO OVIM move the info of running threads from config_dic to this static variable
of_module = {}
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 dpid
"""
db_wim = {
"uuid": str(uuid4()),
"name": ofc_data["name"],
"description": "",
"type": ofc_data["type"],
"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"),
"config": yaml.safe_dump({"dpid": ofc_data["dpid"], "version": ofc_data.get("version")},
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)
old_of_controller = self.show_of_controller(of_id)
if old_of_controller:
result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
if result >= 0:
return ofc_data
else:
raise SdnException("Error uptating OF contorller with uuid {}".format(of_id),
http_code=-result)
else:
raise SdnException("Error uptating OF contorller with uuid {}".format(of_id),
http_code=HTTPStatus.INTERNAL_SERVER_ERROR.value)
def delete_of_controller(self, of_id):
"""
Delete an openflow controller from DB.
:param of_id: openflow controller dpid
:return:
"""
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)
self.db.delete_row(FROM='wim_accounts', WHERE={"uuid": of_id})
self.db.delete_row(FROM='wims', WHERE={"uuid": wim_accounts[0]["wim_id"]})
return of_id
def _format_of_controller(self, 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("dpid")
of_data["version"] = config.get("version")
if wim:
ip, port = wim["wim_url"].split(":")
of_data["ip"] = ip
of_data["port"] = port
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_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)
wims = self.db.get_rows(FROM='wims', WHERE={"uuid": wim_accounts[0]["wim_id"]})
return self._format_of_controller(wim_accounts[0], 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_accounts = self.db.get_rows(FROM='wim_accounts', WHERE={"uuid": sdn_id})
if not wim_accounts:
raise SdnException("Not found sdn id={}".format(sdn_id), http_code=HTTPStatus.NOT_FOUND.value)
wim_id = wim_accounts[0]["wim_id"]
db_wim_port_mappings = []
for map in maps:
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