Merge branch 'v1.1'
[osm/openvim.git] / vim_db.py
index a458a8e..a024427 100644 (file)
--- a/vim_db.py
+++ b/vim_db.py
@@ -37,6 +37,7 @@ import uuid as myUuid
 import auxiliary_functions as af
 import json
 import logging
+from netaddr import IPNetwork, IPSet, IPRange, all_matching_cidrs
 
 HTTP_Bad_Request = 400
 HTTP_Unauthorized = 401 
@@ -173,7 +174,7 @@ class vim_db():
     def __get_used_net_vlan(self):
         #get used from database if needed
         try:
-            cmd = "SELECT vlan FROM nets WHERE vlan>='%s' and (type='ptp' or type='data') ORDER BY vlan LIMIT 25" % self.net_vlan_lastused
+            cmd = "SELECT vlan FROM nets WHERE vlan>='%s' ORDER BY vlan LIMIT 25" % self.net_vlan_lastused
             with self.con:
                 self.cur = self.con.cursor()
                 self.logger.debug(cmd)
@@ -1035,15 +1036,16 @@ class vim_db():
                 with self.con:
                     self.cur = self.con.cursor(mdb.cursors.DictCursor)
                     #get INSTANCE
-                    cmd = "SELECT uuid, name, description, progress, host_id, flavor_id, image_id, status, last_error, tenant_id, ram, vcpus, created_at \
-                        FROM instances WHERE uuid = '" +  str(instance_id) +"'"
+                    cmd = "SELECT uuid, name, description, progress, host_id, flavor_id, image_id, status, last_error, "\
+                        "tenant_id, ram, vcpus, created_at FROM instances WHERE uuid='{}'".format(instance_id)
                     self.logger.debug(cmd)
                     self.cur.execute(cmd)
                     if self.cur.rowcount == 0 : return 0, "instance '" + str(instance_id) +"'not found."
                     instance = self.cur.fetchone()
                     #get networks
-                    cmd = "SELECT uuid as iface_id, net_id, mac as mac_address, ip_address, name, Mbps as bandwidth, vpci, model \
-                        FROM ports WHERE type = 'instance:bridge' AND instance_id = '" + instance_id + "'"
+                    cmd = "SELECT uuid as iface_id, net_id, mac as mac_address, ip_address, name, Mbps as bandwidth, "\
+                        "vpci, model FROM ports WHERE (type='instance:bridge' or type='instance:ovs') AND "\
+                        "instance_id= '{}'".format(instance_id)
                     self.logger.debug(cmd)
                     self.cur.execute(cmd)
                     if self.cur.rowcount > 0 :
@@ -1113,8 +1115,9 @@ class vim_db():
                                 numa_dict['threads-source'] = thread_source
 
                         #get dedicated ports and SRIOV
-                        cmd = "SELECT port_id as iface_id, p.vlan as vlan, p.mac as mac_address, net_id, if(model='PF','yes',if(model='VF','no','yes:sriov')) as dedicated,\
-                            rp.Mbps as bandwidth, name, vpci, pci as source \
+                        cmd = "SELECT port_id as iface_id, p.vlan as vlan, p.mac as mac_address, net_id, if(model='PF',\
+                            'yes',if(model='VF','no','yes:sriov')) as dedicated, rp.Mbps as bandwidth, name, vpci, \
+                            pci as source \
                             FROM resources_port as rp join ports as p on port_id=uuid  WHERE p.instance_id = '%s' AND numa_id = '%s' and p.type='instance:data'" % (instance_id, numa_id) 
                         self.logger.debug(cmd)
                         self.cur.execute(cmd)
@@ -1395,15 +1398,28 @@ class vim_db():
                     #insert resources
                     nb_bridge_ifaces = nb_cores = nb_ifaces = nb_numas = 0
                     #insert bridged_ifaces
+
                     for iface in bridgedifaces:
                         #generate and insert a iface uuid
