Add openflow-port-mapping CLI command
[osm/openvim.git] / httpserver.py
index 86f96a6..edf1e8d 100644 (file)
@@ -26,15 +26,20 @@ This is the thread for the http server North API.
 Two thread will be launched, with normal and administrative permissions.
 '''
 
-__author__="Alfonso Tierno"
+__author__="Alfonso Tierno, Gerardo Garcia, Leonardo Mirabal"
 __date__ ="$10-jul-2014 12:07:15$"
 
 import bottle
+import urlparse
 import yaml
 import json
 import threading
 import datetime
-import RADclass
+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
 from vim_schema import host_new_schema, host_edit_schema, tenant_new_schema, \
@@ -42,11 +47,15 @@ from vim_schema import host_new_schema, host_edit_schema, tenant_new_schema, \
     flavor_new_schema, flavor_update_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
+    port_new_schema, port_update_schema, openflow_controller_schema, of_port_map_new_schema
+import ovim
+import logging
 
 global my
 global url_base
 global config_dic
+global RADclass_module
+RADclass=None  #RADclass module is charged only if not in test mode
 
 url_base="/openvim"
 
@@ -61,6 +70,17 @@ HTTP_Conflict =             409
 HTTP_Service_Unavailable =  503 
 HTTP_Internal_Server_Error= 500 
 
+def md5(fname):
+    hash_md5 = hashlib.md5()
+    with open(fname, "rb") as f:
+        for chunk in iter(lambda: f.read(4096), b""):
+            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
@@ -127,12 +147,14 @@ def check_extended(extended, allow_net_attach=False):
 #
 # dictionaries that change from HTTP API to database naming
 #
+http2db_id={'id':'uuid'}
 http2db_host={'id':'uuid'}
 http2db_tenant={'id':'uuid'}
 http2db_flavor={'id':'uuid','imageRef':'image_id'}
 http2db_image={'id':'uuid', 'created':'created_at', 'updated':'modified_at', 'public': 'public'}
 http2db_server={'id':'uuid','hostId':'host_id','flavorRef':'flavor_id','imageRef':'image_id','created':'created_at'}
 http2db_network={'id':'uuid','provider:vlan':'vlan', 'provider:physical': 'provider'}
+http2db_ofc = {'id': 'uuid'}
 http2db_port={'id':'uuid', 'network_id':'net_id', 'mac_address':'mac', 'device_owner':'type','device_id':'instance_id','binding:switch_port':'switch_port','binding:vlan':'vlan', 'bandwidth':'Mbps'}
 
 def remove_extra_items(data, schema):
@@ -168,7 +190,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:
@@ -190,7 +212,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
@@ -205,6 +228,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
@@ -319,40 +343,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 
@@ -388,7 +413,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:
@@ -435,13 +460,16 @@ def check_valid_tenant(my, tenant_id):
             return HTTP_Not_Found, "tenant '%s' not found" % tenant_id
     return 0, None
 
-def check_valid_uuid(uuid):
-    id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
-    try:
-        js_v(uuid, id_schema)
-        return True
-    except js_e.ValidationError:
-        return False
+def is_url(url):
+    '''
+    Check if string value is a well-wormed url
+    :param url: string url
+    :return: True if is a valid url, False if is not well-formed
+    '''
+
+    parsed_url = urlparse.urlparse(url)
+    return parsed_url
+
 
 @bottle.error(400)
 @bottle.error(401) 
@@ -468,8 +496,12 @@ def enable_cors():
 
 @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_)
@@ -482,7 +514,7 @@ def http_get_hosts():
         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):
@@ -514,9 +546,14 @@ def http_post_hosts():
         ip_name=host['ip_name']
         user=host['user']
         password=host.get('password', None)
+        if not RADclass_module:
+            try:
+                RADclass_module = imp.find_module("RADclass")
+            except (IOError, ImportError) as e:
+                raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e))
 
         #fill rad info
-        rad = RADclass.RADclass()
+        rad = RADclass_module.RADclass()
         (return_status, code) = rad.obtain_RAD(user, password, ip_name)
         
         #return 
@@ -578,7 +615,6 @@ def http_post_hosts():
                                 sriov['source_name'] = index
                                 index += 1
                             interfaces.append  ({'pci':str(port_k), 'Mbps': port_v['speed']/1000000, 'sriovs': new_sriovs, 'mac':port_v['mac'], 'source_name':port_v['source_name']})
-            #@TODO LA memoria devuelta por el RAD es incorrecta, almenos para IVY1, NFV100
             memory=node['memory']['node_size'] / (1024*1024*1024)
             #memory=get_next_2pow(node['memory']['hugepage_nr'])
             host['numas'].append( {'numa_socket': node['id'], 'hugepages': node['memory']['hugepage_nr'], 'memory':memory, 'interfaces': interfaces, 'cores': cores } )
@@ -600,6 +636,13 @@ def http_post_hosts():
             thread.start()
             config_dic['host_threads'][ content['uuid'] ] = thread
 
+            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'])
+
         #return host data
         change_keys_http2db(content, http2db_host, reverse=True)
         if len(warning_text)>0:
@@ -610,6 +653,147 @@ def http_post_hosts():
         bottle.abort(HTTP_Bad_Request, content)
         return
 
+
+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']
+
+    http_controller = config_dic['http_threads'][threading.current_thread().name]
+    dhcp_controller = http_controller.ovim.get_dhcp_controller()
+
+    dhcp_controller.delete_dhcp_port(vlan, net_uuid)
+    dhcp_controller.delete_dhcp_server(vlan, net_uuid, dhcp_path)
+
+
+def create_dhcp_ovs_bridge():
+    """
+    Initialize bridge to allocate the dhcp server at openvim controller
+    :return:
+    """
+    http_controller = config_dic['http_threads'][threading.current_thread().name]
+    dhcp_controller = http_controller.ovim.get_dhcp_controller()
+
+    dhcp_controller.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
+
+    http_controller = config_dic['http_threads'][threading.current_thread().name]
+    dhcp_controller = http_controller.ovim.get_dhcp_controller()
+
+    dhcp_controller.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']
+
+    http_controller = config_dic['http_threads'][threading.current_thread().name]
+    dhcp_controller = http_controller.ovim.get_dhcp_controller()
+
+    dhcp_controller.delete_mac_dhcp_server(vm_ip, mac, vlan, 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']) > 0:
+        # vlxan mesh creation between openvim controller and computes
+        computes_available = existing_hosts['hosts']
+
+        http_controller = config_dic['http_threads'][threading.current_thread().name]
+        dhcp_controller = http_controller.ovim.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, dhcp_controller.host)
+            dhcp_controller.create_ovs_vxlan_tunnel(vxlan_interface_name, compute['ip_name'])
+
+        # 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])
+                    dhcp_controller.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'])
+
+
+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])
+
+    http_controller = config_dic['http_threads'][threading.current_thread().name]
+    dhcp_host = http_controller.ovim.get_dhcp_controller()
+
+    dhcp_host.delete_ovs_vxlan_tunnel(vxlan_interface_name)
+    # remove bridge from openvim controller if no more computes exist
+    if len(existing_hosts):
+        dhcp_host.delete_ovs_bridge()
+    # Remove vxlan mesh
+    for compute in computes_available:
+        if host_id == compute['id']:
+            pass
+        else:
+            dhcp_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):
+    """
+    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'''
@@ -631,12 +815,21 @@ def http_put_host_id(host_id):
         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")
 
