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