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