+        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:
@@ -654,9 +847,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)
-    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 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}
@@ -665,114 +862,140 @@ def http_delete_host_id(host_id):
         print "http_delete_host_id error",result, content
         bottle.abort(-result, content)
         return
-
-
-
 #
 # TENANTS
 #
 
+
 @bottle.route(url_base + '/tenants', method='GET')
 def http_get_tenants():
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_tenant,
-            ('id','name','description','enabled') )
-    result, content = my.db.get_table(FROM='tenants', SELECT=select_,WHERE=where_,LIMIT=limit_)
-    if result < 0:
-        print "http_get_tenants Error", content
-        bottle.abort(-result, content)
-    else:
-        change_keys_http2db(content, http2db_tenant, reverse=True)
-        convert_boolean(content, ('enabled',))
-        data={'tenants' : content}
-        #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
+    """
+    Retreive tenant list from DB
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_tenant,
+                                                      ('id', 'name', 'description', 'enabled'))
+        tenants = my.ovim.get_tenants(select_, where_)
+        delete_nulls(tenants)
+        change_keys_http2db(tenants, http2db_tenant, reverse=True)
+        data = {'tenants': tenants}
         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 + '/tenants/<tenant_id>', method='GET')
 def http_get_tenant_id(tenant_id):
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    result, content = my.db.get_table(FROM='tenants', SELECT=('uuid','name','description', 'enabled'),WHERE={'uuid': tenant_id} )
-    if result < 0:
-        print "http_get_tenant_id error %d %s" % (result, content)
-        bottle.abort(-result, content)
-    elif result==0:
-        print "http_get_tenant_id tenant '%s' not found" % tenant_id
-        bottle.abort(HTTP_Not_Found, "tenant %s not found" % tenant_id)
-    else:
-        change_keys_http2db(content, http2db_tenant, reverse=True)
-        convert_boolean(content, ('enabled',))
-        data={'tenant' : content[0]}
-        #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
+    """
+    Get tenant from DB by id
+    :param tenant_id: tenant id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        tenant = my.ovim.show_tenant_id(tenant_id)
+        delete_nulls(tenant)
+        change_keys_http2db(tenant, http2db_tenant, reverse=True)
+        data = {'tenant': tenant}
         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 + '/tenants', method='POST')
 def http_post_tenants():
