Class OVIM to perform the logic done by http receptor
[osm/openvim.git] / httpserver.py
index cf26544..777bd22 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.
 '''
 
-__author__ = "Alfonso Tierno, Leonardo Mirabal"
-__date__ = "$10-jul-2014 12:07:15$"
+__author__="Alfonso Tierno, Gerardo Garcia, Leonardo Mirabal"
+__date__ ="$10-jul-2014 12:07:15$"
 
 import bottle
 import urlparse
@@ -38,6 +38,7 @@ import datetime
 import hashlib
 import os
 import imp
+from netaddr import IPNetwork, IPAddress, all_matching_cidrs
 #import only if needed because not needed in test mode. To allow an easier installation   import RADclass
 from jsonschema import validate as js_v, exceptions as js_e
 import host_thread as ht
@@ -47,6 +48,8 @@ from vim_schema import host_new_schema, host_edit_schema, tenant_new_schema, \
     image_new_schema, image_update_schema, \
     server_new_schema, server_action_schema, network_new_schema, network_update_schema, \
     port_new_schema, port_update_schema
+import ovim
+import logging
 
 global my
 global url_base
@@ -74,6 +77,11 @@ def md5(fname):
             hash_md5.update(chunk)
     return hash_md5.hexdigest()
 
+def md5_string(fname):
+    hash_md5 = hashlib.md5()
+    hash_md5.update(fname)
+    return hash_md5.hexdigest()
+
 def check_extended(extended, allow_net_attach=False):
     '''Makes and extra checking of extended input that cannot be done using jsonschema
     Attributes: 
@@ -180,7 +188,7 @@ def delete_nulls(var):
 
 
 class httpserver(threading.Thread):
-    def __init__(self, db_conn, name="http", host='localhost', port=8080, admin=False, config_=None):
+    def __init__(self, ovim, name="http", host='localhost', port=8080, admin=False, config_=None):
         '''
         Creates a new thread to attend the http connections
         Attributes:
@@ -202,7 +210,8 @@ class httpserver(threading.Thread):
         threading.Thread.__init__(self)
         self.host = host
         self.port = port  
-        self.db = db_conn
+        self.db = ovim.db  #TODO OVIM remove
+        self.ovim = ovim
         self.admin = admin
         if name in config_dic:
             print "httpserver Warning!!! Onether thread with the same name", name
@@ -217,6 +226,7 @@ class httpserver(threading.Thread):
         #Ensure that when the main program exits the thread will also exit
         self.daemon = True      
         self.setDaemon(True)
+        self.logger = logging.getLogger("openvim.http")
          
     def run(self):
         bottle.run(host=self.host, port=self.port, debug=True) #quiet=True
@@ -331,40 +341,41 @@ def filter_query_string(qs, http2db, allowed):
         limit: limit dictated by user with the query string 'limit'. 100 by default
     abort if not permitted, using bottel.abort
     '''
-    where={}
-    limit=100
-    select=[]
+    where = {}
+    limit = 100
+    select = []
     if type(qs) is not bottle.FormsDict:
         print '!!!!!!!!!!!!!!invalid query string not a dictionary'
-        #bottle.abort(HTTP_Internal_Server_Error, "call programmer")
+        # bottle.abort(HTTP_Internal_Server_Error, "call programmer")
     else:
         for k in qs:
-            if k=='field':
+            if k == 'field':
                 select += qs.getall(k)
                 for v in select:
                     if v not in allowed:
-                        bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field="+v+"'")
-            elif k=='limit':
+                        bottle.abort(HTTP_Bad_Request, "Invalid query string at 'field=" + v + "'")
+            elif k == 'limit':
                 try:
-                    limit=int(qs[k])
+                    limit = int(qs[k])
                 except:
-                    bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit="+qs[k]+"'")
+                    bottle.abort(HTTP_Bad_Request, "Invalid query string at 'limit=" + qs[k] + "'")
             else:
                 if k not in allowed:
