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