-    '''insert a tenant into the database.'''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #parse input data
-    http_content = format_in( tenant_new_schema )
-    r = remove_extra_items(http_content, tenant_new_schema)
-    if r is not None: print "http_post_tenants: Warning: remove extra items ", r
-    change_keys_http2db(http_content['tenant'], http2db_tenant)
+    """
+    Insert a tenant into the database.
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        http_content = format_in(tenant_new_schema)
+        r = remove_extra_items(http_content, tenant_new_schema)
+        if r is not None:
+            my.logger.error("http_post_tenants: Warning: remove extra items " + str(r), exc_info=True)
+        # insert in data base
+        tenant_id = my.ovim.new_tentant(http_content['tenant'])
+        tenant = my.ovim.show_tenant_id(tenant_id)
+        change_keys_http2db(tenant, http2db_tenant, reverse=True)
+        delete_nulls(tenant)
+        data = {'tenant': tenant}
+        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))
 
-    #insert in data base
-    result, content = my.db.new_tenant(http_content['tenant'])
-            
-    if result >= 0:
-        return http_get_tenant_id(content)
-    else:
-        bottle.abort(-result, content)
-        return
     
 @bottle.route(url_base + '/tenants/<tenant_id>', method='PUT')
 def http_put_tenant_id(tenant_id):
-    '''update a tenant into the database.'''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #parse input data
-    http_content = format_in( tenant_edit_schema )
-    r = remove_extra_items(http_content, tenant_edit_schema)
-    if r is not None: print "http_put_tenant_id: Warning: remove extra items ", r
-    change_keys_http2db(http_content['tenant'], http2db_tenant)
+    """
+    Update a tenantinto DB.
+    :param tenant_id: tentant id
+    :return:
+    """
+
+    my = config_dic['http_threads'][threading.current_thread().name]
+    try:
+        # parse input data
+        http_content = format_in(tenant_edit_schema)
+        r = remove_extra_items(http_content, tenant_edit_schema)
+        if r is not None:
+            print "http_put_tenant_id: Warning: remove extra items ", r
+        change_keys_http2db(http_content['tenant'], http2db_tenant)
+        # insert in data base
+        my.ovim.edit_tenant(tenant_id, http_content['tenant'])
+        tenant = my.ovim.show_tenant_id(tenant_id)
+        change_keys_http2db(tenant, http2db_tenant, reverse=True)
+        data = {'tenant': tenant}
+        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))
 
-    #insert in data base
-    result, content = my.db.update_rows('tenants', http_content['tenant'], WHERE={'uuid': tenant_id}, log=True )
-    if result >= 0:
-        return http_get_tenant_id(tenant_id)
-    else:
-        bottle.abort(-result, content)
-        return
 
 @bottle.route(url_base + '/tenants/<tenant_id>', method='DELETE')
 def http_delete_tenant_id(tenant_id):
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #check permissions
-    r, tenants_flavors = my.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id','tenant_id'), WHERE={'tenant_id': tenant_id})
-    if r<=0:
-        tenants_flavors=()
-    r, tenants_images  = my.db.get_table(FROM='tenants_images',  SELECT=('image_id','tenant_id'),  WHERE={'tenant_id': tenant_id})
-    if r<=0:
-        tenants_images=()
-    result, content = my.db.delete_row('tenants', tenant_id)
-    if result == 0:
-        bottle.abort(HTTP_Not_Found, content)
-    elif result >0:
-        print "alf", tenants_flavors, tenants_images
-        for flavor in tenants_flavors:
-            my.db.delete_row_by_key("flavors", "uuid",  flavor['flavor_id'])
-        for image in tenants_images:
-            my.db.delete_row_by_key("images", "uuid",   image['image_id'])
-        data={'result' : content}
-        return format_out(data)
-    else:
-        print "http_delete_tenant_id error",result, content
-        bottle.abort(-result, content)
-        return
+    """
+    Delete a tenant from the database.
+    :param tenant_id: tenant id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
 
+    try:
+        content = my.ovim.delete_tentant(tenant_id)
+        data = {'result': content}
+        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))
 #
 # FLAVORS
 #
 
+
 @bottle.route(url_base + '/<tenant_id>/flavors', method='GET')
 def http_get_flavors(tenant_id):
     my = config_dic['http_threads'][ threading.current_thread().name ]
@@ -1011,13 +1234,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)
@@ -1036,14 +1260,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)
@@ -1077,6 +1302,33 @@ def http_post_images(tenant_id):
     metadata_dict = http_content['image'].pop('metadata', None)
     if metadata_dict is not None: 
         http_content['image']['metadata'] = json.dumps(metadata_dict)
+    #calculate checksum
+    try:
+        image_file = http_content['image'].get('path',None)
+        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:
+            # 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)
+    except Exception as e:
+        print "ERROR. Unexpected exception: %s" % (str(e))
+        bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
     #insert in data base
     result, content = my.db.new_image(http_content['image'], tenant_id)
     if result >= 0:
@@ -1181,10 +1433,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':
@@ -1312,12 +1565,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:
@@ -1335,6 +1601,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:
@@ -1348,30 +1619,46 @@ def http_post_server_id(tenant_id):
             r,c = config_dic['host_threads'][ server['host_id'] ].insert_task( 'restore-iface',*port )
             if r < 0:
                 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
-        #updata nets
-        for net in nets:
-            r,c = config_dic['of_thread'].insert_task("update-net", net)
-            if r < 0:
-                print ':http_post_servers ERROR UPDATING NETS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
+        # update nets
+        for net_id in nets:
+            try:
+                my.ovim.net_update_ofc_thread(net_id)
+            except ovim.ovimException as e:
+                my.logger.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id, str(e)))
 