-                    bottle.abort(HTTP_Bad_Request, "Invalid query string at '"+k+"="+qs[k]+"'")
-                if qs[k]!="null":  where[k]=qs[k]
-                else: where[k]=None 
-    if len(select)==0: select += allowed
-    #change from http api to database naming
-    for i in range(0,len(select)):
-        k=select[i]
-        if k in http2db: 
+                    bottle.abort(HTTP_Bad_Request, "Invalid query string at '" + k + "=" + qs[k] + "'")
+                if qs[k] != "null":
+                    where[k] = qs[k]
+                else:
+                    where[k] = None
+    if len(select) == 0: select += allowed
+    # change from http api to database naming
+    for i in range(0, len(select)):
+        k = select[i]
+        if k in http2db:
             select[i] = http2db[k]
     change_keys_http2db(where, http2db)
-    #print "filter_query_string", select,where,limit
-    
-    return select,where,limit
+    # print "filter_query_string", select,where,limit
 
+    return select, where, limit
 
 def convert_bandwidth(data, reverse=False):
     '''Check the field bandwidth recursively and when found, it removes units and convert to number 
@@ -400,7 +411,7 @@ def convert_bandwidth(data, reverse=False):
             if type(k) is dict or type(k) is tuple or type(k) is list:
                 convert_bandwidth(k, reverse)
 
-def convert_boolean(data, items):
+def convert_boolean(data, items): #TODO OVIM delete
     '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean 
     It assumes that bandwidth is well formed
     Attributes:
@@ -634,6 +645,7 @@ def http_post_hosts():
 
             if config_dic['network_type'] == 'ovs':
                 # create bridge
+                create_dhcp_ovs_bridge()
                 config_dic['host_threads'][content['uuid']].insert_task("new-ovsbridge")
                 # check if more host exist
                 create_vxlan_mesh(content['uuid'])
@@ -649,19 +661,142 @@ def http_post_hosts():
         return
 
 
+def get_dhcp_controller():
+    """
+    Create an host_thread object for manage openvim controller and not create a thread for itself
+    :return: dhcp_host openvim controller object
+    """
+
+    if 'openvim_controller' in config_dic['host_threads']:
+        return config_dic['host_threads']['openvim_controller']
+
+    bridge_ifaces = []
+    controller_ip = config_dic['ovs_controller_ip']
+    ovs_controller_user = config_dic['ovs_controller_user']
+
+    host_test_mode = True if config_dic['mode'] == 'test' or config_dic['mode'] == "OF only" else False
+    host_develop_mode = True if config_dic['mode'] == 'development' else False
+
+    dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip, db=config_dic['db'],
+                               db_lock=config_dic['db_lock'], test=host_test_mode,
+                               image_path=config_dic['image_path'], version=config_dic['version'],
+                               host_id='openvim_controller', develop_mode=host_develop_mode,
+                               develop_bridge_iface=bridge_ifaces)
+
+    config_dic['host_threads']['openvim_controller'] = dhcp_host
+    dhcp_host.ssh_connect()
+    return dhcp_host
+
+
+def delete_dhcp_ovs_bridge(vlan, net_uuid):
+    """
+    Delete bridges and port created during dhcp launching at openvim controller
+    :param vlan: net vlan id
+    :param net_uuid: network identifier
+    :return:
+    """
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    controller_host.delete_dhcp_port(vlan, net_uuid)
+    controller_host.delete_dhcp_server(vlan, net_uuid, dhcp_path)
+
+
+def create_dhcp_ovs_bridge():
+    """
+    Initialize bridge to allocate the dhcp server at openvim controller
+    :return:
+    """
+    controller_host = get_dhcp_controller()
+    controller_host.create_ovs_bridge()
+
+
+def set_mac_dhcp(vm_ip, vlan, first_ip, last_ip, cidr, mac):
+    """"
+    Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
+    :param vm_ip: IP address asigned to a VM
+    :param vlan: Segmentation id
+    :param first_ip: First dhcp range ip
+    :param last_ip: Last dhcp range ip
+    :param cidr: net cidr
+    :param mac: VM vnic mac to be macthed with the IP received
+    """
+    if not vm_ip:
+        return
+    ip_tools = IPNetwork(cidr)
+    cidr_len = ip_tools.prefixlen
+    dhcp_netmask = str(ip_tools.netmask)
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    new_cidr = [first_ip + '/' + str(cidr_len)]
+    if not len(all_matching_cidrs(vm_ip, new_cidr)):
+        vm_ip = None
+
+    controller_host = get_dhcp_controller()
+    controller_host.set_mac_dhcp_server(vm_ip, mac, vlan, dhcp_netmask, dhcp_path)
+
+
+def delete_mac_dhcp(vm_ip, vlan, mac):
+    """
+    Delete into dhcp conf file the ip  assigned to a specific MAC address
+    :param vm_ip: IP address asigned to a VM
+    :param vlan: Segmentation id
+    :param mac:  VM vnic mac to be macthed with the IP received
+    :return:
+    """
+
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    controller_host.delete_mac_dhcp_server(vm_ip, mac, vlan, dhcp_path)
+
+
+def launch_dhcp_server(vlan, first_ip, last_ip, cidr):
+    """
+    Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
+    :param vlan: vlan identifier
+    :param first_ip: First dhcp range ip
+    :param last_ip: Last dhcp range ip
+    :param cidr: net cidr
+    :return:
+    """
+    ip_tools = IPNetwork(cidr)
+    dhcp_netmask = str(ip_tools.netmask)
+    ip_range = [first_ip, last_ip]
+    dhcp_path = config_dic['ovs_controller_file_path']
+
+    controller_host = get_dhcp_controller()
+    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)
+
+
 def create_vxlan_mesh(host_id):
+    """
+    Create vxlan mesh across all openvimc controller and computes.
+    :param host_id: host identifier
+    :param host_id: host identifier
+    :return:
+    """
+    dhcp_compute_name = get_vxlan_interface("dhcp")
     existing_hosts = get_hosts()
-    if len(existing_hosts['hosts']) > 1:
+    if len(existing_hosts['hosts']) > 0:
+        # vlxan mesh creation between openvim controller and computes
         computes_available = existing_hosts['hosts']
+        controller_host = get_dhcp_controller()
+        for compute in computes_available:
+            vxlan_interface_name = get_vxlan_interface(compute['id'][:8])
+            config_dic['host_threads'][compute['id']].insert_task("new-vxlan", dhcp_compute_name, controller_host.host)
+            controller_host.create_ovs_vxlan_tunnel(vxlan_interface_name, compute['ip_name'])
 
-        # TUNEL openvim controller
-
+        # vlxan mesh creation between openvim computes
         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])
+                    controller_host.create_ovs_vxlan_tunnel(vxlan_interface_name, compute_owner['ip_name'])
                     config_dic['host_threads'][compute['id']].insert_task("new-vxlan",
                                                                           vxlan_interface_name,
                                                                           compute_owner['ip_name'])
@@ -674,13 +809,20 @@ def delete_vxlan_mesh(host_id):
     """
     existing_hosts = get_hosts()
     computes_available = existing_hosts['hosts']
+    #
     vxlan_interface_name = get_vxlan_interface(host_id[:8])
-
+    controller_host = get_dhcp_controller()
+    controller_host.delete_ovs_vxlan_tunnel(vxlan_interface_name)
+    # remove bridge from openvim controller if no more computes exist
+    if len(existing_hosts):
+        controller_host.delete_ovs_bridge()
+    # Remove vxlan mesh
     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)
+            controller_host.delete_ovs_vxlan_tunnel(vxlan_interface_name)
+            config_dic['host_threads'][compute['id']].insert_task("del-vxlan", vxlan_interface_name)
 
 
 def get_vxlan_interface(local_uuid):
@@ -1106,13 +1248,14 @@ def http_get_images(tenant_id):
         bottle.abort(result, content)
     #obtain data
     select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_image,
-            ('id','name','description','path','public') )
+            ('id','name','checksum','description','path','public') )
     if tenant_id=='any':
         from_  ='images'
+        where_or_ = None
     else:
-        from_  ='tenants_images inner join images on tenants_images.image_id=images.uuid'
-        where_['tenant_id'] = tenant_id
-    result, content = my.db.get_table(SELECT=select_, FROM=from_, WHERE=where_, LIMIT=limit_)
+        from_  ='tenants_images right join images on tenants_images.image_id=images.uuid'
+        where_or_ = {'tenant_id': tenant_id, 'public': 'yes'}
+    result, content = my.db.get_table(SELECT=select_, DISTINCT=True, FROM=from_, WHERE=where_, WHERE_OR=where_or_, WHERE_AND_OR="AND", LIMIT=limit_)
     if result < 0:
         print "http_get_images Error", content
         bottle.abort(-result, content)
@@ -1131,14 +1274,15 @@ def http_get_image_id(tenant_id, image_id):
         bottle.abort(result, content)
     #obtain data
     select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_image,
-            ('id','name','description','progress', 'status','path', 'created', 'updated','public') )
+            ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
     if tenant_id=='any':
         from_  ='images'
+        where_or_ = None
     else:
-        from_  ='tenants_images as ti inner join images as i on ti.image_id=i.uuid'
-        where_['tenant_id'] = tenant_id
+        from_  ='tenants_images as ti right join images as i on ti.image_id=i.uuid'
+        where_or_ = {'tenant_id': tenant_id, 'public': "yes"}
     where_['uuid'] = image_id
-    result, content = my.db.get_table(SELECT=select_, FROM=from_, WHERE=where_, LIMIT=limit_)
+    result, content = my.db.get_table(SELECT=select_, DISTINCT=True, FROM=from_, WHERE=where_, WHERE_OR=where_or_, WHERE_AND_OR="AND", LIMIT=limit_)
 
     if result < 0:
         print "http_get_images error %d %s" % (result, content)
@@ -1173,15 +1317,26 @@ def http_post_images(tenant_id):
     if metadata_dict is not None: 
         http_content['image']['metadata'] = json.dumps(metadata_dict)
     #calculate checksum
-    host_test_mode = True if config_dic['mode']=='test' or config_dic['mode']=="OF only" else False
     try:
         image_file = http_content['image'].get('path',None)
-        if os.path.exists(image_file):
-            http_content['image']['checksum'] = md5(image_file)
-        elif is_url(image_file):
+        parsed_url = urlparse.urlparse(image_file)
+        if parsed_url.scheme == "" and parsed_url.netloc == "":
+            # The path is a local file
+            if os.path.exists(image_file):
+                http_content['image']['checksum'] = md5(image_file)
+        else:
+            # The path is a URL. Code should be added to download the image and calculate the checksum
+            #http_content['image']['checksum'] = md5(downloaded_image)
             pass
+        # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
+        host_test_mode = True if config_dic['mode']=='test' or config_dic['mode']=="OF only" else False
+        if host_test_mode:
+            if 'checksum' not in http_content['image']:
+                http_content['image']['checksum'] = md5_string(image_file)
         else:
-            if not host_test_mode:
+            # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
+            # If it is a URL, no error is sent. Checksum will be an empty string
+            if parsed_url.scheme == "" and parsed_url.netloc == "" and 'checksum' not in http_content['image']:
                 content = "Image file not found"
                 print "http_post_images error: %d %s" % (HTTP_Bad_Request, content)
                 bottle.abort(HTTP_Bad_Request, content)
@@ -1292,10 +1447,11 @@ def http_put_image_id(tenant_id, image_id):
     where_={'uuid': image_id}
     if tenant_id=='any':
         from_  ='images'
+        where_or_ = None
     else:
-        from_  ='tenants_images as ti inner join images as i on ti.image_id=i.uuid'
-        where_['tenant_id'] = tenant_id
-    result, content = my.db.get_table(SELECT=('public',), FROM=from_, WHERE=where_)
+        from_  ='tenants_images as ti right join images as i on ti.image_id=i.uuid'
+        where_or_ = {'tenant_id': tenant_id, 'public': 'yes'}
+    result, content = my.db.get_table(SELECT=('public',), DISTINCT=True, FROM=from_, WHERE=where_, WHERE_OR=where_or_, WHERE_AND_OR="AND")
     if result==0:
         text_error="Image '%s' not found" % image_id
         if tenant_id!='any':
@@ -1423,12 +1579,25 @@ def http_post_server_id(tenant_id):
         return
     server['flavor']=content[0]
     #check image valid and take info
-    result, content = my.db.get_table(FROM='tenants_images as ti join images as i on ti.image_id=i.uuid',
-        SELECT=('path','metadata'), WHERE={'uuid':server['image_id'], 'tenant_id':tenant_id, "status":"ACTIVE"})
+    result, content = my.db.get_table(FROM='tenants_images as ti right join images as i on ti.image_id=i.uuid',
+                                      SELECT=('path', 'metadata', 'image_id'),
+                                      WHERE={'uuid':server['image_id'], "status":"ACTIVE"},
+                                      WHERE_OR={'tenant_id':tenant_id, 'public': 'yes'},
+                                      WHERE_AND_OR="AND",
+                                      DISTINCT=True)
     if result<=0:
         bottle.abort(HTTP_Not_Found, 'image_id %s not found or not ACTIVE' % server['image_id'])
         return
-    server['image']=content[0]
+    for image_dict in content:
+        if image_dict.get("image_id"):
+            break
+    else:
+        # insert in data base tenants_images
+        r2, c2 = my.db.new_row('tenants_images', {'image_id': server['image_id'], 'tenant_id': tenant_id})
+        if r2<=0:
+            bottle.abort(HTTP_Not_Found, 'image_id %s cannot be used. Error %s' % (server['image_id'], c2))
+            return
+    server['image']={"path": content[0]["path"], "metadata": content[0]["metadata"]}
     if "hosts_id" in server:
         result, content = my.db.get_table(FROM='hosts', SELECT=('uuid',), WHERE={'uuid': server['host_id']})
         if result<=0:
@@ -1446,6 +1615,11 @@ def http_post_server_id(tenant_id):
         print
         if server_start == 'no':
             content['status'] = 'INACTIVE'
+        dhcp_nets_id = []
+        for net in http_content['server']['networks']:
+            if net['type'] == 'instance:ovs':
+                dhcp_nets_id.append(get_network_id(net['net_id']))
+
         ports_to_free=[]
         new_instance_result, new_instance = my.db.new_instance(content, nets, ports_to_free)
         if new_instance_result < 0:
@@ -1466,9 +1640,8 @@ def http_post_server_id(tenant_id):
                 print ':http_post_servers ERROR UPDATING NETS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
 
             
-            
-        #look for dhcp ip address 
-        r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": new_instance})
+        #look for dhcp ip address
+        r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "ip_address", "net_id"], WHERE={"instance_id": new_instance})
         if r2 >0:
             for iface in c2:
                 if config_dic.get("dhcp_server") and iface["net_id"] in config_dic["dhcp_nets"]:
@@ -1481,14 +1654,23 @@ def http_post_server_id(tenant_id):
                 server_net = get_network_id(iface['net_id'])
                 if server_net["network"].get('provider:physical', "")[:3] == 'OVS':
                     vlan = str(server_net['network']['provider:vlan'])
-                    config_dic['host_threads'][server['host_id']].insert_task("create-ovs-bridge-port", vlan)
+                    dhcp_enable = bool(server_net['network']['enable_dhcp'])
+                    if dhcp_enable:
+                        dhcp_firt_ip = str(server_net['network']['dhcp_first_ip'])
+                        dhcp_last_ip = str(server_net['network']['dhcp_last_ip'])
+                        dhcp_cidr = str(server_net['network']['cidr'])
+                        vm_dhcp_ip = c2[0]["ip_address"]
+                        config_dic['host_threads'][server['host_id']].insert_task("create-ovs-bridge-port", vlan)
+
+                        set_mac_dhcp(vm_dhcp_ip, vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, c2[0]['mac'])
+                        launch_dhcp_server(vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr)
+
         #Start server
-        
         server['uuid'] = new_instance
         server_start = server.get('start', 'yes')
 
         if server_start != 'no':
-            server['paused'] = True if server_start == 'paused' else False 
+            server['paused'] = True if server_start == 'paused' else False
             server['action'] = {"start":None}
             server['status'] = "CREATING"
             #Program task
@@ -1572,8 +1754,9 @@ def http_server_action(server_id, tenant_id, action):
                 else: #result==1
                     image_id = content[0]['image_id']    
                 
-        result, content = my.db.get_table(FROM='tenants_images as ti join images as i on ti.image_id=i.uuid',
-            SELECT=('path','metadata'), WHERE={'uuid':image_id, 'tenant_id':tenant_id, "status":"ACTIVE"})
+        result, content = my.db.get_table(FROM='tenants_images as ti right join images as i on ti.image_id=i.uuid',
+            SELECT=('path','metadata'), WHERE={'uuid':image_id, "status":"ACTIVE"},
+            WHERE_OR={'tenant_id':tenant_id, 'public': 'yes'}, WHERE_AND_OR="AND", DISTINCT=True)
         if result<=0:
             bottle.abort(HTTP_Not_Found, 'image_id %s not found or not ACTIVE' % image_id)
             return
@@ -1650,8 +1833,12 @@ def http_server_action(server_id, tenant_id, action):
                         print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
         # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan)
         for net in net_ovs_list:
+            mac = str(net[3])
+            vm_ip = str(net[2])
             vlan = str(net[1])
             net_id = net[0]
+            delete_dhcp_ovs_bridge(vlan, net_id)
+            delete_mac_dhcp(vm_ip, vlan, mac)
             config_dic['host_threads'][server['host_id']].insert_task('del-ovs-port', vlan, net_id)
     return format_out(data)
 
@@ -1705,7 +1892,7 @@ def http_get_networks():
         print "http_get_networks error %d %s" % (result, content)
         bottle.abort(-result, content)
     else:
-        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp') )
+        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
         delete_nulls(content)      
         change_keys_http2db(content, http2db_network, reverse=True)  
         data={'networks' : content}
@@ -1730,7 +1917,7 @@ def get_network_id(network_id):
         print "http_get_networks_id network '%s' not found" % network_id
         bottle.abort(HTTP_Not_Found, 'network %s not found' % network_id)
     else:
-        convert_boolean(content, ('shared', 'admin_state_up', 'enale_dhcp') )
+        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
         change_keys_http2db(content, http2db_network, reverse=True)        
         #get ports
         result, ports = my.db.get_table(FROM='ports', SELECT=('uuid as port_id',), 
@@ -1762,6 +1949,10 @@ def http_post_networks():
     #check valid params
     net_provider = network.get('provider')
     net_type =     network.get('type')
+    net_enable_dhcp = network.get('enable_dhcp')
+    if net_enable_dhcp:
+        net_cidr = network.get('cidr')
+
     net_vlan =     network.get("vlan")
     net_bind_net = network.get("bind_net")
     net_bind_type= network.get("bind_type")
@@ -1822,7 +2013,8 @@ def http_post_networks():
         net_type='bridge_man' 
         
     if net_provider != None:
-        if net_provider[:7] == 'bridge:':
+        if net_provider[:7]=='bridge:':
+            #check it is one of the pre-provisioned bridges
             bridge_net_name = net_provider[7:]
             for brnet in config_dic['bridge_nets']:
                 if brnet[0]==bridge_net_name: # free
@@ -1867,12 +2059,15 @@ def http_post_networks():
     network['provider'] = net_provider
     network['type']     = net_type
     network['vlan']     = net_vlan
+
+    if 'enable_dhcp' in network and network['enable_dhcp']:
+        check_dhcp_data_integrity(network)
+
     result, content = my.db.new_row('nets', network, True, True)
     
     if result >= 0:
         if bridge_net!=None:
             bridge_net[3] = content
-        
         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)
@@ -1887,6 +2082,24 @@ def http_post_networks():
         return
 
 
+def check_dhcp_data_integrity(network):
+    """
+    Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
+    :param network: list with user nets paramters
+    :return:
+    """
+    control_iface = []
+
+    if "cidr" in network:
+        cidr = network["cidr"]
+
+        ips = IPNetwork(cidr)
+        if "dhcp_first_ip" not in network:
+            network["dhcp_first_ip"] = str(ips[2])
+        if "dhcp_last_ip" not in network:
+            network["dhcp_last_ip"] = str(ips[-2])
+
+
 @bottle.route(url_base + '/networks/<network_id>', method='PUT')
 def http_put_network_id(network_id):
     '''update a network_id into the database.'''
@@ -2097,37 +2310,37 @@ def http_get_ports():
     select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_port,
             ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
              'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
-    #result, content = my.db.get_ports(where_)
-    result, content = my.db.get_table(SELECT=select_, WHERE=where_, FROM='ports',LIMIT=limit_)
-    if result < 0:
-        print "http_get_ports Error", result, content
-        bottle.abort(-result, content)
-        return
-    else:
-        convert_boolean(content, ('admin_state_up',) )
-        delete_nulls(content)      
-        change_keys_http2db(content, http2db_port, reverse=True)
-        data={'ports' : content}
+    try:
+        ports = my.ovim.get_ports(columns=select_, filter=where_, limit=limit_)
+        delete_nulls(ports)
+        change_keys_http2db(ports, http2db_port, reverse=True)
+        data={'ports' : ports}
         return format_out(data)
+    except ovim.ovimException as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(HTTP_Bad_Request, str(e))
 
 @bottle.route(url_base + '/ports/<port_id>', method='GET')
 def http_get_port_id(port_id):
     my = config_dic['http_threads'][ threading.current_thread().name ]
-    #obtain data
-    result, content = my.db.get_table(WHERE={'uuid': port_id}, FROM='ports')
-    if result < 0:
-        print "http_get_ports error", result, content
-        bottle.abort(-result, content)
-    elif result==0:
-        print "http_get_ports port '%s' not found" % str(port_id)
-        bottle.abort(HTTP_Not_Found, 'port %s not found' % port_id)
-    else:
-        convert_boolean(content, ('admin_state_up',) )
-        delete_nulls(content)      
-        change_keys_http2db(content, http2db_port, reverse=True)
-        data={'port' : content[0]}
+    try:
+        ports = my.ovim.get_ports(filter={"uuid": port_id})
+        if not ports:
+            bottle.abort(HTTP_Not_Found, 'port %s not found' % port_id)
+            return
+        delete_nulls(ports)
+        change_keys_http2db(ports, http2db_port, reverse=True)
+        data = {'port': ports[0]}
         return format_out(data)
-    
+    except ovim.ovimException as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(HTTP_Bad_Request, str(e))
 
 @bottle.route(url_base + '/ports', method='POST')
 def http_post_ports():
@@ -2141,115 +2354,54 @@ def http_post_ports():
     if r is not None: print "http_post_ports: Warning: remove extra items ", r
     change_keys_http2db(http_content['port'], http2db_port)
     port=http_content['port']
-
-    port['type'] = 'external'
-    if 'net_id' in port and port['net_id'] == None:
-        del port['net_id']
-
-    if 'net_id' in port:
-        #check that new net has the correct type
-        result, new_net = my.db.check_target_net(port['net_id'], None, 'external' )
-        if result < 0:
-            bottle.abort(HTTP_Bad_Request, new_net)
+    try:
+        port_id = my.ovim.new_port(port)
+        ports = my.ovim.get_ports(filter={"uuid": port_id})
+        if not ports:
+            bottle.abort(HTTP_Internal_Server_Error, "port '{}' inserted but not found at database".format(port_id))
             return
-    #insert in data base
-    result, uuid = my.db.new_row('ports', port, True, True)
-    if result > 0:
-        if 'net_id' in port: 
-            r,c = config_dic['of_thread'].insert_task("update-net", port['net_id'])
-            if r < 0:
-                print "http_post_ports error while launching openflow rules"
-                bottle.abort(HTTP_Internal_Server_Error, c)
-        return http_get_port_id(uuid)
-    else:
-        bottle.abort(-result, uuid)
-        return
-    
+        delete_nulls(ports)
+        change_keys_http2db(ports, http2db_port, reverse=True)
+        data = {'port': ports[0]}
+        return format_out(data)
+    except ovim.ovimException as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(HTTP_Bad_Request, str(e))
+
 @bottle.route(url_base + '/ports/<port_id>', method='PUT')
 def http_put_port_id(port_id):
     '''update a port_id into the database.'''
-
     my = config_dic['http_threads'][ threading.current_thread().name ]
     #parse input data
     http_content = format_in( port_update_schema )
     change_keys_http2db(http_content['port'], http2db_port)
     port_dict=http_content['port']
 
-    #Look for the previous port data
-    where_ = {'uuid': port_id}
-    result, content = my.db.get_table(FROM="ports",WHERE=where_)
-    if result < 0:
-        print "http_put_port_id error", result, content
-        bottle.abort(-result, content)
-        return
-    elif result==0:
-        print "http_put_port_id port '%s' not found" % port_id
-        bottle.abort(HTTP_Not_Found, 'port %s not found' % port_id)
-        return
-    print port_dict
-    for k in ('vlan','switch_port','mac_address', 'tenant_id'):
+    for k in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
         if k in port_dict and not my.admin:
             bottle.abort(HTTP_Unauthorized, "Needed admin privileges for changing " + k)
             return
-    
-    port=content[0]
-    #change_keys_http2db(port, http2db_port, reverse=True)
-    nets = []
-    host_id = None
-    result=1
-    if 'net_id' in port_dict:
-        #change of net. 
-        old_net = port.get('net_id', None)
-        new_net = port_dict['net_id']
-        if old_net != new_net:
-            
-            if new_net is not None: nets.append(new_net) #put first the new net, so that new openflow rules are created before removing the old ones 
-            if old_net is not None: nets.append(old_net)
-            if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
-                bottle.abort(HTTP_Forbidden, "bridge interfaces cannot be attached to a different net")
-                return
-            elif port['type'] == 'external':
-                if not my.admin:
-                    bottle.abort(HTTP_Unauthorized, "Needed admin privileges")
-                    return
-            else:
-                if new_net != None:
-                    #check that new net has the correct type
-                    result, new_net_dict = my.db.check_target_net(new_net, None, port['type'] )
-                
-                #change VLAN for SR-IOV ports
-                if result>=0 and port["type"]=="instance:data" and port["model"]=="VF": #TODO consider also VFnotShared
-                    if new_net == None:
-                        port_dict["vlan"] = None
-                    else:
-                        port_dict["vlan"] = new_net_dict["vlan"]
-                    #get host where this VM is allocated
-                    result, content = my.db.get_table(FROM="instances",WHERE={"uuid":port["instance_id"]})
-                    if result<0:
-                        print "http_put_port_id database error", content
-                    elif result>0:
-                        host_id = content[0]["host_id"]
-    
-    #insert in data base
-    if result >= 0:
-        result, content = my.db.update_rows('ports', port_dict, WHERE={'uuid': port_id}, log=False )
-        
-    #Insert task to complete actions
-    if result > 0: 
-        for net_id in nets:
-            r,v = config_dic['of_thread'].insert_task("update-net", net_id)
-            if r<0: print "Error *********   http_put_port_id  update_of_flows: ", v
-            #TODO Do something if fails
-        if host_id != None:
-            config_dic['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
-    
-    if result >= 0:
-        return http_get_port_id(port_id)
-    else:
-        bottle.abort(HTTP_Bad_Request, content)
-        return
+    try:
+        port_id = my.ovim.edit_port(port_id, port_dict, my.admin)
+        ports = my.ovim.get_ports(filter={"uuid": port_id})
+        if not ports:
+            bottle.abort(HTTP_Internal_Server_Error, "port '{}' edited but not found at database".format(port_id))
+            return
+        delete_nulls(ports)
+        change_keys_http2db(ports, http2db_port, reverse=True)
+        data = {'port': ports[0]}
+        return format_out(data)
+    except ovim.ovimException as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(HTTP_Bad_Request, str(e))
+
 
-  
 @bottle.route(url_base + '/ports/<port_id>', method='DELETE')
 def http_delete_port_id(port_id):
     '''delete a port_id from the database.'''
@@ -2257,30 +2409,15 @@ def http_delete_port_id(port_id):
     if not my.admin:
         bottle.abort(HTTP_Unauthorized, "Needed admin privileges")
         return
+    try:
+        result = my.ovim.delete_port(port_id)
+        data = {'result': result}
+        return format_out(data)
+    except ovim.ovimException as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        my.logger.error(str(e), exc_info=True)
+        bottle.abort(HTTP_Bad_Request, str(e))
 
-    #Look for the previous port data
-    where_ = {'uuid': port_id, "type": "external"}
-    result, ports = my.db.get_table(WHERE=where_, FROM='ports',LIMIT=100)
-    
-    if result<=0:
-        print "http_delete_port_id port '%s' not found" % port_id
-        bottle.abort(HTTP_Not_Found, 'port %s not found or device_owner is not external' % port_id)
-        return
-    #delete from the data base
-    result, content = my.db.delete_row('ports', port_id )
 
-    if result == 0:
-        bottle.abort(HTTP_Not_Found, content)
-    elif result >0:
-        network = ports[0].get('net_id', None)
-        if network is not None:
-            #change of net. 
-            r,c = config_dic['of_thread'].insert_task("update-net", network)
-            if r<0: print "!!!!!! http_delete_port_id update_of_flows error", r, c 
-        data={'result' : content}
-        return format_out(data)
-    else:
-        print "http_delete_port_id error",result, content
-        bottle.abort(-result, content)
-    return
-