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