-            
-            
-        #look for dhcp ip address 
-        r2, c2 = my.db.get_table(FROM="ports", SELECT=["mac", "net_id"], WHERE={"instance_id": new_instance})
-        if r2 >0 and config_dic.get("dhcp_server"):
+        # 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 iface["net_id"] in config_dic["dhcp_nets"]:
+                if config_dic.get("dhcp_server") and iface["net_id"] in config_dic["dhcp_nets"]:
                     #print "dhcp insert add task"
                     r,c = config_dic['dhcp_thread'].insert_task("add", iface["mac"])
                     if r < 0:
-                        print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c 
-        
-    #Start server
-        
+                        print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c
+
+                #ensure compute contain the bridge for ovs networks:
+                server_net = get_network_id(iface['net_id'])
+                if server_net["network"].get('provider:physical', "")[:3] == 'OVS':
+                    vlan = str(server_net['network']['provider: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'])
+                        gateway = str(server_net['network']['gateway'])
+                        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'])
+                        http_controller = config_dic['http_threads'][threading.current_thread().name]
+                        http_controller.ovim.launch_dhcp_server(vlan, dhcp_firt_ip, dhcp_last_ip, dhcp_cidr, gateway)
+
+        #Start server
         server['uuid'] = new_instance
-        #server_start = server.get('start', 'yes')
+        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
@@ -1455,8 +1742,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
@@ -1504,24 +1792,29 @@ def http_server_action(server_id, tenant_id, action):
         return http_get_image_id(tenant_id, image_uuid)
     
     #Update DB only for CREATING or DELETING status
-    data={'result' : 'in process'}
+    data={'result' : 'deleting in process'}
+    warn_text=""
     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})
-        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:
-                print ' http_post_server_action error at server deletion ERROR resore-iface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c1
-                data={'result' : 'deleting in process, but ifaces cannot be restored!!!!!'}
-        for net in nets:
-            r1,c1 = config_dic['of_thread'].insert_task("update-net", net)
-            if r1 < 0:
-                print ' http_post_server_action error at server deletion ERROR UPDATING NETS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' +  c1
-                data={'result' : 'deleting in process, but openflow rules cannot be deleted!!!!!'}
-        #look for dhcp ip address 
+                my.logger.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1)
+                warn_text += "; Error iface '{}' cannot be restored '{}'".format(str(port), str(e))
+        for net_id in nets:
+            try:
+                my.ovim.net_update_ofc_thread(net_id)
+            except ovim.ovimException as e:
+                my.logger.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id, str(e)))
+                warn_text += "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id, str (e))
+
+        # look for dhcp ip address
         if r2 >0 and config_dic.get("dhcp_server"):
             for iface in c2:
                 if iface["net_id"] in config_dic["dhcp_nets"]:
@@ -1529,8 +1822,16 @@ 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 
-
-    return format_out(data)
+        # 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 + warn_text)
 
 
 
@@ -1569,397 +1870,371 @@ def http_post_server_action(tenant_id, server_id):
 
 @bottle.route(url_base + '/networks', method='GET')
 def http_get_networks():
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #obtain data
-    select_,where_,limit_ = filter_query_string(bottle.request.query, http2db_network,
-            ('id','name','tenant_id','type',
-             'shared','provider:vlan','status','last_error','admin_state_up','provider:physical') )
-    #TODO temporally remove tenant_id
-    if "tenant_id" in where_:
-        del where_["tenant_id"]
-    result, content = my.db.get_table(SELECT=select_, FROM='nets', WHERE=where_, LIMIT=limit_)
-    if result < 0:
-        print "http_get_networks error %d %s" % (result, content)
-        bottle.abort(-result, content)
-    else:
-        convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp') )
-        delete_nulls(content)      
-        change_keys_http2db(content, http2db_network, reverse=True)  
-        data={'networks' : content}
+    """
+    Get all networks available
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        # obtain data
+        select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_network,
+                                                      ('id', 'name', 'tenant_id', 'type',
+                                                       'shared', 'provider:vlan', 'status', 'last_error',
+                                                       'admin_state_up', 'provider:physical'))
+        if "tenant_id" in where_:
+            del where_["tenant_id"]
+
+        content = my.ovim.get_networks(select_, where_, limit_)
+
+        delete_nulls(content)
+        change_keys_http2db(content, http2db_network, reverse=True)
+        data = {'networks': content}
         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 + '/networks/<network_id>', method='GET')
 def http_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)
+    """
+    Get a network data by id
+    :param network_id:
+    :return:
+    """
+    data = get_network_id(network_id)
+    return format_out(data)
+
+
+def get_network_id(network_id):
+    """
+    Get network from DB by id
+    :param network_id: network Id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        # obtain data
+        where_ = bottle.request.query
+        content = my.ovim.show_network(network_id, where_)
+
+        change_keys_http2db(content, http2db_network, reverse=True)
+        delete_nulls(content)
+        data = {'network': content}
+        return 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))
 
