Added LICENSE file to root folder
[osm/openvim.git] / osm_openvim / ovim.py
index 2fca38f..612e4df 100755 (executable)
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 
 ##
-# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
+# Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
 # This file is part of openvim
 # All Rights Reserved.
 #
@@ -43,9 +43,9 @@ import openflow_conn
 
 __author__ = "Alfonso Tierno, Leonardo Mirabal"
 __date__ = "$06-Feb-2017 12:07:15$"
-__version__ = "0.5.18-r534"
-version_date = "Jun 2017"
-database_version = 21      #needed database schema version
+__version__ = "0.5.29-r549"
+version_date = "Sep 2018"
+database_version = 23      #needed database schema version
 
 HTTP_Bad_Request =          400
 HTTP_Unauthorized =         401
@@ -189,7 +189,6 @@ class ovim():
             self.get_version(), self.get_version_date(), self.get_database_version()))
         # create database connection for openflow threads
         self.config["db"] = self._create_database_connection()
-        self.config["db_lock"] = threading.Lock()
 
         self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
 
@@ -199,8 +198,8 @@ class ovim():
         host_develop_bridge_iface = self.config.get('development_bridge', None)
 
         # get host list from data base before starting threads
-        r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid', 'password', 'keyfile'),
-                                     FROM='hosts', WHERE={'status': 'ok'})
+        r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid', 'hypervisors', 'password', 'keyfile'),
+                                     FROM='hosts', WHERE={'status': 'ok'}) #Unikernels extension
         if r < 0:
             raise ovimException("Cannot get hosts from database {}".format(hosts))
 
@@ -210,11 +209,12 @@ class ovim():
             thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.config["db"],
                                     password=host['password'],
                                     keyfile=host.get('keyfile', self.config["host_ssh_keyfile"]),
-                                    db_lock=self.config["db_lock"], test=host_test_mode,
+                                    test=host_test_mode,
                                     image_path=self.config['host_image_path'],
                                     version=self.config['version'], host_id=host['uuid'],
                                     develop_mode=host_develop_mode,
                                     develop_bridge_iface=host_develop_bridge_iface,
+                                    hypervisors=host['hypervisors'],  #Unikernels extension
                                     logger_name=self.logger_name + ".host." + host['name'],
                                     debug=self.config.get('log_level_host'))
 
@@ -267,7 +267,7 @@ class ovim():
         dhcp_params = self.config.get("dhcp_server")
         if dhcp_params:
             thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
-                                    db=self.config["db"], db_lock=self.config["db_lock"],
+                                    db=self.config["db"],
                                     logger_name=self.logger_name + ".dhcp",
                                     debug=self.config.get('log_level_of'))
             thread.start()
@@ -283,31 +283,47 @@ class ovim():
 
         for net in content:
             net_type = net['type']
-            if (net_type == 'bridge_data' or net_type == 'bridge_man') and \
+            if net['status'] != "INACTIVE" and (net_type == 'bridge_data' or net_type == 'bridge_man') and \
                     net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
                 try:
-                    routes = yaml.safe_load(net.get('routes'))
-                    dns = yaml.safe_load(net.get('dns'))
-                    self.launch_dhcp_server(net.get('vlan'),
-                                            net.get('dhcp_first_ip'),
-                                            net.get('dhcp_last_ip'),
-                                            net.get('cidr'),
-                                            net.get('gateway_ip'),
-                                            dns,
-                                            routes)
-                    self.launch_link_bridge_to_ovs(net['vlan'], net.get('links'), net.get('routes'))
+                    config_routes = net.get('routes')
+                    if config_routes:
+                        routes = yaml.safe_load(config_routes)
+                    else:
+                        routes = None
+
+                    config_dns = net.get('dns')
+                    if config_dns:
+                        dns = yaml.safe_load(config_dns)
+                    else:
+                        dns = None
+
+                    links = net.get('links')
+                    if links:
+                        links = yaml.safe_load(net.get('links'))
+                    if net.get('enable_dhcp'):
+                        self.launch_dhcp_server(net.get('vlan'),
+                                                net.get('dhcp_first_ip'),
+                                                net.get('dhcp_last_ip'),
+                                                net.get('cidr'),
+                                                net.get('gateway_ip'),
+                                                dns,
+                                                routes)
+                    self.launch_link_bridge_to_ovs(net['vlan'], net.get('gateway_ip'), net.get('cidr'), links, routes)
+                    if net["status"] == "ERROR":
+                        self.db.update_rows("nets", UPDATE={"status": "ACTIVE", "last_error": None},
+                                            WHERE={"uuid": net["uuid"]})
                 except Exception as e:
                     self.logger.error("Fail at launching dhcp server for net_id='%s' net_name='%s': %s",
                                       net["uuid"], net["name"], str(e))