+                        if 'enable_dhcp' in iface and iface['enable_dhcp']:
+                            dhcp_first_ip = iface["dhcp_first_ip"]
+                            del iface["dhcp_first_ip"]
+                            dhcp_last_ip = iface["dhcp_last_ip"]
+                            del iface["dhcp_last_ip"]
+                            dhcp_cidr = iface["cidr"]
+                            del iface["cidr"]
+                            del iface["enable_dhcp"]
+                            used_dhcp_ips = self._get_dhcp_ip_used_list(iface["net_id"])
+                            iface["ip_address"] = self.get_free_ip_from_range(dhcp_first_ip, dhcp_last_ip,
+                                                                              dhcp_cidr, used_dhcp_ips)
+
                         iface['uuid'] = str(myUuid.uuid1()) # create_uuid
                         cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'ports')" % (iface['uuid'], uuid)
                         self.logger.debug(cmd)
                         self.cur.execute(cmd)
                         #insert iface
                         iface['instance_id'] = uuid
-                        iface['type'] = 'instance:bridge'
+                        iface['type'] = 'instance:bridge'
                         if 'name' not in iface: iface['name']="br"+str(nb_bridge_ifaces)
                         iface['Mbps']=iface.pop('bandwidth', None)
                         if 'mac_address' not in iface:
@@ -1503,7 +1519,59 @@ class vim_db():
                 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 get_free_ip_from_range(self, first_ip, last_ip, cidr, ip_used_list):
+        """
+        Calculate a free IP from a range given
+        :param first_ip: First dhcp ip range
+        :param last_ip: Last dhcp ip range
+        :param cidr: net cidr
+        :param ip_used_list: contain all used ips to avoid ip collisions
+        :return:
+        """
+
+        ip_tools = IPNetwork(cidr)
+        cidr_len = ip_tools.prefixlen
+        ips = IPNetwork(first_ip + '/' + str(cidr_len))
+        ip_used_list.append(str(ips[0])) # first ip
+        ip_used_list.append(str(ips[1])) # gw ip
+        ip_used_list.append(str(ips[-1])) # broadcast ip
+        for vm_ip in ips:
+            if str(vm_ip) not in ip_used_list:
+                return vm_ip
+
+        return None
+
+    def _get_dhcp_ip_used_list(self, net_id):
+        """
+        REtreive from DB all ips already used by the dhcp server for a given net
+        :param net_id:
+        :return:
+        """
+        WHERE={'type': 'instance:ovs', 'net_id': net_id}
+        for retry_ in range(0, 2):
+            cmd = ""
+            self.cur = self.con.cursor(mdb.cursors.DictCursor)
+            select_ = "SELECT uuid, ip_address FROM ports "
+
+            if WHERE is None or len(WHERE) == 0:
+                where_ = ""
+            else:
+                where_ = "WHERE " + " AND ".join(
+                    map(lambda x: str(x) + (" is Null" if WHERE[x] is None else "='" + str(WHERE[x]) + "'"),
+                        WHERE.keys()))
+            limit_ = "LIMIT 100"
+            cmd = " ".join((select_, where_, limit_))
+            self.logger.debug(cmd)
+            self.cur.execute(cmd)
+            ports = self.cur.fetchall()
+            ip_address_list = []
+            for port in ports:
+                ip_address_list.append(port['ip_address'])
+
+            return ip_address_list
+
+
+    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:
@@ -1523,7 +1591,14 @@ class vim_db():
                     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, vlan, ip_address, mac FROM ports WHERE instance_id='{}' AND net_id is not Null AND "\
+                            "type='instance:ovs'".format(instance_id)
+                    self.logger.debug(cmd)
+                    self.cur.execute(cmd)
+                    net_ovs_list += self.cur.fetchall()
 
                     #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 \
@@ -1634,8 +1709,8 @@ class vim_db():
             if net['tenant_id']==tenant_id and net['shared']=='false':
                 return -1, "needed admin privileges to attach to the net %s" % net_id
         #check types
-        if (net['type'] in ('p2p','data') and 'port_type' == 'instance:bridge') or \
-            (net['type'] in ('bridge_data','bridge_man') and 'port_type' != 'instance:bridge') :
+        if (net['type'] in ('p2p','data') and port_type != 'instance:data') or \
+            (net['type'] in ('bridge_data','bridge_man') and port_type not in ('instance:bridge', 'instance:ovs')):
             return -1, "can not attach a port of type %s into a net of type %s" % (port_type, net['type'])
         if net['type'] == 'ptp':
             #look how many