-    if result < 0:
-        print "http_get_networks_id error %d %s" % (result, content)
-        bottle.abort(-result, content)
-    elif result==0:
-        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') )
-        change_keys_http2db(content, http2db_network, reverse=True)        
-        #get ports
-        result, ports = my.db.get_table(FROM='ports', SELECT=('uuid as port_id',), 
-                                              WHERE={'net_id': network_id}, LIMIT=100)
-        if len(ports) > 0:
-            content[0]['ports'] = ports
-        delete_nulls(content[0])      
-        data={'network' : content[0]}
-        return format_out(data)
 
 @bottle.route(url_base + '/networks', method='POST')
 def http_post_networks():
-    '''insert a network into the database.'''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #parse input data
-    http_content = format_in( network_new_schema )
-    r = remove_extra_items(http_content, network_new_schema)
-    if r is not None: print "http_post_networks: Warning: remove extra items ", r
-    change_keys_http2db(http_content['network'], http2db_network)
-    network=http_content['network']
-    #check valid tenant_id
-    tenant_id= network.get('tenant_id')
-    if tenant_id!=None:
-        result, _ = my.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id,"enabled":True})
-        if result<=0:
-            bottle.abort(HTTP_Not_Found, 'tenant %s not found or not enabled' % tenant_id)
-            return
-    bridge_net = None
-    #check valid params
-    net_provider = network.get('provider')
-    net_type =     network.get('type')
-    net_vlan =     network.get("vlan")
-    net_bind_net = network.get("bind_net")
-    net_bind_type= network.get("bind_type")
-    name = network["name"]
-    
-    #check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
-    vlan_index =name.rfind(":")
-    if net_bind_net==None and net_bind_type==None and vlan_index > 1: 
-        try:
-            vlan_tag = int(name[vlan_index+1:])
-            if vlan_tag >0 and vlan_tag < 4096:
-                net_bind_net = name[:vlan_index]
-                net_bind_type = "vlan:" + name[vlan_index+1:]
-        except:
-            pass 
-        
-    if net_bind_net != None:
-        #look for a valid net
-        if check_valid_uuid(net_bind_net):
-            net_bind_key = "uuid"
-        else:
-            net_bind_key = "name"
-        result, content = my.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net} )
-        if result<0:
-            bottle.abort(HTTP_Internal_Server_Error, 'getting nets from db ' + content)
-            return
-        elif result==0:
-            bottle.abort(HTTP_Bad_Request, "bind_net %s '%s'not found" % (net_bind_key, net_bind_net) )
-            return
-        elif result>1:
-            bottle.abort(HTTP_Bad_Request, "more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net) )
-            return
-        network["bind_net"] = content[0]["uuid"]
-    if net_bind_type != None:
-        if net_bind_type[0:5] != "vlan:":
-            bottle.abort(HTTP_Bad_Request, "bad format for 'bind_type', must be 'vlan:<tag>'")
-            return
-        if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:])<=0 :
-            bottle.abort(HTTP_Bad_Request, "bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095")
-            return
-        network["bind_type"] = net_bind_type
-    
-    if net_provider!=None:
-        if net_provider[:9]=="openflow:":
-            if net_type!=None:
-                if net_type!="ptp" and net_type!="data":
-                    bottle.abort(HTTP_Bad_Request, "Only 'ptp' or 'data' net types can be bound to 'openflow'")
-            else:
-                net_type='data'
-        else:
-            if net_type!=None:
-                if net_type!="bridge_man" and net_type!="bridge_data":
-                    bottle.abort(HTTP_Bad_Request, "Only 'bridge_man' or 'bridge_data' net types can be bound to 'bridge', 'macvtap' or 'default")
-            else:
-                net_type='bridge_man'
-    
-    if net_type==None:
-        net_type='bridge_man' 
-        
-    if net_provider != None:
-        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
-                    if brnet[3] != None:
-                        bottle.abort(HTTP_Conflict, "invalid 'provider:physical', bridge '%s' is already used" % bridge_net_name)
-                        return
-                    bridge_net=brnet
-                    net_vlan = brnet[1]
-                    break
-#            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':
-        #look for a free precreated nets
-        for brnet in config_dic['bridge_nets']:
-            if brnet[3]==None: # free
-                if bridge_net != None:
-                    if net_type=='bridge_man': #look for the smaller speed
-                        if brnet[2] < bridge_net[2]:   bridge_net = brnet
-                    else: #look for the larger speed
-                        if brnet[2] > bridge_net[2]:   bridge_net = brnet
-                else:
-                    bridge_net = brnet
-                    net_vlan = brnet[1]
-        if bridge_net==None:
-            bottle.abort(HTTP_Bad_Request, "Max limits of bridge networks reached. Future versions of VIM will overcome this limit")
-            return
-        else:
-            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"):
-        net_vlan = my.db.get_free_net_vlan()
-        if net_vlan < 0:
-            bottle.abort(HTTP_Internal_Server_Error, "Error getting an available vlan")
-            return
-    
-    network['provider'] = net_provider
-    network['type']     = net_type
-    network['vlan']     = net_vlan
-    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"):
-            if network["name"] in config_dic["dhcp_server"].get("nets", () ):
-                config_dic["dhcp_nets"].append(content)
-                print "dhcp_server: add new net", content
-            elif bridge_net != None and bridge_net[0] in config_dic["dhcp_server"].get("bridge_ifaces", () ):
-                config_dic["dhcp_nets"].append(content)
-                print "dhcp_server: add new net", content
-        return http_get_network_id(content)
-    else:
-        print "http_post_networks error %d %s" % (result, content)
-        bottle.abort(-result, content)
-        return
+    """
+    Insert a network into the database.
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        # parse input data
+        http_content = format_in(network_new_schema )
+        r = remove_extra_items(http_content, network_new_schema)
+        if r is not None:
+            print "http_post_networks: Warning: remove extra items ", r
+        change_keys_http2db(http_content['network'], http2db_network)
+        network = http_content['network']
+        content = my.ovim.new_network(network)
+        return format_out(get_network_id(content))
+    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 + '/networks/<network_id>', method='PUT')
 def http_put_network_id(network_id):
-    '''update a network_id into the database.'''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #parse input data
-    http_content = format_in( network_update_schema )
-    r = remove_extra_items(http_content, network_update_schema)
-    change_keys_http2db(http_content['network'], http2db_network)
-    network=http_content['network']
-
-    #Look for the previous data
-    where_ = {'uuid': network_id}
-    result, network_old = my.db.get_table(FROM='nets', WHERE=where_)
-    if result < 0:
-        print "http_put_network_id error %d %s" % (result, network_old)
-        bottle.abort(-result, network_old)
-        return
-    elif result==0:
-        print "http_put_network_id network '%s' not found" % network_id
-        bottle.abort(HTTP_Not_Found, 'network %s not found' % network_id)
-        return
-    #get ports
-    nbports, content = my.db.get_table(FROM='ports', SELECT=('uuid as port_id',), 
-                                              WHERE={'net_id': network_id}, LIMIT=100)
-    if result < 0:
-        print "http_put_network_id error %d %s" % (result, network_old)
-        bottle.abort(-result, content)
-        return
-    if nbports>0:
-        if 'type' in network and network['type'] != network_old[0]['type']:
-            bottle.abort(HTTP_Method_Not_Allowed, "Can not change type of network while having ports attached")
-        if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
-            bottle.abort(HTTP_Method_Not_Allowed, "Can not change vlan of network while having ports attached")
-
-    #check valid params
-    net_provider = network.get('provider', network_old[0]['provider'])
-    net_type     = network.get('type', network_old[0]['type'])
-    net_bind_net = network.get("bind_net")
-    net_bind_type= network.get("bind_type")
-    if net_bind_net != None:
-        #look for a valid net
-        if check_valid_uuid(net_bind_net):
-            net_bind_key = "uuid"
-        else:
-            net_bind_key = "name"
-        result, content = my.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net} )
-        if result<0:
-            bottle.abort(HTTP_Internal_Server_Error, 'getting nets from db ' + content)
-            return
-        elif result==0:
-            bottle.abort(HTTP_Bad_Request, "bind_net %s '%s'not found" % (net_bind_key, net_bind_net) )
-            return
-        elif result>1:
-            bottle.abort(HTTP_Bad_Request, "more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net) )
-            return
-        network["bind_net"] = content[0]["uuid"]
-    if net_bind_type != None:
-        if net_bind_type[0:5] != "vlan:":
-            bottle.abort(HTTP_Bad_Request, "bad format for 'bind_type', must be 'vlan:<tag>'")
-            return
-        if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:])<=0 :
-            bottle.abort(HTTP_Bad_Request, "bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095")
-            return
-    if net_provider!=None:
-        if net_provider[:9]=="openflow:":
-            if net_type!="ptp" and net_type!="data":
-                bottle.abort(HTTP_Bad_Request, "Only 'ptp' or 'data' net types can be bound to 'openflow'")
-        else:
-            if net_type!="bridge_man" and net_type!="bridge_data":
-                    bottle.abort(HTTP_Bad_Request, "Only 'bridge_man' or 'bridge_data' net types can be bound to 'bridge', 'macvtap' or 'default")
+    """
+    Update a network_id into DB.
+    :param network_id: network id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+    
+    try:
+        # parse input data
+        http_content = format_in(network_update_schema)
+        change_keys_http2db(http_content['network'], http2db_network)
+        network = http_content['network']
+        return format_out(my.ovim.edit_network(network_id, network))
+
+    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))
 