-                    self.db.update_rows("nets", {"status": "ERROR",
+                    self.db.update_rows("nets", UPDATE={"status": "ERROR",
                                                  "last_error": "Fail at launching dhcp server: " + str(e)},
-                                        {"uuid": net["uuid"]})
+                                        WHERE={"uuid": net["uuid"]})
 
     def _start_of_db_tasks(self):
         """
         Start ofc task for existing ofcs in database
         :param db_of:
-        :param db_lock:
         :return:
         """
         ofcs = self.get_of_controllers()
@@ -435,7 +451,6 @@ class ovim():
         ofc_net_same_vlan = False
 
         thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.config["db"],
-                                     db_lock=self.config["db_lock"],
                                      pmp_with_same_vlan=ofc_net_same_vlan,
                                      logger_name=self.logger_name + ".ofc." + ofc_uuid,
                                      debug=self.config.get('log_level_of'))
@@ -483,7 +498,7 @@ class ovim():
 
         return content
 
-    def show_network(self, network_id, db_filter={}):
+    def show_network(self, network_id, db_filter={}, skip_on_not_found=False):
         """
         Get network from DB by id
         :param network_id: net Id
@@ -500,7 +515,9 @@ class ovim():
         if result < 0:
             raise ovimException(str(content), -result)
         elif result == 0:
-            raise ovimException("show_network network '%s' not found" % network_id, -result)
+            if skip_on_not_found:
+                return
+            raise ovimException("show_network network '%s' not found" % network_id, HTTP_Not_Found)
         else:
             convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
             # get ports from DB
@@ -508,8 +525,12 @@ class ovim():
                                               WHERE={'net_id': network_id}, LIMIT=100)
             if len(ports) > 0:
                 content[0]['ports'] = ports
-
             convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
+
+            result, flows = self.db.get_table(FROM='of_flows', SELECT=('priority', 'vlan_id', 'ingress_port', 'src_mac', 'dst_mac', 'actions'),
+                                              WHERE={'net_id': network_id}, LIMIT=100)
+            if len(flows) > 0:
+                content[0]['flows'] = flows
             return content[0]
 
     def new_network(self, network):
@@ -644,7 +665,7 @@ class ovim():
         network['vlan'] = net_vlan
         network['region'] = net_region
         dhcp_integrity = True
-        if 'enable_dhcp' in network and network['enable_dhcp']:
+        if network.get('enable_dhcp'):
             dhcp_integrity = self._check_dhcp_data_integrity(network)
         
         if network.get('links'):
@@ -655,8 +676,7 @@ class ovim():
             network['routes'] = yaml.safe_dump(network['routes'], default_flow_style=True, width=256)
 
         result, content = self.db.new_row('nets', network, True, True)
-
-        if result >= 0 and dhcp_integrity:
+        if result >= 0:  # and dhcp_integrity:
             if bridge_net:
                 bridge_net[3] = content
             if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
@@ -668,7 +688,8 @@ class ovim():
                     self.logger.debug("dhcp_server: add new net", content, content)
             return content
         else:
-            raise ovimException("Error posting network", HTTP_Internal_Server_Error)
+            raise ovimException("Error creating network: {}".format(content), -result)
+
 # TODO kei change update->edit
 
     def edit_network(self, network_id, network):
@@ -762,12 +783,15 @@ class ovim():
         else:
             raise ovimException(content, -result)
 
-    def delete_network(self, network_id):
+    def delete_network(self, network_id, idempotent=True):
         """
         Delete network by network id
         :param network_id:  network id
         :return:
         """
+        net_data = self.show_network(network_id, skip_on_not_found=idempotent)
+        if not net_data:  # network does not exist
+            return
 
         # delete from the data base
         result, content = self.db.delete_row('nets', network_id)
@@ -781,7 +805,18 @@ class ovim():
                     break
             if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
                 self.config["dhcp_nets"].remove(network_id)
-            return content
+
+            if net_data.get('enable_dhcp'):
+                dhcp_path = self.config['ovs_controller_file_path']
+                dhcp_controller = self.get_dhcp_controller()
+                dhcp_controller.delete_dhcp_server(net_data['vlan'], network_id, dhcp_path)
+                dhcp_controller.delete_dhcp_port(net_data['vlan'], network_id, dhcp_path)
+                links = yaml.load(net_data.get('links'))
+                if links:
+                    links = yaml.load(net_data.get('links'))
+                    self.delete_link_bridge_to_ovs(net_data['vlan'], links)
+
+                return content
         else:
             raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
 
@@ -969,6 +1004,8 @@ class ovim():
             del port_data['compute_node']
 
         result, uuid = self.db.new_row('ports', port_data, True, True)
+        # set net status to BUILD
+        self.db.update_rows('nets', {"status": "BUILD"}, WHERE={'uuid': port_data['net_id']})
         if result > 0:
             try:
                 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
@@ -1025,7 +1062,7 @@ class ovim():
             self.logger.error(message)
             raise ovimException(message, HTTP_Internal_Server_Error)
 
-    def delete_port(self, port_id):
+    def delete_port(self, port_id, idempotent=False):
         # Look for the previous port data
         result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
         if result < 0:
@@ -1033,21 +1070,25 @@ class ovim():
         # delete from the data base
         result, content = self.db.delete_row('ports', port_id)
         if result == 0:
+            if idempotent:
+                return
             raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
         elif result < 0:
             raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
         # update network
-        network = ports[0].get('net_id', None)
-        if network:
+        net_id = ports[0].get('net_id', None)
+        if net_id:
             # change of net.
 
+            # set net status to BUILD
+            self.db.update_rows('nets', {"status": "BUILD"}, WHERE={'uuid': net_id})
             try:
-                self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
+                self.net_update_ofc_thread(net_id, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
             except ovimException as e:
-                raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
+                raise ovimException("Cannot insert a task for delete network '{}' {}".format(net_id, str(e)),
                                     HTTP_Internal_Server_Error)
             except Exception as e:
-                raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
+                raise ovimException("Cannot insert a task for delete network '{}' {}".format(net_id, str(e)),
                                     HTTP_Internal_Server_Error)
 
         return content
@@ -1333,6 +1374,8 @@ class ovim():
                 map['switch_dpid'] = switch_dpid
             if region:
                 map['region'] = region
+            if map.get("pci"):
+                map["pci"] = map["pci"].lower()
 
         for of_map in of_maps:
             result, uuid = self.db.new_row('of_port_mappings', of_map, True)
@@ -1390,7 +1433,7 @@ class ovim():
         dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
                                    password=self.config.get('ovs_controller_password'),
                                    keyfile=self.config.get('ovs_controller_keyfile'),
-                                   db=self.config["db"], db_lock=self.config["db_lock"], test=host_test_mode,
+                                   db=self.config["db"], test=host_test_mode,
                                    image_path=self.config['host_image_path'], version=self.config['version'],
                                    host_id='openvim_controller', develop_mode=host_develop_mode,
                                    develop_bridge_iface=bridge_ifaces,
@@ -1422,7 +1465,7 @@ class ovim():
         dhcp_path = self.config['ovs_controller_file_path']
 
         controller_host = self.get_dhcp_controller()
-        # TODO leo check if is need ti to create an ovim-vlan bridge, looks like not
+
         # controller_host.create_linux_bridge(vlan)
         controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
         dhcp_path = self.config['ovs_controller_file_path']