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