-    #insert in data base
-    result, content = my.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True )
-    if result >= 0:
-        if result>0: # and nbports>0 and 'admin_state_up' in network and network['admin_state_up'] != network_old[0]['admin_state_up']:
-            r,c = config_dic['of_thread'].insert_task("update-net", network_id)
-            if r  < 0:
-                print "http_put_network_id error while launching openflow rules"
-                bottle.abort(HTTP_Internal_Server_Error, c)
-            if config_dic.get("dhcp_server"):
-                if network_id in config_dic["dhcp_nets"]:
-                    config_dic["dhcp_nets"].remove(network_id)
-                    print "dhcp_server: delete net", network_id
-                if network.get("name", network_old["name"]) in config_dic["dhcp_server"].get("nets", () ):
-                    config_dic["dhcp_nets"].append(network_id)
-                    print "dhcp_server: add new net", network_id
-                else:
-                    net_bind = network.get("bind", network_old["bind"] )
-                    if net_bind and net_bind[:7]=="bridge:" and net_bind[7:] in config_dic["dhcp_server"].get("bridge_ifaces", () ):
-                        config_dic["dhcp_nets"].append(network_id)
-                        print "dhcp_server: add new net", network_id
-        return http_get_network_id(network_id)
-    else:
-        bottle.abort(-result, content)
-        return
 
