Bug 462 (Enhancement) - Add support for Xen and Unikernels
[osm/openvim.git] / osm_openvim / ovim.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 # This file is part of openvim
7 # All Rights Reserved.
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
23 ##
24
25 """
26 This is the thread for the http server North API.
27 Two thread will be launched, with normal and administrative permissions.
28 """
29
30 import threading
31 import yaml
32 import vim_db
33 import logging
34 # import imp
35 import os.path
36 import argparse
37 from netaddr import IPNetwork
38 from jsonschema import validate as js_v, exceptions as js_e
39 import host_thread as ht
40 import dhcp_thread as dt
41 import openflow_thread as oft
42 import openflow_conn
43
44 __author__ = "Alfonso Tierno, Leonardo Mirabal"
45 __date__ = "$06-Feb-2017 12:07:15$"
46 __version__ = "0.5.24-r542"
47 version_date = "Mar 2018"
48 database_version = 23 #needed database schema version
49
50 HTTP_Bad_Request = 400
51 HTTP_Unauthorized = 401
52 HTTP_Not_Found = 404
53 HTTP_Forbidden = 403
54 HTTP_Method_Not_Allowed = 405
55 HTTP_Not_Acceptable = 406
56 HTTP_Request_Timeout = 408
57 HTTP_Conflict = 409
58 HTTP_Service_Unavailable = 503
59 HTTP_Internal_Server_Error= 500
60
61
62 def convert_boolean(data, items):
63 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
64 It assumes that bandwidth is well formed
65 Attributes:
66 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
67 'items': tuple of keys to convert
68 Return:
69 None
70 '''
71 if type(data) is dict:
72 for k in data.keys():
73 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
74 convert_boolean(data[k], items)
75 if k in items:
76 if type(data[k]) is str:
77 if data[k] == "false":
78 data[k] = False
79 elif data[k] == "true":
80 data[k] = True
81 if type(data) is tuple or type(data) is list:
82 for k in data:
83 if type(k) is dict or type(k) is tuple or type(k) is list:
84 convert_boolean(k, items)
85
86
87
88 class ovimException(Exception):
89 def __init__(self, message, http_code=HTTP_Bad_Request):
90 self.http_code = http_code
91 Exception.__init__(self, message)
92
93
94 class ovim():
95 running_info = {} #TODO OVIM move the info of running threads from config_dic to this static variable
96 of_module = {}
97
98 def __init__(self, configuration):
99 self.config = configuration
100 self.logger_name = configuration.get("logger_name", "openvim")
101 self.logger = logging.getLogger(self.logger_name)
102 self.db = None
103 self.db = self._create_database_connection()
104 self.of_test_mode = False
105
106 def _create_database_connection(self):
107 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
108 self.logger_name + ".db", self.config.get('log_level_db'))
109 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
110 self.config['db_name']) == -1:
111 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
112 # self.config['db_host'])
113 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
114 self.config['db_user'],
115 self.config['db_host']) )
116 return db
117
118 @staticmethod
119 def get_version():
120 return __version__
121
122 @staticmethod
123 def get_version_date():
124 return version_date
125
126 @staticmethod
127 def get_database_version():
128 return database_version
129
130 @staticmethod
131 def _check_dhcp_data_integrity(network):
132 """
133 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
134 :param network: list with user nets paramters
135 :return:
136 """
137 if "cidr" in network:
138 cidr = network["cidr"]
139 ip_tools = IPNetwork(cidr)
140 cidr_len = ip_tools.prefixlen
141 if cidr_len > 29:
142 return False
143
144 ips = IPNetwork(cidr)
145 if "dhcp_first_ip" not in network:
146 network["dhcp_first_ip"] = str(ips[3])
147 if "dhcp_last_ip" not in network:
148 network["dhcp_last_ip"] = str(ips[-2])
149 if "gateway_ip" not in network:
150 network["gateway_ip"] = str(ips[1])
151
152 return True
153 else:
154 return False
155
156 @staticmethod
157 def _check_valid_uuid(uuid):
158 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
159 try:
160 js_v(uuid, id_schema)
161 return True
162 except js_e.ValidationError:
163 return False
164
165 def start_service(self):
166 """
167 Start ovim services
168 :return:
169 """
170 global database_version
171 # if self.running_info:
172 # return #TODO service can be checked and rebuild broken threads
173 r = self.db.get_db_version()
174 db_path = __file__
175 db_path = db_path[:db_path.rfind("/")]
176 if os.path.exists(db_path + "/database_utils/migrate_vim_db.sh"):
177 db_path += "/database_utils"
178 else:
179 db_path += "/../database_utils"
180
181 if r[0] < 0:
182 raise ovimException("DATABASE is not valid. If you think it is corrupted, you can init it with"
183 " '{db_path}/init_vim_db.sh' script".format(db_path=db_path))
184 elif r[0] != database_version:
185 raise ovimException("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
186 " with '{db_path}/migrate_vim_db.sh {target}'".format(
187 current=r[0], target=database_version, db_path=db_path))
188 self.logger.critical("Starting ovim server version: '{} {}' database version '{}'".format(
189 self.get_version(), self.get_version_date(), self.get_database_version()))
190 # create database connection for openflow threads
191 self.config["db"] = self._create_database_connection()
192 self.config["db_lock"] = threading.Lock()
193
194 self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
195
196 # Create one thread for each host
197 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
198 host_develop_mode = True if self.config['mode'] == 'development' else False
199 host_develop_bridge_iface = self.config.get('development_bridge', None)
200
201 # get host list from data base before starting threads
202 r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid', 'hypervisors', 'password', 'keyfile'),
203 FROM='hosts', WHERE={'status': 'ok'}) #Unikernels extension
204 if r < 0:
205 raise ovimException("Cannot get hosts from database {}".format(hosts))
206
207 self.config['host_threads'] = {}
208
209 for host in hosts:
210 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.config["db"],
211 password=host['password'],
212 keyfile=host.get('keyfile', self.config["host_ssh_keyfile"]),
213 db_lock=self.config["db_lock"], test=host_test_mode,
214 image_path=self.config['host_image_path'],
215 version=self.config['version'], host_id=host['uuid'],
216 develop_mode=host_develop_mode,
217 develop_bridge_iface=host_develop_bridge_iface,
218 hypervisors=host['hypervisors'], #Unikernels extension
219 logger_name=self.logger_name + ".host." + host['name'],
220 debug=self.config.get('log_level_host'))
221
222 try:
223 thread.check_connectivity()
224 except Exception as e:
225 self.logger.critical('Error detected for compute = {} with ip = {}'
226 .format(host['name'], host['ip_name']))
227 thread.start()
228 self.config['host_threads'][host['uuid']] = thread
229
230 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
231 # speed in Gbit/s
232
233 self.config['dhcp_nets'] = []
234 self.config['bridge_nets'] = []
235 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
236 # skip 'development_bridge'
237 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
238 continue
239 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
240
241 # check if this bridge is already used (present at database) for a network)
242 used_bridge_nets = []
243 for brnet in self.config['bridge_nets']:
244 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
245 if r > 0:
246 brnet[3] = nets[0]['uuid']
247 used_bridge_nets.append(brnet[0])
248 if self.config.get("dhcp_server"):
249 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
250 self.config['dhcp_nets'].append(nets[0]['uuid'])
251 if len(used_bridge_nets) > 0:
252 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
253 # get nets used by dhcp
254 if self.config.get("dhcp_server"):
255 for net in self.config["dhcp_server"].get("nets", ()):
256 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
257 if r > 0:
258 self.config['dhcp_nets'].append(nets[0]['uuid'])
259
260 # OFC default
261 self._start_ofc_default_task()
262
263 # OFC per tenant in DB
264 self._start_of_db_tasks()
265
266 # create dhcp_server thread
267 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
268 dhcp_params = self.config.get("dhcp_server")
269 if dhcp_params:
270 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
271 db=self.config["db"], db_lock=self.config["db_lock"],
272 logger_name=self.logger_name + ".dhcp",
273 debug=self.config.get('log_level_of'))
274 thread.start()
275 self.config['dhcp_thread'] = thread
276
277
278
279 # create ovs dhcp thread
280 result, content = self.db.get_table(FROM='nets')
281 if result < 0:
282 self.logger.error("http_get_ports Error %d %s", result, content)
283 raise ovimException(str(content), -result)
284
285 for net in content:
286 net_type = net['type']
287 if net['status'] != "INACTIVE" and (net_type == 'bridge_data' or net_type == 'bridge_man') and \
288 net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
289 try:
290 config_routes = net.get('routes')
291 if config_routes:
292 routes = yaml.safe_load(config_routes)
293 else:
294 routes = None
295
296 config_dns = net.get('dns')
297 if config_dns:
298 dns = yaml.safe_load(config_dns)
299 else:
300 dns = None
301
302 links = net.get('links')
303 if links:
304 links = yaml.safe_load(net.get('links'))
305 if net.get('enable_dhcp'):
306 self.launch_dhcp_server(net.get('vlan'),
307 net.get('dhcp_first_ip'),
308 net.get('dhcp_last_ip'),
309 net.get('cidr'),
310 net.get('gateway_ip'),
311 dns,
312 routes)
313 self.launch_link_bridge_to_ovs(net['vlan'], net.get('gateway_ip'), net.get('cidr'), links, routes)
314 if net["status"] == "ERROR":
315 self.db.update_rows("nets", UPDATE={"status": "ACTIVE", "last_error": None},
316 WHERE={"uuid": net["uuid"]})
317 except Exception as e:
318 self.logger.error("Fail at launching dhcp server for net_id='%s' net_name='%s': %s",
319 net["uuid"], net["name"], str(e))
320 self.db.update_rows("nets", UPDATE={"status": "ERROR",
321 "last_error": "Fail at launching dhcp server: " + str(e)},
322 WHERE={"uuid": net["uuid"]})
323
324 def _start_of_db_tasks(self):
325 """
326 Start ofc task for existing ofcs in database
327 :param db_of:
328 :param db_lock:
329 :return:
330 """
331 ofcs = self.get_of_controllers()
332
333 for ofc in ofcs:
334 of_conn = self._load_of_module(ofc)
335 # create ofc thread per of controller
336 self._create_ofc_task(ofc['uuid'], ofc['dpid'], of_conn)
337
338 def _create_ofc_task(self, ofc_uuid, dpid, of_conn):
339 """
340 Create an ofc thread for handle each sdn controllers
341 :param ofc_uuid: sdn controller uuid
342 :param dpid: sdn controller dpid
343 :param of_conn: OF_conn module
344 :return:
345 """
346 if 'ofcs_thread' not in self.config and 'ofcs_thread_dpid' not in self.config:
347 ofcs_threads = {}
348 ofcs_thread_dpid = []
349 else:
350 ofcs_threads = self.config['ofcs_thread']
351 ofcs_thread_dpid = self.config['ofcs_thread_dpid']
352
353 if ofc_uuid not in ofcs_threads:
354 ofc_thread = self._create_ofc_thread(of_conn, ofc_uuid)
355 if ofc_uuid == "Default":
356 self.config['of_thread'] = ofc_thread
357
358 ofcs_threads[ofc_uuid] = ofc_thread
359 self.config['ofcs_thread'] = ofcs_threads
360
361 ofcs_thread_dpid.append({dpid: ofc_thread})
362 self.config['ofcs_thread_dpid'] = ofcs_thread_dpid
363
364 def _start_ofc_default_task(self):
365 """
366 Create default ofc thread
367 """
368 if 'of_controller' not in self.config \
369 and 'of_controller_ip' not in self.config \
370 and 'of_controller_port' not in self.config \
371 and 'of_controller_dpid' not in self.config:
372 return
373
374 # OF THREAD
375 db_config = {}
376 db_config['ip'] = self.config.get('of_controller_ip')
377 db_config['port'] = self.config.get('of_controller_port')
378 db_config['dpid'] = self.config.get('of_controller_dpid')
379 db_config['type'] = self.config.get('of_controller')
380 db_config['user'] = self.config.get('of_user')
381 db_config['password'] = self.config.get('of_password')
382
383 # create connector to the openflow controller
384 # load other parameters starting by of_ from config dict in a temporal dict
385
386 of_conn = self._load_of_module(db_config)
387 # create openflow thread
388 self._create_ofc_task("Default", db_config['dpid'], of_conn)
389
390 def _load_of_module(self, db_config):
391 """
392 import python module for each SDN controller supported
393 :param db_config: SDN dn information
394 :return: Module
395 """
396 if not db_config:
397 raise ovimException("No module found it", HTTP_Internal_Server_Error)
398
399 module_info = None
400
401 try:
402 if self.of_test_mode:
403 return openflow_conn.OfTestConnector({"name": db_config['type'],
404 "dpid": db_config['dpid'],
405 "of_debug": self.config['log_level_of']})
406 temp_dict = {}
407
408 if db_config:
409 temp_dict['of_ip'] = db_config['ip']
410 temp_dict['of_port'] = db_config['port']
411 temp_dict['of_dpid'] = db_config['dpid']
412 temp_dict['of_controller'] = db_config['type']
413 temp_dict['of_user'] = db_config.get('user')
414 temp_dict['of_password'] = db_config.get('password')
415
416 temp_dict['of_debug'] = self.config['log_level_of']
417
418 if temp_dict['of_controller'] == 'opendaylight':
419 module = "ODL"
420 else:
421 module = temp_dict['of_controller']
422
423 if module not in ovim.of_module:
424 try:
425 pkg = __import__("osm_openvim." + module)
426 of_conn_module = getattr(pkg, module)
427 ovim.of_module[module] = of_conn_module
428 self.logger.debug("Module load from {}".format("osm_openvim." + module))
429 except Exception as e:
430 self.logger.error("Cannot open openflow controller module of type '%s'", module)
431 raise ovimException("Cannot open openflow controller of type module '{}'"
432 "Revise it is installed".format(module),
433 HTTP_Internal_Server_Error)
434 else:
435 of_conn_module = ovim.of_module[module]
436 return of_conn_module.OF_conn(temp_dict)
437 except Exception as e:
438 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
439 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e).__name__, str(e)),
440 HTTP_Internal_Server_Error)
441
442 def _create_ofc_thread(self, of_conn, ofc_uuid="Default"):
443 """
444 Create and launch a of thread
445 :return: thread obj
446 """
447 # create openflow thread
448
449 #if 'of_controller_nets_with_same_vlan' in self.config:
450 # ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
451 #else:
452 # ofc_net_same_vlan = False
453 ofc_net_same_vlan = False
454
455 thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.config["db"],
456 db_lock=self.config["db_lock"],
457 pmp_with_same_vlan=ofc_net_same_vlan,
458 logger_name=self.logger_name + ".ofc." + ofc_uuid,
459 debug=self.config.get('log_level_of'))
460 #r, c = thread.OF_connector.obtain_port_correspondence()
461 #if r < 0:
462 # raise ovimException("Cannot get openflow information %s", c)
463 thread.start()
464 return thread
465
466 def stop_service(self):
467 threads = self.config.get('host_threads', {})
468 if 'of_thread' in self.config:
469 threads['of'] = (self.config['of_thread'])
470 if 'ofcs_thread' in self.config:
471 ofcs_thread = self.config['ofcs_thread']
472 for ofc in ofcs_thread:
473 threads[ofc] = ofcs_thread[ofc]
474
475 if 'dhcp_thread' in self.config:
476 threads['dhcp'] = (self.config['dhcp_thread'])
477
478 for thread_id, thread in threads.items():
479 if thread_id == 'openvim_controller':
480 continue
481 thread.insert_task("exit")
482 for thread_id, thread in threads.items():
483 if thread_id == 'openvim_controller':
484 continue
485 thread.join()
486
487 def get_networks(self, columns=None, db_filter={}, limit=None):
488 """
489 Retreive networks available
490 :param columns: List with select query parameters
491 :param db_filter: List with where query parameters
492 :param limit: Query limit result
493 :return:
494 """
495 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=db_filter, LIMIT=limit)
496
497 if result < 0:
498 raise ovimException(str(content), -result)
499
500 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
501
502 return content
503
504 def show_network(self, network_id, db_filter={}):
505 """
506 Get network from DB by id
507 :param network_id: net Id
508 :param db_filter: List with where query parameters
509 :return:
510 """
511 # obtain data
512 if not network_id:
513 raise ovimException("Not network id was not found")
514 db_filter['uuid'] = network_id
515
516 result, content = self.db.get_table(FROM='nets', WHERE=db_filter, LIMIT=100)
517
518 if result < 0:
519 raise ovimException(str(content), -result)
520 elif result == 0:
521 raise ovimException("show_network network '%s' not found" % network_id, -result)
522 else:
523 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
524 # get ports from DB
525 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
526 WHERE={'net_id': network_id}, LIMIT=100)
527 if len(ports) > 0:
528 content[0]['ports'] = ports
529
530 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
531 return content[0]
532
533 def new_network(self, network):
534 """
535 Create a net in DB
536 :return:
537 """
538 tenant_id = network.get('tenant_id')
539
540 if tenant_id:
541 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
542 if result <= 0:
543 raise ovimException("set_network error, no tenant founded", -result)
544
545 bridge_net = None
546 # check valid params
547 net_provider = network.get('provider')
548 net_type = network.get('type')
549 net_vlan = network.get("vlan")
550 net_bind_net = network.get("bind_net")
551 net_bind_type = network.get("bind_type")
552 net_region = network.get("region")
553 name = network["name"]
554
555 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
556 vlan_index = name.rfind(":")
557 if not net_bind_net and not net_bind_type and vlan_index > 1:
558 try:
559 vlan_tag = int(name[vlan_index + 1:])
560 if not vlan_tag and vlan_tag < 4096:
561 net_bind_net = name[:vlan_index]
562 net_bind_type = "vlan:" + name[vlan_index + 1:]
563 except:
564 pass
565
566 if net_bind_net:
567 # look for a valid net
568 if self._check_valid_uuid(net_bind_net):
569 net_bind_key = "uuid"
570 else:
571 net_bind_key = "name"
572 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
573 if result < 0:
574 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
575 elif result == 0:
576 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
577 elif result > 1:
578 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
579 network["bind_net"] = content[0]["uuid"]
580
581 if net_bind_type:
582 if net_bind_type[0:5] != "vlan:":
583 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
584 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
585 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
586 HTTP_Bad_Request)
587 network["bind_type"] = net_bind_type
588
589 if net_provider:
590 if net_provider[:9] == "openflow:":
591 if net_type:
592 if net_type != "ptp" and net_type != "data":
593 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
594 HTTP_Bad_Request)
595 else:
596 net_type = 'data'
597 else:
598 if net_type:
599 if net_type != "bridge_man" and net_type != "bridge_data":
600 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
601 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
602 else:
603 net_type = 'bridge_man'
604
605 if not net_type:
606 net_type = 'bridge_man'
607
608 if net_provider:
609 if net_provider[:7] == 'bridge:':
610 # check it is one of the pre-provisioned bridges
611 bridge_net_name = net_provider[7:]
612 for brnet in self.config['bridge_nets']:
613 if brnet[0] == bridge_net_name: # free
614 if brnet[3]:
615 raise ovimException("invalid 'provider:physical', "
616 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
617 bridge_net = brnet
618 net_vlan = brnet[1]
619 break
620 # if bridge_net==None:
621 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
622 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
623 # return
624
625 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
626 # look for a free precreated nets
627 for brnet in self.config['bridge_nets']:
628 if not brnet[3]: # free
629 if not bridge_net:
630 if net_type == 'bridge_man': # look for the smaller speed
631 if brnet[2] < bridge_net[2]:
632 bridge_net = brnet
633 else: # look for the larger speed
634 if brnet[2] > bridge_net[2]:
635 bridge_net = brnet
636 else:
637 bridge_net = brnet
638 net_vlan = brnet[1]
639 if not bridge_net:
640 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
641 "will overcome this limit", HTTP_Bad_Request)
642 else:
643 self.logger.debug("using net " + bridge_net)
644 net_provider = "bridge:" + bridge_net[0]
645 net_vlan = bridge_net[1]
646 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
647 net_provider = 'OVS'
648 if not net_region:
649 if net_type == "data" or net_type == "ptp":
650 net_region = "__DATA__"
651 elif net_provider == "OVS":
652 net_region = "__OVS__"
653 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
654 net_vlan = self.db.get_free_net_vlan(net_region)
655 if net_vlan < 0:
656 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
657 if net_provider == 'OVS':
658 net_provider = 'OVS' + ":" + str(net_vlan)
659
660 network['provider'] = net_provider
661 network['type'] = net_type
662 network['vlan'] = net_vlan
663 network['region'] = net_region
664 dhcp_integrity = True
665 if network.get('enable_dhcp'):
666 dhcp_integrity = self._check_dhcp_data_integrity(network)
667
668 if network.get('links'):
669 network['links'] = yaml.safe_dump(network['links'], default_flow_style=True, width=256)
670 if network.get('dns'):
671 network['dns'] = yaml.safe_dump(network['dns'], default_flow_style=True, width=256)
672 if network.get('routes'):
673 network['routes'] = yaml.safe_dump(network['routes'], default_flow_style=True, width=256)
674
675 result, content = self.db.new_row('nets', network, True, True)
676 if result >= 0: # and dhcp_integrity:
677 if bridge_net:
678 bridge_net[3] = content
679 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
680 if network["name"] in self.config["dhcp_server"].get("nets", ()):
681 self.config["dhcp_nets"].append(content)
682 self.logger.debug("dhcp_server: add new net", content)
683 elif bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
684 self.config["dhcp_nets"].append(content)
685 self.logger.debug("dhcp_server: add new net", content, content)
686 return content
687 else:
688 raise ovimException("Error creating network: {}".format(content), -result)
689
690 # TODO kei change update->edit
691
692 def edit_network(self, network_id, network):
693 """
694 Update entwork data byt id
695 :return:
696 """
697 # Look for the previous data
698 where_ = {'uuid': network_id}
699 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
700 if result < 0:
701 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
702 elif result == 0:
703 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
704 # get ports
705 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
706 WHERE={'net_id': network_id}, LIMIT=100)
707 if result < 0:
708 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
709 if nbports > 0:
710 if 'type' in network and network['type'] != network_old[0]['type']:
711 raise ovimException("Can not change type of network while having ports attached",
712 HTTP_Method_Not_Allowed)
713 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
714 raise ovimException("Can not change vlan of network while having ports attached",
715 HTTP_Method_Not_Allowed)
716
717 # check valid params
718 net_provider = network.get('provider', network_old[0]['provider'])
719 net_type = network.get('type', network_old[0]['type'])
720 net_bind_net = network.get("bind_net")
721 net_bind_type = network.get("bind_type")
722 if net_bind_net:
723 # look for a valid net
724 if self._check_valid_uuid(net_bind_net):
725 net_bind_key = "uuid"
726 else:
727 net_bind_key = "name"
728 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
729 if result < 0:
730 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
731 elif result == 0:
732 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
733 elif result > 1:
734 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
735 HTTP_Bad_Request)
736 network["bind_net"] = content[0]["uuid"]
737 if net_bind_type:
738 if net_bind_type[0:5] != "vlan:":
739 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
740 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
741 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
742 HTTP_Bad_Request)
743 if net_provider:
744 if net_provider[:9] == "openflow:":
745 if net_type != "ptp" and net_type != "data":
746 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
747 else:
748 if net_type != "bridge_man" and net_type != "bridge_data":
749 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
750 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
751
752 # insert in data base
753 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
754 if result >= 0:
755 # if result > 0 and nbports>0 and 'admin_state_up' in network
756 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
757 if result > 0:
758
759 try:
760 if nbports:
761 self.net_update_ofc_thread(network_id)
762 except ovimException as e:
763 raise ovimException("Error while launching openflow rules in network '{}' {}"
764 .format(network_id, str(e)), HTTP_Internal_Server_Error)
765 except Exception as e:
766 raise ovimException("Error while launching openflow rules in network '{}' {}"
767 .format(network_id, str(e)), HTTP_Internal_Server_Error)
768
769 if self.config.get("dhcp_server"):
770 if network_id in self.config["dhcp_nets"]:
771 self.config["dhcp_nets"].remove(network_id)
772 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
773 self.config["dhcp_nets"].append(network_id)
774 else:
775 net_bind = network.get("bind_type", network_old[0]["bind_type"])
776 if net_bind and net_bind and net_bind[:7] == "bridge:" and net_bind[7:] in self.config["dhcp_server"].get(
777 "bridge_ifaces", ()):
778 self.config["dhcp_nets"].append(network_id)
779 return network_id
780 else:
781 raise ovimException(content, -result)
782
783 def delete_network(self, network_id):
784 """
785 Delete network by network id
786 :param network_id: network id
787 :return:
788 """
789 net_data = self.show_network(network_id)
790
791 # delete from the data base
792 result, content = self.db.delete_row('nets', network_id)
793
794 if result == 0:
795 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
796 elif result > 0:
797 for brnet in self.config['bridge_nets']:
798 if brnet[3] == network_id:
799 brnet[3] = None
800 break
801 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
802 self.config["dhcp_nets"].remove(network_id)
803
804 if net_data.get('enable_dhcp'):
805 dhcp_path = self.config['ovs_controller_file_path']
806 dhcp_controller = self.get_dhcp_controller()
807 dhcp_controller.delete_dhcp_server(net_data['vlan'], network_id, dhcp_path)
808 dhcp_controller.delete_dhcp_port(net_data['vlan'], network_id, dhcp_path)
809 links = yaml.load(net_data.get('links'))
810 if links:
811 links = yaml.load(net_data.get('links'))
812 self.delete_link_bridge_to_ovs(net_data['vlan'], links)
813
814 return content
815 else:
816 raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
817
818 def get_openflow_rules(self, network_id=None):
819 """
820 Get openflow id from DB
821 :param network_id: Network id, if none all networks will be retrieved
822 :return: Return a list with Openflow rules per net
823 """
824 # ignore input data
825 if not network_id:
826 where_ = {}
827 else:
828 where_ = {"net_id": network_id}
829 result, content = self.db.get_table(
830 SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
831 WHERE=where_, FROM='of_flows')
832
833 if result < 0:
834 raise ovimException(str(content), -result)
835 return content
836
837 def edit_openflow_rules(self, network_id=None):
838
839 """
840 To make actions over the net. The action is to reinstall the openflow rules
841 network_id can be 'all'
842 :param network_id: Network id, if none all networks will be retrieved
843 :return : Number of nets updated
844 """
845
846 # ignore input data
847 if not network_id:
848 where_ = {}
849 else:
850 where_ = {"uuid": network_id}
851 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
852
853 if result < 0:
854 raise ovimException(str(content), -result)
855
856 for net in content:
857 if net["type"] != "ptp" and net["type"] != "data":
858 result -= 1
859 continue
860
861 try:
862 self.net_update_ofc_thread(net['uuid'])
863 except ovimException as e:
864 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
865 HTTP_Internal_Server_Error)
866 except Exception as e:
867 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
868 HTTP_Internal_Server_Error)
869
870 return result
871
872 def delete_openflow_rules(self, ofc_id=None):
873 """
874 To make actions over the net. The action is to delete ALL openflow rules
875 :return: return operation result
876 """
877
878 if not ofc_id:
879 if 'Default' in self.config['ofcs_thread']:
880 r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
881 else:
882 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
883
884 elif ofc_id in self.config['ofcs_thread']:
885 r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
886
887 # ignore input data
888 if r < 0:
889 raise ovimException(str(c), -r)
890 else:
891 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
892 return r
893
894 def get_openflow_ports(self, ofc_id=None):
895 """
896 Obtain switch ports names of openflow controller
897 :return: Return flow ports in DB
898 """
899 if not ofc_id:
900 if 'Default' in self.config['ofcs_thread']:
901 conn = self.config['ofcs_thread']['Default'].OF_connector
902 else:
903 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
904
905 elif ofc_id in self.config['ofcs_thread']:
906 conn = self.config['ofcs_thread'][ofc_id].OF_connector
907 else:
908 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
909 return conn.pp2ofi
910
911 def get_ports(self, columns=None, filter={}, limit=None):
912 # result, content = my.db.get_ports(where_)
913 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
914 if result < 0:
915 self.logger.error("http_get_ports Error %d %s", result, content)
916 raise ovimException(str(content), -result)
917 else:
918 convert_boolean(content, ('admin_state_up',))
919 return content
920
921 def new_port(self, port_data):
922 port_data['type'] = 'external'
923 if port_data.get('net_id'):
924 # check that new net has the correct type
925 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
926 if result < 0:
927 raise ovimException(str(new_net), -result)
928 # insert in data base
929 result, uuid = self.db.new_row('ports', port_data, True, True)
930 if result > 0:
931 if 'net_id' in port_data:
932 try:
933 self.net_update_ofc_thread(port_data['net_id'])
934 except ovimException as e:
935 raise ovimException("Cannot insert a task for updating network '{}' {}"
936 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
937 except Exception as e:
938 raise ovimException("Cannot insert a task for updating network '{}' {}"
939 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
940
941 return uuid
942 else:
943 raise ovimException(str(uuid), -result)
944
945 def new_external_port(self, port_data):
946 """
947 Create new external port and check port mapping correspondence
948 :param port_data: port_data = {
949 'region': 'datacenter region',
950 'compute_node': 'compute node id',
951 'pci': 'pci port address',
952 'vlan': 'net vlan',
953 'net_id': 'net id',
954 'tenant_id': 'tenant id',
955 'mac': 'switch mac',
956 'name': 'port name'
957 'ip_address': 'ip address - optional'}
958 :return:
959 """
960
961 port_data['type'] = 'external'
962
963 if port_data.get('net_id'):
964 # check that new net has the correct type
965 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
966 if result < 0:
967 raise ovimException(str(new_net), -result)
968 # insert in data base
969 db_filter = {}
970
971 if port_data.get('region'):
972 db_filter['region'] = port_data['region']
973 if port_data.get('pci'):
974 db_filter['pci'] = port_data['pci']
975 if port_data.get('compute_node'):
976 db_filter['compute_node'] = port_data['compute_node']
977
978 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
979 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
980
981 if not len(port_mapping_data):
982 raise ovimException("No port mapping founded for '{}'".format(str(db_filter)),
983 HTTP_Not_Found)
984 elif len(port_mapping_data) > 1:
985 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
986 HTTP_Conflict)
987
988 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
989 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
990 port_data['switch_port'] = port_mapping_data[0]['switch_port']
991 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
992
993 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
994 if 'region' in port_data:
995 del port_data['region']
996 if 'pci' in port_data:
997 del port_data['pci']
998 if 'compute_node' in port_data:
999 del port_data['compute_node']
1000
1001 result, uuid = self.db.new_row('ports', port_data, True, True)
1002 if result > 0:
1003 try:
1004 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
1005 except ovimException as e:
1006 raise ovimException("Cannot insert a task for updating network '{}' {}".
1007 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
1008 except Exception as e:
1009 raise ovimException("Cannot insert a task for updating network '{}' {}"
1010 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
1011 return uuid
1012 else:
1013 raise ovimException(str(uuid), -result)
1014
1015 def net_update_ofc_thread(self, net_id, ofc_id=None, switch_dpid=None):
1016 """
1017 Insert a update net task by net id or ofc_id for each ofc thread
1018 :param net_id: network id
1019 :param ofc_id: openflow controller id
1020 :param switch_dpid: switch dpid
1021 :return:
1022 """
1023 if not net_id:
1024 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
1025
1026 r = -1
1027 c = 'No valid ofc_id or switch_dpid received'
1028
1029 if not ofc_id:
1030 ports = self.get_ports(filter={"net_id": net_id})
1031 for port in ports:
1032 port_ofc_id = port.get('ofc_id', None)
1033 if port_ofc_id:
1034 ofc_id = port['ofc_id']
1035 switch_dpid = port['switch_dpid']
1036 break
1037 #TODO if not ofc_id: look at database table ofcs
1038
1039
1040 # If no ofc_id found it, default ofc_id is used.
1041 if not ofc_id and not switch_dpid:
1042 ofc_id = "Default"
1043
1044 if ofc_id and ofc_id in self.config['ofcs_thread']:
1045 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
1046 elif switch_dpid:
1047
1048 ofcs_dpid_list = self.config['ofcs_thread_dpid']
1049 for ofc_t in ofcs_dpid_list:
1050 if switch_dpid in ofc_t:
1051 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
1052
1053 if r < 0:
1054 message = "Cannot insert a task for updating network '{}', {}".format(net_id, c)
1055 self.logger.error(message)
1056 raise ovimException(message, HTTP_Internal_Server_Error)
1057
1058 def delete_port(self, port_id):
1059 # Look for the previous port data
1060 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
1061 if result < 0:
1062 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
1063 # delete from the data base
1064 result, content = self.db.delete_row('ports', port_id)
1065 if result == 0:
1066 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1067 elif result < 0:
1068 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
1069 # update network
1070 network = ports[0].get('net_id', None)
1071 if network:
1072 # change of net.
1073
1074 try:
1075 self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
1076 except ovimException as e:
1077 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1078 HTTP_Internal_Server_Error)
1079 except Exception as e:
1080 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1081 HTTP_Internal_Server_Error)
1082
1083 return content
1084
1085 def edit_port(self, port_id, port_data, admin=True):
1086 # Look for the previous port data
1087 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
1088 if result < 0:
1089 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
1090 elif result == 0:
1091 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1092 port = content[0]
1093 nets = []
1094 host_id = None
1095 result = 1
1096 if 'net_id' in port_data:
1097 # change of net.
1098 old_net = port.get('net_id', None)
1099 new_net = port_data['net_id']
1100 if old_net != new_net:
1101
1102 if new_net:
1103 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
1104 if old_net:
1105 nets.append(old_net)
1106 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
1107 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
1108 elif port['type'] == 'external' and not admin:
1109 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
1110 if new_net:
1111 # check that new net has the correct type
1112 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
1113 if result < 0:
1114 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
1115 # change VLAN for SR-IOV ports
1116 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
1117 if new_net:
1118 port_data["vlan"] = None
1119 else:
1120 port_data["vlan"] = new_net_dict["vlan"]
1121 # get host where this VM is allocated
1122 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
1123 if result > 0:
1124 host_id = content[0]["host_id"]
1125
1126 # insert in data base
1127 if result >= 0:
1128 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
1129 port.update(port_data)
1130
1131 # Insert task to complete actions
1132 if result > 0:
1133 for net_id in nets:
1134 try:
1135 self.net_update_ofc_thread(net_id, port["ofc_id"], switch_dpid=port["switch_dpid"])
1136 except ovimException as e:
1137 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1138 HTTP_Internal_Server_Error)
1139 except Exception as e:
1140 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1141 HTTP_Internal_Server_Error)
1142
1143 if host_id:
1144 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1145 if r < 0:
1146 self.logger.error("Error updating network '{}' {}".format(r,v))
1147 # TODO Do something if fails
1148 if result >= 0:
1149 return port_id
1150 else:
1151 raise ovimException("Error {}".format(content), http_code=-result)
1152
1153 def new_of_controller(self, ofc_data):
1154 """
1155 Create a new openflow controller into DB
1156 :param ofc_data: Dict openflow controller data
1157 :return: openflow controller dpid
1158 """
1159
1160 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
1161 if result < 0:
1162 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1163
1164 ofc_data['uuid'] = ofc_uuid
1165 of_conn = self._load_of_module(ofc_data)
1166 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1167
1168 return ofc_uuid
1169
1170 def edit_of_controller(self, of_id, ofc_data):
1171 """
1172 Edit an openflow controller entry from DB
1173 :return:
1174 """
1175 if not ofc_data:
1176 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1177
1178 old_of_controller = self.show_of_controller(of_id)
1179
1180 if old_of_controller:
1181 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1182 if result >= 0:
1183 return ofc_data
1184 else:
1185 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1186 http_code=-result)
1187 else:
1188 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1189 http_code=HTTP_Internal_Server_Error)
1190
1191 def delete_of_controller(self, of_id):
1192 """
1193 Delete an openflow controller from DB.
1194 :param of_id: openflow controller dpid
1195 :return:
1196 """
1197
1198 ofc = self.show_of_controller(of_id)
1199
1200 result, content = self.db.delete_row("ofcs", of_id)
1201 if result < 0:
1202 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1203 elif result == 0:
1204 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
1205
1206 ofc_thread = self.config['ofcs_thread'][of_id]
1207 del self.config['ofcs_thread'][of_id]
1208 for ofc_th in self.config['ofcs_thread_dpid']:
1209 if ofc['dpid'] in ofc_th:
1210 self.config['ofcs_thread_dpid'].remove(ofc_th)
1211
1212 ofc_thread.insert_task("exit")
1213 #ofc_thread.join()
1214
1215 return content
1216
1217 def show_of_controller(self, uuid):
1218 """
1219 Show an openflow controller by dpid from DB.
1220 :param db_filter: List with where query parameters
1221 :return:
1222 """
1223
1224 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1225
1226 if result == 0:
1227 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1228 http_code=HTTP_Not_Found)
1229 elif result < 0:
1230 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1231 http_code=HTTP_Internal_Server_Error)
1232 return content[0]
1233
1234 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
1235 """
1236 Show an openflow controllers from DB.
1237 :param columns: List with SELECT query parameters
1238 :param db_filter: List with where query parameters
1239 :param limit: result Limit
1240 :return:
1241 """
1242 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
1243
1244 if result < 0:
1245 raise ovimException(str(content), -result)
1246
1247 return content
1248
1249 def get_tenants(self, columns=None, db_filter={}, limit=None):
1250 """
1251 Retrieve tenant list from DB
1252 :param columns: List with SELECT query parameters
1253 :param db_filter: List with where query parameters
1254 :param limit: result limit
1255 :return:
1256 """
1257 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1258 if result < 0:
1259 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1260 else:
1261 convert_boolean(content, ('enabled',))
1262 return content
1263
1264 def show_tenant_id(self, tenant_id):
1265 """
1266 Get tenant from DB by id
1267 :param tenant_id: tenant id
1268 :return:
1269 """
1270 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1271 WHERE={"uuid": tenant_id})
1272 if result < 0:
1273 raise ovimException(str(content), -result)
1274 elif result == 0:
1275 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1276 else:
1277 convert_boolean(content, ('enabled',))
1278 return content[0]
1279
1280 def new_tentant(self, tenant):
1281 """
1282 Create a tenant and store in DB
1283 :param tenant: Dictionary with tenant data
1284 :return: the uuid of created tenant. Raise exception upon error
1285 """
1286
1287 # insert in data base
1288 result, tenant_uuid = self.db.new_tenant(tenant)
1289
1290 if result >= 0:
1291 return tenant_uuid
1292 else:
1293 raise ovimException(str(tenant_uuid), -result)
1294
1295 def delete_tentant(self, tenant_id):
1296 """
1297 Delete a tenant from the database.
1298 :param tenant_id: Tenant id
1299 :return: delete tenant id
1300 """
1301
1302 # check permissions
1303 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1304 WHERE={'tenant_id': tenant_id})
1305 if r <= 0:
1306 tenants_flavors = ()
1307 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1308 WHERE={'tenant_id': tenant_id})
1309 if r <= 0:
1310 tenants_images = ()
1311
1312 result, content = self.db.delete_row('tenants', tenant_id)
1313 if result == 0:
1314 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1315 elif result > 0:
1316 for flavor in tenants_flavors:
1317 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1318 for image in tenants_images:
1319 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1320 return content
1321 else:
1322 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1323
1324 def edit_tenant(self, tenant_id, tenant_data):
1325 """
1326 Update a tenant data identified by tenant id
1327 :param tenant_id: tenant id
1328 :param tenant_data: Dictionary with tenant data
1329 :return:
1330 """
1331
1332 # Look for the previous data
1333 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1334 if result < 0:
1335 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1336 HTTP_Internal_Server_Error)
1337 elif result == 0:
1338 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1339
1340 # insert in data base
1341 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1342 if result >= 0:
1343 return content
1344 else:
1345 raise ovimException(str(content), -result)
1346
1347 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1348 """
1349 Create new port mapping entry
1350 :param of_maps: List with port mapping information
1351 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1352 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1353 :param ofc_id: ofc id
1354 :param switch_dpid: switch dpid
1355 :param region: datacenter region id
1356 :return:
1357 """
1358
1359 for map in of_maps:
1360 if ofc_id:
1361 map['ofc_id'] = ofc_id
1362 if switch_dpid:
1363 map['switch_dpid'] = switch_dpid
1364 if region:
1365 map['region'] = region
1366 if map.get("pci"):
1367 map["pci"] = map["pci"].lower()
1368
1369 for of_map in of_maps:
1370 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1371 if result > 0:
1372 of_map["uuid"] = uuid
1373 else:
1374 raise ovimException(str(uuid), -result)
1375 return of_maps
1376
1377 def clear_of_port_mapping(self, db_filter={}):
1378 """
1379 Clear port mapping filtering using db_filter dict
1380 :param db_filter: Parameter to filter during remove process
1381 :return:
1382 """
1383 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1384 # delete_row_by_key
1385 if result >= 0:
1386 return content
1387 else:
1388 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1389 HTTP_Internal_Server_Error)
1390
1391 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1392 """
1393 Retrive port mapping from DB
1394 :param column:
1395 :param db_filter:
1396 :return:
1397 """
1398 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1399
1400 if result < 0:
1401 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1402 raise ovimException(str(content), -result)
1403 else:
1404 return content
1405
1406 def get_dhcp_controller(self):
1407 """
1408 Create an host_thread object for manage openvim controller and not create a thread for itself
1409 :return: dhcp_host openvim controller object
1410 """
1411
1412 if 'openvim_controller' in self.config['host_threads']:
1413 return self.config['host_threads']['openvim_controller']
1414
1415 bridge_ifaces = []
1416 controller_ip = self.config['ovs_controller_ip']
1417 ovs_controller_user = self.config.get('ovs_controller_user')
1418
1419 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1420 host_develop_mode = True if self.config['mode'] == 'development' else False
1421
1422 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
1423 password=self.config.get('ovs_controller_password'),
1424 keyfile=self.config.get('ovs_controller_keyfile'),
1425 db=self.config["db"], db_lock=self.config["db_lock"], test=host_test_mode,
1426 image_path=self.config['host_image_path'], version=self.config['version'],
1427 host_id='openvim_controller', develop_mode=host_develop_mode,
1428 develop_bridge_iface=bridge_ifaces,
1429 logger_name=self.logger_name + ".host.controller",
1430 debug=self.config.get('log_level_host'))
1431 # dhcp_host.start()
1432 self.config['host_threads']['openvim_controller'] = dhcp_host
1433 try:
1434 dhcp_host.check_connectivity()
1435 except Exception as e:
1436 pass
1437
1438 return dhcp_host
1439
1440 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway, dns, routes):
1441 """
1442 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1443 :param vlan: vlan identifier
1444 :param first_ip: First dhcp range ip
1445 :param last_ip: Last dhcp range ip
1446 :param cidr: net cidr
1447 :param gateway: net gateway
1448 :return:
1449 """
1450 ip_tools = IPNetwork(cidr)
1451 dhcp_netmask = str(ip_tools.netmask)
1452 ip_range = [first_ip, last_ip]
1453
1454 dhcp_path = self.config['ovs_controller_file_path']
1455
1456 controller_host = self.get_dhcp_controller()
1457
1458 # controller_host.create_linux_bridge(vlan)
1459 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
1460 dhcp_path = self.config['ovs_controller_file_path']
1461 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway, dns, routes)
1462
1463 def launch_link_bridge_to_ovs(self, vlan, gateway, dhcp_cidr, links=None, routes=None):
1464 """
1465 Launch creating of connections (veth) between user bridge (link) and OVS
1466 :param vlan:
1467 :param gateway:
1468 :param links:
1469 :return:
1470 """
1471
1472 if links:
1473 controller_host = self.get_dhcp_controller()
1474 for link in links:
1475 if 'iface' in link and 'nat' not in link:
1476 controller_host.create_link_bridge_to_ovs(vlan, link['iface'])
1477 elif 'nat' in link:
1478 controller_host.create_qrouter_ovs_connection(vlan, gateway, dhcp_cidr)
1479 controller_host.create_qrouter_br_connection(vlan, dhcp_cidr, link)
1480
1481 if len(routes):
1482 controller_host.add_ns_routes(vlan, routes)
1483
1484 def delete_link_bridge_to_ovs(self, vlan, links=None):
1485 """
1486 Delete connections (veth) between user bridge (link) and OVS
1487 :param vlan:
1488 :param links:
1489 :return:
1490 """
1491 if links:
1492 controller_host = self.get_dhcp_controller()
1493
1494 for link in links:
1495 if 'iface' in link and 'nat' not in link:
1496 controller_host.remove_link_bridge_to_ovs(vlan, link['iface'])
1497 elif 'nat' in link:
1498 controller_host.delete_qrouter_connection(vlan, link['iface'])
1499
1500
1501 if __name__ == "__main__":
1502
1503 parser = argparse.ArgumentParser()
1504 parser.add_argument("-v","--version", help="show ovim library version", action="store_true")
1505 parser.add_argument("--database-version", help="show required database version", action="store_true")
1506 args = parser.parse_args()
1507 if args.version:
1508 print ('openvimd version {} {}'.format(ovim.get_version(), ovim.get_version_date()))
1509 print ('(c) Copyright Telefonica')
1510 elif args.database_version:
1511 print ('required database version: {}'.format(ovim.get_database_version()))
1512