Openvim managment nets OVS refactoring 17/817/11
authorMirabal <leonardo.mirabal@altran.com>
Thu, 15 Dec 2016 10:51:19 +0000 (10:51 +0000)
committerMirabal <leonardo.mirabal@altran.com>
Wed, 18 Jan 2017 13:29:31 +0000 (13:29 +0000)
- OVS bridge creation and vxlan tunnel mesh (stp active) creation in openvim hosts.
- Add/remove/edit nets handling OVS ports, dhcp nets are supported to keep backwards compatibility.

Change-Id: I971067c77e50df292fa5cdd3bf926a222af349aa
Signed-off-by: Mirabal <leonardo.mirabal@altran.com>
host_thread.py
httpserver.py
openvimd.cfg
openvimd.py
vim_db.py
vim_schema.py

index 173634b..bb3a433 100644 (file)
@@ -25,9 +25,8 @@
 This is thread that interact with the host and the libvirt to manage VM
 One thread will be launched per host 
 '''
 This is thread that interact with the host and the libvirt to manage VM
 One thread will be launched per host 
 '''
-__author__="Pablo Montes, Alfonso Tierno"
-__date__ ="$10-jul-2014 12:07:15$"
-
+__author__ = "Pablo Montes, Alfonso Tierno, Leonardo Mirabal"
+__date__ = "$10-jul-2014 12:07:15$"
 
 import json
 import yaml
 
 import json
 import yaml
@@ -45,10 +44,12 @@ import random
 
 #TODO: insert a logging system
 
 
 #TODO: insert a logging system
 
-global lvirt_module
-lvirt_module=None  #libvirt module is charged only if not in test mode
+
+#global lvirt_module
+#lvirt_module=None  #libvirt module is charged only if not in test mode
 
 class host_thread(threading.Thread):
 
 class host_thread(threading.Thread):
+    lvirt_module = None
     def __init__(self, name, host, user, db, db_lock, test, image_path, host_id, version, develop_mode, develop_bridge_iface):
         '''Init a thread.
         Arguments: 
     def __init__(self, name, host, user, db, db_lock, test, image_path, host_id, version, develop_mode, develop_bridge_iface):
         '''Init a thread.
         Arguments: 
@@ -64,9 +65,11 @@ class host_thread(threading.Thread):
         self.db = db
         self.db_lock = db_lock
         self.test = test
         self.db = db
         self.db_lock = db_lock
         self.test = test
-        if not test:
+
+        if not test and not host_thread.lvirt_module:
             try:
             try:
-                lvirt_module = imp.find_module("libvirt")
+                module_info = imp.find_module("libvirt")
+                host_thread.lvirt_module = imp.load_module("libvirt", *module_info)
             except (IOError, ImportError) as e:
                 raise ImportError("Cannot import python-libvirt. Openvim not properly installed" +str(e))
 
             except (IOError, ImportError) as e:
                 raise ImportError("Cannot import python-libvirt. Openvim not properly installed" +str(e))
 
@@ -88,7 +91,8 @@ class host_thread(threading.Thread):
         
         self.queueLock = threading.Lock()
         self.taskQueue = Queue.Queue(2000)
         
         self.queueLock = threading.Lock()
         self.taskQueue = Queue.Queue(2000)
-        
+        self.ssh_conn = None
+
     def ssh_connect(self):
         try:
             #Connect SSH
     def ssh_connect(self):
         try:
             #Connect SSH
@@ -131,7 +135,7 @@ class host_thread(threading.Thread):
             except paramiko.ssh_exception.SSHException as e:
                 text = e.args[0]
                 print self.name, ": load_localinfo ssh Exception:", text
             except paramiko.ssh_exception.SSHException as e:
                 text = e.args[0]
                 print self.name, ": load_localinfo ssh Exception:", text
-            except lvirt_module.libvirtError as e:
+            except host_thread.lvirt_module.libvirtError as e:
                 text = e.get_error_message()
                 print self.name, ": load_localinfo libvirt Exception:", text
             except yaml.YAMLError as exc:
                 text = e.get_error_message()
                 print self.name, ": load_localinfo libvirt Exception:", text
             except yaml.YAMLError as exc:
@@ -176,7 +180,7 @@ class host_thread(threading.Thread):
         except paramiko.ssh_exception.SSHException as e:
             text = e.args[0]
             print self.name, ": load_hostinfo ssh Exception:", text
         except paramiko.ssh_exception.SSHException as e:
             text = e.args[0]
             print self.name, ": load_hostinfo ssh Exception:", text
-        except lvirt_module.libvirtError as e:
+        except host_thread.lvirt_module.libvirtError as e:
             text = e.get_error_message()
             print self.name, ": load_hostinfo libvirt Exception:", text
         except yaml.YAMLError as exc:
             text = e.get_error_message()
             print self.name, ": load_hostinfo libvirt Exception:", text
         except yaml.YAMLError as exc:
@@ -217,7 +221,7 @@ class host_thread(threading.Thread):
                 print self.name, ": save_localinfo ssh Exception:", text
                 if "SSH session not active" in text:
                     self.ssh_connect()
                 print self.name, ": save_localinfo ssh Exception:", text
                 if "SSH session not active" in text:
                     self.ssh_connect()
-            except lvirt_module.libvirtError as e:
+            except host_thread.lvirt_module.libvirtError as e:
                 text = e.get_error_message()
                 print self.name, ": save_localinfo libvirt Exception:", text
             except yaml.YAMLError as exc:
                 text = e.get_error_message()
                 print self.name, ": save_localinfo libvirt Exception:", text
             except yaml.YAMLError as exc:
@@ -331,6 +335,27 @@ class host_thread(threading.Thread):
                 elif task[0] == 'restore-iface':
                     print self.name, ": processing task restore-iface %s mac=%s" % (task[1], task[2])
                     self.restore_iface(task[1], task[2])
                 elif task[0] == 'restore-iface':
                     print self.name, ": processing task restore-iface %s mac=%s" % (task[1], task[2])
                     self.restore_iface(task[1], task[2])
+                elif task[0] == 'new-ovsbridge':
+                    print self.name, ": Creating compute OVS bridge"
+                    self.create_ovs_bridge()
+                    break
+                elif task[0] == 'new-vxlan':
+                    print self.name, ": Creating vxlan tunnel=" + task[1] + ", remote ip=" + task[2]
+                    self.create_ovs_vxlan_tunnel(task[1], task[2])
+                    break
+                elif task[0] == 'del-ovsbridge':
+                    print self.name, ": Deleting OVS bridge"
+                    self.delete_ovs_bridge()
+                    break
+                elif task[0] == 'del-vxlan':
+                    print self.name, ": Deleting vxlan " + task[1] + " tunnel"
+                    self.delete_ovs_vxlan_tunnel(task[1])
+                    break
+                elif task[0] == 'create-ovs-bridge-port':
+                    print self.name, ": Adding port ovim-" + task[1] + " to OVS bridge"
+                    self.create_ovs_bridge_port(task[1])
+                elif task[0] == 'del-ovs-port':
+                    self.delete_bridge_port_attached_to_ovs(task[1], task[2])
                 else:
                     print self.name, ": unknown task", task
                 
                 else:
                     print self.name, ": unknown task", task
                 
@@ -589,6 +614,10 @@ class host_thread(threading.Thread):
                         self.tab() + "<alias name='net" + str(net_nb)+ "'/>"
                 elif model==None:
                     model = "virtio"
                         self.tab() + "<alias name='net" + str(net_nb)+ "'/>"
                 elif model==None:
                     model = "virtio"
+            elif content[0]['provider'][0:3] == "OVS":
+                vlan = content[0]['provider'].replace('OVS:', '')
+                text += self.tab() + "<interface type='bridge'>" + \
+                        self.inc_tab() + "<source bridge='ovim-" + vlan + "'/>"
             else:
                 return -1, 'Unknown Bridge net provider ' + content[0]['provider']
             if model!=None:
             else:
                 return -1, 'Unknown Bridge net provider ' + content[0]['provider']
             if model!=None:
@@ -677,7 +706,182 @@ class host_thread(threading.Thread):
         """Decrement and return indentation according to xml_level"""
         self.xml_level -= 1
         return self.tab()
         """Decrement and return indentation according to xml_level"""
         self.xml_level -= 1
         return self.tab()
-    
+
+    def create_ovs_bridge(self):
+        """
+        Create a bridge in compute OVS to allocate VMs
+        :return: True if success
+        """
+        command = 'sudo ovs-vsctl --may-exist add-br br-int -- set Bridge br-int stp_enable=true'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_port_to_ovs_bridge(self, vlan, net_uuid):
+        """
+        Delete linux bridge port attched to a OVS bridge, if port is not free the port is not removed
+        :param vlan: vlan port id
+        :param net_uuid: network id
+        :return:
+        """
+
+        command = 'sudo ovs-vsctl del-port br-int ovim-' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def is_port_free(self, vlan, net_uuid):
+        """
+        Check if por is free before delete from the compute.
+        :param vlan: vlan port id
+        :param net_uuid: network id
+        :return: True if is not free
+        """
+        self.db_lock.acquire()
+        result, content = self.db.get_table(
+            FROM='ports as p join instances as i on p.instance_id=i.uuid',
+            WHERE={"i.host_id":  self.host_id, 'p.type': 'instance:bridge', 'p.net_id':  net_uuid}
+        )
+        self.db_lock.release()
+
+        if content > 0:
+            return False
+        else:
+            return True
+
+    def add_port_to_ovs_bridge(self, vlan):
+        """
+        Add a bridge linux as a port to a OVS bridge and set a vlan for an specific linux bridge
+        :param vlan: vlan port id
+        :return:
+        """
+        command = 'sudo ovs-vsctl add-port br-int ovim-' + vlan + ' tag=' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_bridge_port_attached_to_ovs(self, vlan, net_uuid):
+        """
+        Delete from an existing OVS bridge a linux bridge port attached and the linux bridge itself.
+        :param vlan:
+        :param net_uuid:
+        :return: True if success
+        """
+        if not self.is_port_free(vlan, net_uuid):
+            return True
+        self.delete_port_to_ovs_bridge(vlan, net_uuid)
+        self.delete_linux_bridge(vlan)
+        return  True
+
+    def delete_linux_bridge(self, vlan):
+        """
+        Delete a linux bridge in a scpecific compute.
+        :param vlan: vlan port id
+        :return: True if success
+        """
+        command = 'sudo ifconfig ovim-' + vlan + ' down &&  sudo brctl delbr ovim-' + vlan
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def create_ovs_bridge_port(self, vlan):
+        """
+        Generate a linux bridge and attache the port to a OVS bridge
+        :param vlan: vlan port id
+        :return:
+        """
+        self.create_linux_bridge(vlan)
+        self.add_port_to_ovs_bridge(vlan)
+
+    def create_linux_bridge(self, vlan):
+        """
+        Create a linux bridge with STP active
+        :param vlan: netowrk vlan id
+        :return:
+        """
+        command = 'sudo brctl addbr ovim-' + vlan + ' && sudo ifconfig ovim-' + vlan + ' up'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) != 0:
+            return False
+
+        command = 'sudo brctl stp ovim-' + vlan + ' on'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def create_ovs_vxlan_tunnel(self, vxlan_interface, remote_ip):
+        """
+        Create a vlxn tunnel between to computes with an OVS installed. STP is also active at port level
+        :param vxlan_interface: vlxan inteface name.
+        :param remote_ip: tunnel endpoint remote compute ip.
+        :return:
+        """
+        command = 'sudo ovs-vsctl add-port br-int ' + vxlan_interface + \
+                  ' -- set Interface ' + vxlan_interface + '  type=vxlan options:remote_ip=' + remote_ip + \
+                  ' -- set Port ' + vxlan_interface + ' other_config:stp-path-cost=10'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        print content
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_ovs_vxlan_tunnel(self, vxlan_interface):
+        """
+        Delete a vlxan tunnel  port from a OVS brdige.
+        :param vxlan_interface: vlxan name to be delete it.
+        :return: True if success.
+        """
+        command = 'sudo ovs-vsctl del-port br-int ' + vxlan_interface
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        print content
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
+    def delete_ovs_bridge(self):
+        """
+        Delete a OVS bridge from  a compute.
+        :return: True if success
+        """
+        command = 'sudo ovs-vsctl del-br br-int'
+        print self.name, ': command:', command
+        (_, stdout, _) = self.ssh_conn.exec_command(command)
+        content = stdout.read()
+        if len(content) == 0:
+            return True
+        else:
+            return False
+
     def get_file_info(self, path):
         command = 'ls -lL --time-style=+%Y-%m-%dT%H:%M:%S ' + path
         print self.name, ': command:', command
     def get_file_info(self, path):
         command = 'ls -lL --time-style=+%Y-%m-%dT%H:%M:%S ' + path
         print self.name, ': command:', command