-  
 @bottle.route(url_base + '/networks/<network_id>', method='DELETE')
 def http_delete_network_id(network_id):
-    '''delete a network_id from the database.'''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
+    """
+    Delete a network_id from the database.
+    :param network_id: Network id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
 
-    #delete from the data base
-    result, content = my.db.delete_row('nets', network_id )
-   
-    if result == 0:
-        bottle.abort(HTTP_Not_Found, content)
-    elif result >0:
-        for brnet in config_dic['bridge_nets']:
-            if brnet[3]==network_id:
-                brnet[3]=None
-                break
-        if config_dic.get("dhcp_server") and network_id in config_dic["dhcp_nets"]:
-            config_dic["dhcp_nets"].remove(network_id)
-            print "dhcp_server: delete net", network_id
-        data={'result' : content}
+    try:
+        # delete from the data base
+        content = my.ovim.delete_network(network_id)
+        data = {'result': content}
         return format_out(data)
-    else:
-        print "http_delete_network_id error",result, content
-        bottle.abort(-result, content)
-        return
+
+    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))
+
 #
 # OPENFLOW
 #
+
+
+@bottle.route(url_base + '/openflow/controller', method='GET')
+def http_get_openflow_controller():
+    """
+    Retrieve a openflow controllers list from DB.
+    :return:
+    """
+    # TODO check if show a proper list
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_ofc,
+                                                      ('id', 'name', 'dpid', 'ip', 'port', 'type',
+                                                       'version', 'user', 'password'))
+
+        content = my.ovim.get_of_controllers(select_, where_)
+        delete_nulls(content)
+        change_keys_http2db(content, http2db_ofc, reverse=True)
+        data = {'ofcs': content}
+        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 + '/openflow/controller/<uuid>', method='GET')
+def http_get_openflow_controller_id(uuid):
+    """
+    Get an openflow controller by dpid from DB.get_of_controllers
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+
+        content = my.ovim.show_of_controller(uuid)
+        delete_nulls(content)
+        change_keys_http2db(content, http2db_ofc, reverse=True)
+        data = {'ofc': content}
+        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 + '/openflow/controller/', method='POST')
+def http_post_openflow_controller():
+    """
+    Create a new openflow controller into DB
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        http_content = format_in(openflow_controller_schema)
+        of_c = http_content['ofc']
+        uuid = my.ovim.new_of_controller(of_c)
+        content = my.ovim.show_of_controller(uuid)
+        delete_nulls(content)
+        change_keys_http2db(content, http2db_ofc, reverse=True)
+        data = {'ofc': content}
+        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 + '/openflow/controller/<of_controller_id>', method='PUT')
+def http_put_openflow_controller_by_id(of_controller_id):
+    """
+    Create an openflow controller into DB
+    :param of_controller_id: openflow controller dpid
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        http_content = format_in(openflow_controller_schema)
+        of_c = http_content['ofc']
+
+        content = my.ovim.edit_of_controller(of_controller_id, of_c)
+        delete_nulls(content)
+        change_keys_http2db(content, http2db_ofc, reverse=True)
+        data = {'ofc': content}
+        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 + '/openflow/controller/<of_controller_id>', method='DELETE')
+def http_delete_openflow_controller(of_controller_id):
+    """
+    Delete  an openflow controller from DB.
+    :param of_controller_id: openflow controller dpid
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        content = my.ovim.delete_of_controller(of_controller_id)
+        data = {'result': content}
+        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 + '/networks/<network_id>/openflow', method='GET')
 def http_get_openflow_id(network_id):
-    '''To obtain the list of openflow rules of a network
-    '''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
-    #ignore input data
-    if network_id=='all':
-        where_={}
-    else:
-        where_={"net_id": network_id}
-    result, content = my.db.get_table(SELECT=("name","net_id","priority","vlan_id","ingress_port","src_mac","dst_mac","actions"),
-            WHERE=where_, FROM='of_flows')
-    if result < 0:
-        bottle.abort(-result, content)
-        return
-    data={'openflow-rules' : content}
+    """
+    To obtain the list of openflow rules of a network
+    :param network_id: network id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    # ignore input data
+    if network_id == 'all':
+        network_id = None
+    try:
+        content = my.ovim.get_openflow_rules(network_id)
+        data = {'openflow-rules': content}
+    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))
+
     return format_out(data)
 
+
 @bottle.route(url_base + '/networks/<network_id>/openflow', method='PUT')
 def http_put_openflow_id(network_id):
-    '''To make actions over the net. The action is to reinstall the openflow rules
+    """
+    To make actions over the net. The action is to reinstall the openflow rules
     network_id can be 'all'
-    '''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
+    :param network_id: network id
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
     if not my.admin:
         bottle.abort(HTTP_Unauthorized, "Needed admin privileges")
