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