@@ -944,7 +1148,7 @@ class host_thread(threading.Thread):
                 print self.name, ": create xml server error:", xml
                 return -2, xml
             print self.name, ": create xml:", xml
                 print self.name, ": create xml server error:", xml
                 return -2, xml
             print self.name, ": create xml:", xml
-            atribute = lvirt_module.VIR_DOMAIN_START_PAUSED if paused == "yes" else 0
+            atribute = host_thread.lvirt_module.VIR_DOMAIN_START_PAUSED if paused == "yes" else 0
         #4 Start the domain
             if not rebuild: #ensures that any pending destroying server is done
                 self.server_forceoff(True)
         #4 Start the domain
             if not rebuild: #ensures that any pending destroying server is done
                 self.server_forceoff(True)
@@ -959,7 +1163,7 @@ class host_thread(threading.Thread):
             print self.name, ": launch_server(%s) ssh Exception: %s" %(server_id, text)
             if "SSH session not active" in text:
                 self.ssh_connect()
             print self.name, ": launch_server(%s) ssh Exception: %s" %(server_id, text)
             if "SSH session not active" in text:
                 self.ssh_connect()
-        except lvirt_module.libvirtError as e:
+        except host_thread.lvirt_module.libvirtError as e:
             text = e.get_error_message()
             print self.name, ": launch_server(%s) libvirt Exception: %s"  %(server_id, text)
         except Exception as e:
             text = e.get_error_message()
             print self.name, ": launch_server(%s) libvirt Exception: %s"  %(server_id, text)
         except Exception as e:
@@ -982,26 +1186,26 @@ class host_thread(threading.Thread):
             return            
         
         try:
             return            
         
         try:
-            conn = lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
+            conn = host_thread.lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
             domains=  conn.listAllDomains() 
             domain_dict={}
             for domain in domains:
                 uuid = domain.UUIDString() ;
                 libvirt_status = domain.state()
                 #print libvirt_status
             domains=  conn.listAllDomains() 
             domain_dict={}
             for domain in domains:
                 uuid = domain.UUIDString() ;
                 libvirt_status = domain.state()
                 #print libvirt_status
-                if libvirt_status[0] == lvirt_module.VIR_DOMAIN_RUNNING or libvirt_status[0] == lvirt_module.VIR_DOMAIN_SHUTDOWN:
+                if libvirt_status[0] == host_thread.lvirt_module.VIR_DOMAIN_RUNNING or libvirt_status[0] == host_thread.lvirt_module.VIR_DOMAIN_SHUTDOWN:
                     new_status = "ACTIVE"
                     new_status = "ACTIVE"
-                elif libvirt_status[0] == lvirt_module.VIR_DOMAIN_PAUSED:
+                elif libvirt_status[0] == host_thread.lvirt_module.VIR_DOMAIN_PAUSED:
                     new_status = "PAUSED"
                     new_status = "PAUSED"
-                elif libvirt_status[0] == lvirt_module.VIR_DOMAIN_SHUTOFF:
+                elif libvirt_status[0] == host_thread.lvirt_module.VIR_DOMAIN_SHUTOFF:
                     new_status = "INACTIVE"
                     new_status = "INACTIVE"
-                elif libvirt_status[0] == lvirt_module.VIR_DOMAIN_CRASHED:
+                elif libvirt_status[0] == host_thread.lvirt_module.VIR_DOMAIN_CRASHED:
                     new_status = "ERROR"
                 else:
                     new_status = None
                 domain_dict[uuid] = new_status
                     new_status = "ERROR"
                 else:
                     new_status = None
                 domain_dict[uuid] = new_status
-            conn.close
-        except lvirt_module.libvirtError as e:
+            conn.close()
+        except host_thread.lvirt_module.libvirtError as e:
             print self.name, ": get_state() Exception '", e.get_error_message()
             return
 
             print self.name, ": get_state() Exception '", e.get_error_message()
             return
 
@@ -1067,10 +1271,10 @@ class host_thread(threading.Thread):
                 self.create_image(None, req)
         else:
             try:
                 self.create_image(None, req)
         else:
             try:
