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