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