-                conn = lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
+                conn = host_thread.lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
                 try:
                     dom = conn.lookupByUUIDString(server_id)
                 try:
                     dom = conn.lookupByUUIDString(server_id)
-                except lvirt_module.libvirtError as e:
+                except host_thread.lvirt_module.libvirtError as e:
                     text = e.get_error_message()
                     if 'LookupByUUIDString' in text or 'Domain not found' in text or 'No existe un dominio coincidente' in text:
                         dom = None
                     text = e.get_error_message()
                     if 'LookupByUUIDString' in text or 'Domain not found' in text or 'No existe un dominio coincidente' in text:
                         dom = None
@@ -1152,8 +1356,8 @@ class host_thread(threading.Thread):
                     else:
                         new_status = 'ACTIVE'
                 elif 'start' in req['action']:
                     else:
                         new_status = 'ACTIVE'
                 elif 'start' in req['action']:
-                    #La instancia está sólo en la base de datos pero no en la libvirt. es necesario crearla
-                    rebuild = True if req['action']['start']=='rebuild'  else False
+                    # The instance is only create in DB but not yet at libvirt domain, needs to be create
+                    rebuild = True if req['action']['start'] == 'rebuild'  else False
                     r = self.launch_server(conn, req, rebuild, dom)
                     if r[0] <0:
                         new_status = 'ERROR'
                     r = self.launch_server(conn, req, rebuild, dom)
                     if r[0] <0:
                         new_status = 'ERROR'
@@ -1196,8 +1400,8 @@ class host_thread(threading.Thread):
                         
         
                 conn.close()    
                         
         
                 conn.close()    
-            except lvirt_module.libvirtError as e:
-                if conn is not None: conn.close
+            except host_thread.lvirt_module.libvirtError as e:
+                if conn is not None: conn.close()
                 text = e.get_error_message()
                 new_status = "ERROR"
                 last_error = text
                 text = e.get_error_message()
                 new_status = "ERROR"
                 last_error = text
@@ -1253,7 +1457,7 @@ class host_thread(threading.Thread):
             return 0, None
         try:
             if not lib_conn:
             return 0, None
         try:
             if not lib_conn:
-                conn = lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
+                conn = host_thread.lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
             else:
                 conn = lib_conn
                 
             else:
                 conn = lib_conn
                 
@@ -1264,13 +1468,13 @@ class host_thread(threading.Thread):
             iface.destroy()
             iface.create()
             print self.name, ": restore_iface '%s' %s" % (name, mac)
             iface.destroy()
             iface.create()
             print self.name, ": restore_iface '%s' %s" % (name, mac)
-        except lvirt_module.libvirtError as e:
+        except host_thread.lvirt_module.libvirtError as e:
             error_text = e.get_error_message()
             print self.name, ": restore_iface '%s' '%s' libvirt exception: %s" %(name, mac, error_text) 
             ret=-1
         finally:
             if lib_conn is None and conn is not None:
             error_text = e.get_error_message()
             print self.name, ": restore_iface '%s' '%s' libvirt exception: %s" %(name, mac, error_text) 
             ret=-1
         finally:
             if lib_conn is None and conn is not None:
-                conn.close
+                conn.close()
         return ret, error_text
 
         
         return ret, error_text
 
         
@@ -1354,12 +1558,12 @@ class host_thread(threading.Thread):
             
             try:
                 conn=None
             
             try:
                 conn=None
-                conn = lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
+                conn = host_thread.lvirt_module.open("qemu+ssh://"+self.user+"@"+self.host+"/system")
                 dom = conn.lookupByUUIDString(port["instance_id"])
                 if old_net:
                     text="\n".join(xml)
                     print self.name, ": edit_iface detaching SRIOV interface", text
                 dom = conn.lookupByUUIDString(port["instance_id"])
                 if old_net:
                     text="\n".join(xml)
                     print self.name, ": edit_iface detaching SRIOV interface", text
-                    dom.detachDeviceFlags(text, flags=lvirt_module.VIR_DOMAIN_AFFECT_LIVE)
+                    dom.detachDeviceFlags(text, flags=host_thread.lvirt_module.VIR_DOMAIN_AFFECT_LIVE)
                 if new_net:
                     xml[-1] ="  <vlan>   <tag id='" + str(port['vlan']) + "'/>   </vlan>"
                     self.xml_level = 1
                 if new_net:
                     xml[-1] ="  <vlan>   <tag id='" + str(port['vlan']) + "'/>   </vlan>"
                     self.xml_level = 1
@@ -1367,14 +1571,14 @@ class host_thread(threading.Thread):
                     xml.append('</interface>')                
                     text="\n".join(xml)
                     print self.name, ": edit_iface attaching SRIOV interface", text
                     xml.append('</interface>')                
                     text="\n".join(xml)
                     print self.name, ": edit_iface attaching SRIOV interface", text
-                    dom.attachDeviceFlags(text, flags=lvirt_module.VIR_DOMAIN_AFFECT_LIVE)
+                    dom.attachDeviceFlags(text, flags=host_thread.lvirt_module.VIR_DOMAIN_AFFECT_LIVE)
                     
                     
-            except lvirt_module.libvirtError as e:
+            except host_thread.lvirt_module.libvirtError as e:
                 text = e.get_error_message()
                 print self.name, ": edit_iface(",port["instance_id"],") libvirt exception:", text 
                 
             finally:
                 text = e.get_error_message()
                 print self.name, ": edit_iface(",port["instance_id"],") libvirt exception:", text 
                 
             finally:
-                if conn is not None: conn.close
+                if conn is not None: conn.close()
                             
                               
 def create_server(server, db, db_lock, only_of_ports):
                             
                               
 def create_server(server, db, db_lock, only_of_ports):
