Provide openflow information at network show
[osm/openvim.git] / osm_openvim / ovim.py
index c999049..6b95456 100755 (executable)
@@ -28,6 +28,7 @@ Two thread will be launched, with normal and administrative permissions.
 """
 
 import threading
+import yaml
 import vim_db
 import logging
 # import imp
@@ -42,9 +43,9 @@ import openflow_conn
 
 __author__ = "Alfonso Tierno, Leonardo Mirabal"
 __date__ = "$06-Feb-2017 12:07:15$"
-__version__ = "0.5.13-r529"
-version_date = "May 2017"
-database_version = 18      #needed database schema version
+__version__ = "0.5.25-r545"
+version_date = "Jul 2018"
+database_version = 23      #needed database schema version
 
 HTTP_Bad_Request =          400
 HTTP_Unauthorized =         401
@@ -142,7 +143,7 @@ class ovim():
 
             ips = IPNetwork(cidr)
             if "dhcp_first_ip" not in network:
-                network["dhcp_first_ip"] = str(ips[2])
+                network["dhcp_first_ip"] = str(ips[3])
             if "dhcp_last_ip" not in network:
                 network["dhcp_last_ip"] = str(ips[-2])
             if "gateway_ip" not in network:
@@ -191,6 +192,41 @@ class ovim():
         self.config["db_lock"] = threading.Lock()
 
         self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
+
+        # Create one thread for each host
+        host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
+        host_develop_mode = True if self.config['mode'] == 'development' else False
+        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', 'hypervisors', 'password', 'keyfile'),
+                                     FROM='hosts', WHERE={'status': 'ok'}) #Unikernels extension
+        if r < 0:
+            raise ovimException("Cannot get hosts from database {}".format(hosts))
+
+        self.config['host_threads'] = {}
+
+        for host in hosts:
+            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,
+                                    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'))
+
+            try:
+                thread.check_connectivity()
+            except Exception as e:
+                self.logger.critical('Error detected for compute = {} with ip = {}'
+                                     .format(host['name'], host['ip_name']))
+            thread.start()
+            self.config['host_threads'][host['uuid']] = thread
+
         # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
         # speed in Gbit/s
 
@@ -238,29 +274,7 @@ class ovim():
             thread.start()
             self.config['dhcp_thread'] = thread
 
-        # Create one thread for each host
-        host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
-        host_develop_mode = True if self.config['mode'] == 'development' else False
-        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'), FROM='hosts', WHERE={'status': 'ok'})
-        if r < 0:
-            raise ovimException("Cannot get hosts from database {}".format(hosts))
-
-        self.config['host_threads'] = {}
-        for host in hosts:
-            host['image_path'] = '/opt/VNF/images/openvim'
-            thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.config["db"],
-                                    db_lock=self.config["db_lock"], test=host_test_mode,
-                                    image_path=self.config['image_path'],
-                                    version=self.config['version'], host_id=host['uuid'],
-                                    develop_mode=host_develop_mode,
-                                    develop_bridge_iface=host_develop_bridge_iface,
-                                    logger_name=self.logger_name + ".host." + host['name'],
-                                    debug=self.config.get('log_level_host'))
-            thread.start()
-            self.config['host_threads'][host['uuid']] = thread
 
         # create ovs dhcp thread
         result, content = self.db.get_table(FROM='nets')
@@ -270,13 +284,42 @@ class ovim():
 
         for net in content:
             net_type = net['type']
-            if (net_type == 'bridge_data' or net_type == 'bridge_man') \
-                    and net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
-                    self.launch_dhcp_server(net['vlan'],
-                                            net['dhcp_first_ip'],
-                                            net['dhcp_last_ip'],
-                                            net['cidr'],
-                                            net['gateway_ip'])
+            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:
+                    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", UPDATE={"status": "ERROR",
+                                                 "last_error": "Fail at launching dhcp server: " + str(e)},
+                                        WHERE={"uuid": net["uuid"]})
 
     def _start_of_db_tasks(self):
         """
@@ -378,19 +421,12 @@ class ovim():
                 module = temp_dict['of_controller']
 
             if module not in ovim.of_module:
-                for base in ("", "osm_openvim.", "lib_osm_openvim."):
-                    try:
-                        pkg = __import__(base + module)
-                        if base:
-                            of_conn_module = getattr(pkg, module)
-                        else:
-                            of_conn_module = pkg
-                        ovim.of_module[module] = of_conn_module
-                        self.logger.debug("Module load from {}".format(base + module))
-                        break
-                    except Exception as e:
-                        self.logger.warning("Module {} not found {}".format(base + module, e))
-                else:
+                try:
+                    pkg = __import__("osm_openvim." + module)
+                    of_conn_module = getattr(pkg, module)
+                    ovim.of_module[module] = of_conn_module
+                    self.logger.debug("Module load from {}".format("osm_openvim." + module))
+                except Exception as e:
                     self.logger.error("Cannot open openflow controller module of type '%s'", module)
                     raise ovimException("Cannot open openflow controller of type module '{}'"
                                         "Revise it is installed".format(module),
@@ -439,9 +475,13 @@ class ovim():
         if 'dhcp_thread' in self.config:
             threads['dhcp'] = (self.config['dhcp_thread'])
 
-        for thread in threads.values():
+        for thread_id, thread in threads.items():
+            if thread_id == 'openvim_controller':
+                continue
             thread.insert_task("exit")
-        for thread in threads.values():
+        for thread_id, thread in threads.items():
+            if thread_id == 'openvim_controller':
+                continue
             thread.join()
 
     def get_networks(self, columns=None, db_filter={}, limit=None):
@@ -486,8 +526,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):
@@ -571,7 +615,7 @@ class ovim():
                 bridge_net_name = net_provider[7:]
                 for brnet in self.config['bridge_nets']:
                     if brnet[0] == bridge_net_name:  # free
