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