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