index ecf878c..e5ce5cd 100644 (file)
@@ -26,8 +26,8 @@ This is the thread for the http server North API.
 Two thread will be launched, with normal and administrative permissions.
 '''
 
 Two thread will be launched, with normal and administrative permissions.
 '''
 
-__author__="Alfonso Tierno"
-__date__ ="$10-jul-2014 12:07:15$"
+__author__ = "Alfonso Tierno, Leonardo Mirabal"
+__date__ = "$10-jul-2014 12:07:15$"
 
 import bottle
 import urlparse
 
 import bottle
 import urlparse
@@ -492,8 +492,12 @@ def enable_cors():
 
 @bottle.route(url_base + '/hosts', method='GET')
 def http_get_hosts():
 
 @bottle.route(url_base + '/hosts', method='GET')
 def http_get_hosts():
-    select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_host,
-            ('id','name','description','status','admin_state_up') )
+    return format_out(get_hosts())
+
+
+def get_hosts():
+    select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_host,
+                                                  ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
     
     myself = config_dic['http_threads'][ threading.current_thread().name ]
     result, content = myself.db.get_table(FROM='hosts', SELECT=select_, WHERE=where_, LIMIT=limit_)
     
     myself = config_dic['http_threads'][ threading.current_thread().name ]
     result, content = myself.db.get_table(FROM='hosts', SELECT=select_, WHERE=where_, LIMIT=limit_)
@@ -506,7 +510,7 @@ def http_get_hosts():
         for row in content:
             row['links'] = ( {'href': myself.url_preffix + '/hosts/' + str(row['id']), 'rel': 'bookmark'}, )
         data={'hosts' : content}
         for row in content:
             row['links'] = ( {'href': myself.url_preffix + '/hosts/' + str(row['id']), 'rel': 'bookmark'}, )
         data={'hosts' : content}
-        return format_out(data)
+        return data
 
 @bottle.route(url_base + '/hosts/<host_id>', method='GET')
 def http_get_host_id(host_id):
 
 @bottle.route(url_base + '/hosts/<host_id>', method='GET')
 def http_get_host_id(host_id):
@@ -628,6 +632,12 @@ def http_post_hosts():
             thread.start()
             config_dic['host_threads'][ content['uuid'] ] = thread
 
             thread.start()
             config_dic['host_threads'][ content['uuid'] ] = thread
 
+            if config_dic['network_type'] == 'ovs':
+                # create bridge
+                config_dic['host_threads'][content['uuid']].insert_task("new-ovsbridge")
+                # check if more host exist
+                create_vxlan_mesh(content['uuid'])
+
         #return host data
         change_keys_http2db(content, http2db_host, reverse=True)
         if len(warning_text)>0:
         #return host data
         change_keys_http2db(content, http2db_host, reverse=True)
         if len(warning_text)>0:
@@ -638,6 +648,50 @@ def http_post_hosts():
         bottle.abort(HTTP_Bad_Request, content)
         return
 
         bottle.abort(HTTP_Bad_Request, content)
         return
 
+
+def create_vxlan_mesh(host_id):
+    existing_hosts = get_hosts()
+    if len(existing_hosts['hosts']) > 1:
+        computes_available = existing_hosts['hosts']
+
+        # TUNEL openvim controller
+
+        for count, compute_owner in enumerate(computes_available):
+            for compute in computes_available:
+                if compute_owner['id'] == compute['id']:
+                    pass
+                else:
+                    vxlan_interface_name = get_vxlan_interface(compute_owner['id'][:8])
+                    config_dic['host_threads'][compute['id']].insert_task("new-vxlan",
+                                                                          vxlan_interface_name,
+                                                                          compute_owner['ip_name'])
+
+
+def delete_vxlan_mesh(host_id):
+    """
+    Create a task for remove a specific compute of the vlxan mesh
+    :param host_id: host id to be deleted.
+    """
+    existing_hosts = get_hosts()
+    computes_available = existing_hosts['hosts']
+    vxlan_interface_name = get_vxlan_interface(host_id[:8])
+
+    for compute in computes_available:
+        if host_id == compute['id']:
+            pass
+        else:
+            config_dic['host_threads'][compute['id']].insert_task("del-vxlan",vxlan_interface_name)
+
+
+def get_vxlan_interface(local_uuid):
+    """
+    Genearte a vxlan interface name
+    :param local_uuid: host id
+    :return: vlxan-8digits
+    """
+    return 'vxlan-' + local_uuid[:8]
+
+
 @bottle.route(url_base + '/hosts/<host_id>', method='PUT')
 def http_put_host_id(host_id):
     '''modify a host into the database. All resources are got and inserted'''
 @bottle.route(url_base + '/hosts/<host_id>', method='PUT')
 def http_put_host_id(host_id):
     '''modify a host into the database. All resources are got and inserted'''
@@ -659,12 +713,21 @@ def http_put_host_id(host_id):
         change_keys_http2db(content, http2db_host, reverse=True)
         data={'host' : content}
 
         change_keys_http2db(content, http2db_host, reverse=True)
         data={'host' : content}
 
+        if config_dic['network_type'] == 'ovs':
+            delete_vxlan_mesh(host_id)
+            config_dic['host_threads'][host_id].insert_task("del-ovsbridge")
+
         #reload thread
         config_dic['host_threads'][host_id].name = content.get('name',content['ip_name'])
         config_dic['host_threads'][host_id].user = content['user']
         config_dic['host_threads'][host_id].host = content['ip_name']
         config_dic['host_threads'][host_id].insert_task("reload")
 
         #reload thread
         config_dic['host_threads'][host_id].name = content.get('name',content['ip_name'])
         config_dic['host_threads'][host_id].user = content['user']
         config_dic['host_threads'][host_id].host = content['ip_name']
         config_dic['host_threads'][host_id].insert_task("reload")
 