-        return
-    #ignore input data
-    if network_id=='all':
-        where_={}
-    else:
-        where_={"uuid": network_id}
-    result, content = my.db.get_table(SELECT=("uuid","type"), WHERE=where_, FROM='nets')
-    if result < 0:
-        bottle.abort(-result, content)
-        return
-    
-    for net in content:
-        if net["type"]!="ptp" and net["type"]!="data":
-            result-=1
-            continue
-        r,c = config_dic['of_thread'].insert_task("update-net", net['uuid'])
-        if r  < 0:
-            print "http_put_openflow_id error while launching openflow rules"
-            bottle.abort(HTTP_Internal_Server_Error, c)
-    data={'result' : str(result)+" nets updates"}
+
+    if network_id == 'all':
+        network_id = None
+
+    try:
+        result = my.ovim.edit_openflow_rules(network_id)
+    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))
+
+    data = {'result': str(result) + " nets updates"}
     return format_out(data)
 
-@bottle.route(url_base + '/networks/openflow/clear', method='DELETE')
+@bottle.route(url_base + '/networks/clear/openflow/<ofc_id>', method='DELETE')
 @bottle.route(url_base + '/networks/clear/openflow', method='DELETE')
-def http_clear_openflow_rules():
-    '''To make actions over the net. The action is to delete ALL openflow rules
-    '''
-    my = config_dic['http_threads'][ threading.current_thread().name ]
+def http_clear_openflow_rules(ofc_id=None):
+    """
+    To make actions over the net. The action is to delete ALL openflow rules
+    :return:
+    """
+    my = config_dic['http_threads'][ threading.current_thread().name]
+
     if not my.admin:
         bottle.abort(HTTP_Unauthorized, "Needed admin privileges")
-        return
-    #ignore input data
-    r,c = config_dic['of_thread'].insert_task("clear-all")
-    if r  < 0:
-        print "http_delete_openflow_id error while launching openflow rules"
-        bottle.abort(HTTP_Internal_Server_Error, c)
-        return
-
-    data={'result' : " Clearing openflow rules in process"}
+    try:
+        my.ovim.delete_openflow_rules(ofc_id)
+    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))
+
+    data = {'result': " Clearing openflow rules in process"}
     return format_out(data)
 
+@bottle.route(url_base + '/networks/openflow/ports/<ofc_id>', method='GET')
 @bottle.route(url_base + '/networks/openflow/ports', method='GET')
-def http_get_openflow_ports():
-    '''Obtain switch ports names of openflow controller
-    '''
-    data={'ports' : config_dic['of_thread'].OF_connector.pp2ofi}
-    return format_out(data)
+def http_get_openflow_ports(ofc_id=None):
+    """
+    Obtain switch ports names of openflow controller
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
 
+    try:
+        ports = my.ovim.get_openflow_ports(ofc_id)
+        data = {'ports': ports}
+    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))
 
+    return format_out(data)
 #
 # PORTS
 #
 
+
 @bottle.route(url_base + '/ports', method='GET')
 def http_get_ports():
     #obtain data
@@ -1967,37 +2242,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():
@@ -2011,115 +2286,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':
-                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.'''
@@ -2127,30 +2341,90 @@ 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))
+
+
+@bottle.route(url_base + '/openflow/mapping', method='POST')
+def http_of_port_mapping():
+    """
+    Create new compute port mapping entry
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
 
-    #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 )
+    try:
+        http_content = format_in(of_port_map_new_schema)
+        r = remove_extra_items(http_content, of_port_map_new_schema)
+        if r is not None:
+            my.logger.error("http_of_port_mapping: Warning: remove extra items " + str(r), exc_info=True)
+
+        # insert in data base
+        port_mapping = my.ovim.set_of_port_mapping(http_content['of_port_mapings'])
+        change_keys_http2db(port_mapping, http2db_id, reverse=True)
+        delete_nulls(port_mapping)
+        data = {'of_port_mappings': port_mapping}
+        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 + '/openflow/mapping', method='GET')
+def get_of_port_mapping():
+    """
+    Get compute port mapping
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
 
-    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}
+    try:
+        select_, where_, limit_ = filter_query_string(bottle.request.query, http2db_id,
+                                                      ('id', 'ofc_id', 'region', 'compute_node', 'pci',
+                                                       'switch_dpid', 'switch_port', 'switch_mac'))
+        # insert in data base
+        port_mapping = my.ovim.get_of_port_mappings(select_, where_)
+        change_keys_http2db(port_mapping, http2db_id, reverse=True)
+        delete_nulls(port_mapping)
+        data = {'of_port_mappings': port_mapping}
         return format_out(data)
-    else:
-        print "http_delete_port_id error",result, content
-        bottle.abort(-result, content)
-    return
-    
+    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 + '/openflow/mapping/<region>', method='DELETE')
+def delete_of_port_mapping(region):
+    """
+    Insert a tenant into the database.
+    :return:
+    """
+    my = config_dic['http_threads'][threading.current_thread().name]
+
+    try:
+        # insert in data base
+        db_filter = {'region': region}
+        result = my.ovim.clear_of_port_mapping(db_filter)
+        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))
+