-                        if not brnet[3]:
+                        if brnet[3]:
                             raise ovimException("invalid 'provider:physical', "
                                                 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
                         bridge_net = brnet
@@ -622,24 +666,31 @@ 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'):
+            network['links'] = yaml.safe_dump(network['links'], default_flow_style=True, width=256)
+        if network.get('dns'):
+            network['dns'] = yaml.safe_dump(network['dns'], default_flow_style=True, width=256)
+        if network.get('routes'):
+            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':
                 if network["name"] in self.config["dhcp_server"].get("nets", ()):
                     self.config["dhcp_nets"].append(content)
                     self.logger.debug("dhcp_server: add new net", content)
-                elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
+                elif bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
                     self.config["dhcp_nets"].append(content)
                     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):
@@ -739,6 +790,7 @@ class ovim():
         :param network_id:  network id
         :return:
         """
+        net_data = self.show_network(network_id)
 
         # delete from the data base
         result, content = self.db.delete_row('nets', network_id)
@@ -752,9 +804,20 @@ 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 %s" % network_id, HTTP_Internal_Server_Error)
+            raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
 
     def get_openflow_rules(self, network_id=None):
         """
@@ -843,7 +906,7 @@ class ovim():
             else:
                 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
 
-        if ofc_id in self.config['ofcs_thread']:
+        elif ofc_id in self.config['ofcs_thread']:
             conn = self.config['ofcs_thread'][ofc_id].OF_connector
         else:
             raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
@@ -1304,6 +1367,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)
@@ -1353,25 +1418,30 @@ class ovim():
 
         bridge_ifaces = []
         controller_ip = self.config['ovs_controller_ip']
-        ovs_controller_user = self.config['ovs_controller_user']
+        ovs_controller_user = self.config.get('ovs_controller_user')
 
         host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
         host_develop_mode = True if self.config['mode'] == 'development' else False
 
         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,
-                                   image_path=self.config['image_path'], version=self.config['version'],
+                                   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,
                                    logger_name=self.logger_name + ".host.controller",
                                    debug=self.config.get('log_level_host'))
-        dhcp_host.start()
+        dhcp_host.start()
         self.config['host_threads']['openvim_controller'] = dhcp_host
-        if not host_test_mode:
-            dhcp_host.ssh_connect()
+        try:
+            dhcp_host.check_connectivity()
+        except Exception as e:
+           pass
+
         return dhcp_host
 
-    def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
+    def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway, dns, routes):
         """
         Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
         :param vlan: vlan identifier
@@ -1388,9 +1458,49 @@ class ovim():
         dhcp_path = self.config['ovs_controller_file_path']
 
         controller_host = self.get_dhcp_controller()
-        controller_host.create_linux_bridge(vlan)
+
+        # controller_host.create_linux_bridge(vlan)
         controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
-        controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
+        dhcp_path = self.config['ovs_controller_file_path']
+        controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway, dns, routes)
+
+    def launch_link_bridge_to_ovs(self, vlan, gateway, dhcp_cidr, links=None, routes=None):
+        """
+        Launch creating of connections (veth) between user bridge (link) and OVS
+        :param vlan: 
+        :param gateway: 
+        :param links: 
+        :return: 
+        """
+
+        if links:
+            controller_host = self.get_dhcp_controller()
+            for link in links:
+                if 'iface' in link and 'nat' not in link:
+                        controller_host.create_link_bridge_to_ovs(vlan, link['iface'])
+                elif 'nat' in link:
+                    controller_host.create_qrouter_ovs_connection(vlan, gateway, dhcp_cidr)
+                    controller_host.create_qrouter_br_connection(vlan, dhcp_cidr, link)
+
+            if len(routes):
+                controller_host.add_ns_routes(vlan, routes)
+
+    def delete_link_bridge_to_ovs(self, vlan,  links=None):
+        """
+        Delete connections (veth) between user bridge (link) and OVS
+        :param vlan: 
+        :param links: 
+        :return: 
+        """
+        if links:
+            controller_host = self.get_dhcp_controller()
+
+            for link in links:
+                if 'iface' in link and 'nat' not in link:
+                    controller_host.remove_link_bridge_to_ovs(vlan, link['iface'])
+                elif 'nat' in link:
+                    controller_host.delete_qrouter_connection(vlan,  link['iface'])
+
 
 if __name__ == "__main__":