+        if config_dic['network_type'] == 'ovs':
+            # create mesh with new host data
+            config_dic['host_threads'][host_id].insert_task("new-ovsbridge")
+            create_vxlan_mesh(host_id)
+
         #print data
         return format_out(data)
     else:
         #print data
         return format_out(data)
     else:
@@ -682,9 +745,13 @@ def http_delete_host_id(host_id):
     result, content = my.db.delete_row('hosts', host_id)
     if result == 0:
         bottle.abort(HTTP_Not_Found, content)
     result, content = my.db.delete_row('hosts', host_id)
     if result == 0:
         bottle.abort(HTTP_Not_Found, content)
-    elif result >0:
-        #terminate thread
+    elif result > 0:
+        if config_dic['network_type'] == 'ovs':
+            delete_vxlan_mesh(host_id)
+        # terminate thread
         if host_id in config_dic['host_threads']:
         if host_id in config_dic['host_threads']:
+            if config_dic['network_type'] == 'ovs':
+                config_dic['host_threads'][host_id].insert_task("del-ovsbridge")
             config_dic['host_threads'][host_id].insert_task("exit")
         #return data
         data={'result' : content}
             config_dic['host_threads'][host_id].insert_task("exit")
         #return data
         data={'result' : content}
@@ -1413,7 +1480,11 @@ def http_post_server_id(tenant_id):
     #Start server
         
         server['uuid'] = new_instance
     #Start server
         
         server['uuid'] = new_instance
-        #server_start = server.get('start', 'yes')
+        # server_start = server.get('start', 'yes')
+        if config_dic['network_type'] == 'ovs':
+            server_net = get_network_id(c2[0]['net_id'])
+            vlan = str(server_net['network']['provider:vlan'])
+            config_dic['host_threads'][server['host_id']].insert_task("create-ovs-bridge-port", vlan)
         if server_start != 'no':
             server['paused'] = True if server_start == 'paused' else False 
             server['action'] = {"start":None}
         if server_start != 'no':
             server['paused'] = True if server_start == 'paused' else False 
             server['action'] = {"start":None}
@@ -1552,9 +1623,11 @@ def http_server_action(server_id, tenant_id, action):
     if new_status != None and new_status == 'DELETING':
         nets=[]
         ports_to_free=[]
     if new_status != None and new_status == 'DELETING':
         nets=[]
         ports_to_free=[]
-        #look for dhcp ip address 
+
+        net_ovs_list = []
+        #look for dhcp ip address
         r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": server_id})
         r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": server_id})
-        r,c = my.db.delete_instance(server_id, tenant_id, nets, ports_to_free, "requested by http")
+        r, c = my.db.delete_instance(server_id, tenant_id, nets, ports_to_free, net_ovs_list, "requested by http")
         for port in ports_to_free:
             r1,c1 = config_dic['host_threads'][ server['host_id'] ].insert_task( 'restore-iface',*port )
             if r1 < 0:
         for port in ports_to_free:
             r1,c1 = config_dic['host_threads'][ server['host_id'] ].insert_task( 'restore-iface',*port )
             if r1 < 0:
@@ -1573,7 +1646,12 @@ def http_server_action(server_id, tenant_id, action):
                     #print "dhcp insert del task"
                     if r < 0:
                         print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
                     #print "dhcp insert del task"
                     if r < 0:
                         print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
-
+        if config_dic['network_type'] == 'ovs':
+            # delete ovs-port and linux bridge
+            for net in net_ovs_list:
+                server_net = get_network_id(net)
+                vlan = str(server_net['network']['provider:vlan'])
+                config_dic['host_threads'][server['host_id']].insert_task('del-ovs-port', vlan, server_net['network']['id'])
     return format_out(data)
 
 
     return format_out(data)
 
 
@@ -1634,8 +1712,12 @@ def http_get_networks():
 
 @bottle.route(url_base + '/networks/<network_id>', method='GET')
 def http_get_network_id(network_id):
 
 @bottle.route(url_base + '/networks/<network_id>', method='GET')
 def http_get_network_id(network_id):
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #obtain data
+        data = get_network_id(network_id)
+        return format_out(data)
+
+def get_network_id(network_id):
+    my = config_dic['http_threads'][threading.current_thread().name]
+    # obtain data
     where_ = bottle.request.query
     where_['uuid'] = network_id
     result, content = my.db.get_table(FROM='nets', WHERE=where_, LIMIT=100)
     where_ = bottle.request.query
     where_['uuid'] = network_id
     result, content = my.db.get_table(FROM='nets', WHERE=where_, LIMIT=100)
@@ -1656,7 +1738,7 @@ def http_get_network_id(network_id):
             content[0]['ports'] = ports
         delete_nulls(content[0])      
         data={'network' : content[0]}
             content[0]['ports'] = ports
         delete_nulls(content[0])      
         data={'network' : content[0]}
-        return format_out(data)
+        return data
 
 @bottle.route(url_base + '/networks', method='POST')
 def http_post_networks():
 
 @bottle.route(url_base + '/networks', method='POST')
 def http_post_networks():
@@ -1739,8 +1821,7 @@ def http_post_networks():
         net_type='bridge_man' 
         
     if net_provider != None:
         net_type='bridge_man' 
         
     if net_provider != None:
-        if net_provider[:7]=='bridge:':
-            #check it is one of the pre-provisioned bridges
+        if net_provider[:7] == 'bridge:':
             bridge_net_name = net_provider[7:]
             for brnet in config_dic['bridge_nets']:
                 if brnet[0]==bridge_net_name: # free
             bridge_net_name = net_provider[7:]
             for brnet in config_dic['bridge_nets']:
                 if brnet[0]==bridge_net_name: # free
