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