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