@@ -1753,7 +1834,7 @@ def http_post_networks():
 #            if bridge_net==None:     
 #                bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
 #                return
 #            if bridge_net==None:     
 #                bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
 #                return
-    elif net_type=='bridge_data' or net_type=='bridge_man':
+    elif config_dic['network_type'] == 'bridge' and net_type =='bridge_data' or net_type ==  'bridge_man' :
         #look for a free precreated nets
         for brnet in config_dic['bridge_nets']:
             if brnet[3]==None: # free
         #look for a free precreated nets
         for brnet in config_dic['bridge_nets']:
             if brnet[3]==None: # free
@@ -1772,12 +1853,16 @@ def http_post_networks():
             print "using net", bridge_net
             net_provider = "bridge:"+bridge_net[0]
             net_vlan = bridge_net[1]
             print "using net", bridge_net
             net_provider = "bridge:"+bridge_net[0]
             net_vlan = bridge_net[1]
-    if net_vlan==None and (net_type=="data" or net_type=="ptp"):
+    elif net_type == 'bridge_data' or net_type == 'bridge_man' and config_dic['network_type'] == 'ovs':
+        net_provider = 'OVS'
+    if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
         net_vlan = my.db.get_free_net_vlan()
         if net_vlan < 0:
             bottle.abort(HTTP_Internal_Server_Error, "Error getting an available vlan")
             return
         net_vlan = my.db.get_free_net_vlan()
         if net_vlan < 0:
             bottle.abort(HTTP_Internal_Server_Error, "Error getting an available vlan")
             return
-    
+    if config_dic['network_type'] == 'ovs':
+        net_provider = 'OVS' + ":" + str(net_vlan)
+
     network['provider'] = net_provider
     network['type']     = net_type
     network['vlan']     = net_vlan
     network['provider'] = net_provider
     network['type']     = net_type
     network['vlan']     = net_vlan
@@ -1787,7 +1872,7 @@ def http_post_networks():
         if bridge_net!=None:
             bridge_net[3] = content
         
         if bridge_net!=None:
             bridge_net[3] = content
         
-        if config_dic.get("dhcp_server"):
+        if config_dic.get("dhcp_server") and config_dic['network_type'] == 'bridge':
             if network["name"] in config_dic["dhcp_server"].get("nets", () ):
                 config_dic["dhcp_nets"].append(content)
                 print "dhcp_server: add new net", content
             if network["name"] in config_dic["dhcp_server"].get("nets", () ):
                 config_dic["dhcp_nets"].append(content)
                 print "dhcp_server: add new net", content
index 2d1aabb..bfa937a 100644 (file)
@@ -72,8 +72,14 @@ tenant_id: fc7b43b6-6bfa-11e4-84d2-5254006d6777   # Default tenant identifier fo
 network_vlan_range_start: 3000
 network_vlan_range_end:   4000
 
 network_vlan_range_start: 3000
 network_vlan_range_end:   4000
 
+# Network overlay supported by openvim compute, bsae on openvswicth or linux bridges to manage management networks for NFVs.
+# - ovs : Create a vlxan mesh between computes to handle the network overlay, no prepopulated bridges or networks is need it.
+# - bridge: Use pre-populate linux bridges by openvim configuration scripts.
+
+network_type : ovs
+
 #host bridge interfaces for networks
 #host bridge interfaces for networks
-# Openvim cannot create bridge networks automatically, in the same way as other CMS do.
+# Openvim cannot create bridge networks automatically, in the same way as other CMS do when network type is "bridge".
 # Bridge networks need to be pre-provisioned on each host and Openvim uses those pre-provisioned bridge networks.
 # Openvim assumes that the following bridge interfaces have been created on each host, appropriately associated to a physical port.
 # The following information needs to be provided:
 # Bridge networks need to be pre-provisioned on each host and Openvim uses those pre-provisioned bridge networks.
 # Openvim assumes that the following bridge interfaces have been created on each host, appropriately associated to a physical port.
 # The following information needs to be provided:
@@ -82,21 +88,21 @@ network_vlan_range_end:   4000
 #    - The speed of the physical port in Gbps, where that bridge interface was created
 # For instance, next example assumes that 10 bridges have been created on each host
 # using vlans 2001 to 2010, associated to a 1Gbps physical port 
 #    - The speed of the physical port in Gbps, where that bridge interface was created
 # For instance, next example assumes that 10 bridges have been created on each host
 # using vlans 2001 to 2010, associated to a 1Gbps physical port 
-bridge_ifaces:
-   #name:      [vlan, speed in Gbps]
-   virbrMan1:  [2001, 1]
-   virbrMan2:  [2002, 1]
-   virbrMan3:  [2003, 1]
-   virbrMan4:  [2004, 1]
-   virbrMan5:  [2005, 1]
-   virbrMan6:  [2006, 1]
-   virbrMan7:  [2007, 1]
-   virbrMan8:  [2008, 1]
-   virbrMan9:  [2009, 1]
-   virbrMan10: [2010, 1]
+#bridge_ifaces:
+#   #name:      [vlan, speed in Gbps]
+#   virbrMan1:  [2001, 1]
+#   virbrMan2:  [2002, 1]
+#   virbrMan3:  [2003, 1]
+#   virbrMan4:  [2004, 1]
+#   virbrMan5:  [2005, 1]
+#   virbrMan6:  [2006, 1]
+#   virbrMan7:  [2007, 1]
+#   virbrMan8:  [2008, 1]
+#   virbrMan9:  [2009, 1]
+#   virbrMan10: [2010, 1]
 
 #Used only when 'mode' is at development'. Indicates which 'bridge_ifaces' is used for dataplane networks
 
 #Used only when 'mode' is at development'. Indicates which 'bridge_ifaces' is used for dataplane networks
-development_bridge: virbrMan10
+#development_bridge: virbrMan10
 
 #DHCP SERVER PARAMETERS. 
 #In case some of the previous 'bridge_ifaces' are connected to an EXTERNAL dhcp server, provide 
 
 #DHCP SERVER PARAMETERS. 
 #In case some of the previous 'bridge_ifaces' are connected to an EXTERNAL dhcp server, provide 
@@ -126,3 +132,5 @@ dhcp_server:
 log_level:       ERROR
 log_level_db:    DEBUG
 log_level_of:    DEBUG
 log_level:       ERROR
 log_level_db:    DEBUG
 log_level_of:    DEBUG
+
+
index 6513943..573ce27 100755 (executable)
@@ -69,6 +69,8 @@ def load_configuration(configuration_file):
                      'log_level': "DEBUG",
                      'log_level_db': "ERROR",
                      'log_level_of': 'ERROR',
                      'log_level': "DEBUG",
                      'log_level_db': "ERROR",
                      'log_level_of': 'ERROR',
+                     'bridge_ifaces': {},
+                     'network_type': 'ovs',
             }
     try:
         #First load configuration from configuration file
             }
     try:
         #First load configuration from configuration file
@@ -214,7 +216,8 @@ if __name__=="__main__":
             #allow backward compatibility of test_mode option
             if 'test_mode' in config_dic and config_dic['test_mode']==True:
                 config_dic['mode'] = 'test' 
             #allow backward compatibility of test_mode option
             if 'test_mode' in config_dic and config_dic['test_mode']==True:
                 config_dic['mode'] = 'test' 
-        if config_dic['mode'] == 'development' and ( 'development_bridge' not in config_dic or config_dic['development_bridge'] not in config_dic.get("bridge_ifaces",None) ):
+        if config_dic['mode'] == 'development' and config_dic['network_type'] == 'bridge' and \
+                ( 'development_bridge' not in config_dic or config_dic['development_bridge'] not in config_dic.get("bridge_ifaces",None) ):
             logger.error("'%s' is not a valid 'development_bridge', not one of the 'bridge_ifaces'", config_file)
             exit(-1)
             
             logger.error("'%s' is not a valid 'development_bridge', not one of the 'bridge_ifaces'", config_file)
             exit(-1)
             
index d88306d..dbb8ffe 100644 (file)
--- a/vim_db.py
+++ b/vim_db.py
@@ -1495,7 +1495,7 @@ class vim_db():
                 r,c = self.format_error(e, "new_instance", cmd)
                 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
 
                 r,c = self.format_error(e, "new_instance", cmd)
                 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
 
-    def delete_instance(self, instance_id, tenant_id, net_list, ports_to_free, logcause="requested by http"):
+    def delete_instance(self, instance_id, tenant_id, net_dataplane_list, ports_to_free, net_ovs_list, logcause="requested by http"):
         for retry_ in range(0,2):
             cmd=""
             try:
         for retry_ in range(0,2):
             cmd=""
             try:
@@ -1515,7 +1515,16 @@ class vim_db():
                     self.cur.execute(cmd)
                     net_list__ = self.cur.fetchall()
                     for net in net_list__:
                     self.cur.execute(cmd)
                     net_list__ = self.cur.fetchall()
                     for net in net_list__:
-                        net_list.append(net[0])
+                        net_dataplane_list.append(net[0])
+
+                    # get ovs manangement nets
+                    cmd = "SELECT DISTINCT net_id from ports WHERE instance_id = " \
+                          "'%s' AND net_id is not Null AND type='instance:bridge'" % instance_id
+                    self.logger.debug(cmd)
+                    self.cur.execute(cmd)
+                    net_list__ = self.cur.fetchall()
+                    for net in net_list__:
+                        net_ovs_list.append(net[0])
 
                     #get dataplane interfaces releases by this VM; both PF and VF with no other VF 
                     cmd="SELECT source_name, mac FROM (SELECT root_id, count(instance_id) as used FROM resources_port WHERE instance_id='%s' GROUP BY root_id ) AS A" % instance_id \
 
                     #get dataplane interfaces releases by this VM; both PF and VF with no other VF 
                     cmd="SELECT source_name, mac FROM (SELECT root_id, count(instance_id) as used FROM resources_port WHERE instance_id='%s' GROUP BY root_id ) AS A" % instance_id \
index 0a5929d..5bf555d 100644 (file)
@@ -117,12 +117,13 @@ config_schema = {
         "log_level": log_level_schema,
         "log_level_db": log_level_schema,
         "log_level_of": log_level_schema,
         "log_level": log_level_schema,
         "log_level_db": log_level_schema,
         "log_level_of": log_level_schema,
+        "network_type": {"type": "string", "enum": ["ovs", "bridge"]},
     },
     "patternProperties": {
         "of_*" : {"type": ["string", "integer", "boolean"]}
     },
     "required": ['db_host', 'db_user', 'db_passwd', 'db_name',
     },
     "patternProperties": {
         "of_*" : {"type": ["string", "integer", "boolean"]}
     },
     "required": ['db_host', 'db_user', 'db_passwd', 'db_name',
-            'of_controller_ip', 'of_controller_port', 'of_controller_dpid', 'bridge_ifaces', 'of_controller'],
+            'of_controller_ip', 'of_controller_port', 'of_controller_dpid', 'of_controller'],
     "additionalProperties": False
 }
 
     "additionalProperties": False
 }