Fix minor service start in ovim
[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['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 'enable_dhcp' in network and network['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 posting network", HTTP_Internal_Server_Error)
688 # TODO kei change update->edit
689
690 def edit_network(self, network_id, network):
691 """
692 Update entwork data byt id
693 :return:
694 """
695 # Look for the previous data
696 where_ = {'uuid': network_id}
697 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
698 if result < 0:
699 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
700 elif result == 0:
701 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
702 # get ports
703 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
704 WHERE={'net_id': network_id}, LIMIT=100)
705 if result < 0:
706 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
707 if nbports > 0:
708 if 'type' in network and network['type'] != network_old[0]['type']:
709 raise ovimException("Can not change type of network while having ports attached",
710 HTTP_Method_Not_Allowed)
711 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
712 raise ovimException("Can not change vlan of network while having ports attached",
713 HTTP_Method_Not_Allowed)
714
715 # check valid params
716 net_provider = network.get('provider', network_old[0]['provider'])
717 net_type = network.get('type', network_old[0]['type'])
718 net_bind_net = network.get("bind_net")
719 net_bind_type = network.get("bind_type")
720 if net_bind_net:
721 # look for a valid net
722 if self._check_valid_uuid(net_bind_net):
723 net_bind_key = "uuid"
724 else:
725 net_bind_key = "name"
726 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
727 if result < 0:
728 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
729 elif result == 0:
730 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
731 elif result > 1:
732 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
733 HTTP_Bad_Request)
734 network["bind_net"] = content[0]["uuid"]
735 if net_bind_type:
736 if net_bind_type[0:5] != "vlan:":
737 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
738 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
739 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
740 HTTP_Bad_Request)
741 if net_provider:
742 if net_provider[:9] == "openflow:":
743 if net_type != "ptp" and net_type != "data":
744 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
745 else:
746 if net_type != "bridge_man" and net_type != "bridge_data":
747 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
748 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
749
750 # insert in data base
751 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
752 if result >= 0:
753 # if result > 0 and nbports>0 and 'admin_state_up' in network
754 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
755 if result > 0:
756
757 try:
758 if nbports:
759 self.net_update_ofc_thread(network_id)
760 except ovimException as e:
761 raise ovimException("Error while launching openflow rules in network '{}' {}"
762 .format(network_id, str(e)), HTTP_Internal_Server_Error)
763 except Exception as e:
764 raise ovimException("Error while launching openflow rules in network '{}' {}"
765 .format(network_id, str(e)), HTTP_Internal_Server_Error)
766
767 if self.config.get("dhcp_server"):
768 if network_id in self.config["dhcp_nets"]:
769 self.config["dhcp_nets"].remove(network_id)
770 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
771 self.config["dhcp_nets"].append(network_id)
772 else:
773 net_bind = network.get("bind_type", network_old[0]["bind_type"])
774 if net_bind and net_bind and net_bind[:7] == "bridge:" and net_bind[7:] in self.config["dhcp_server"].get(
775 "bridge_ifaces", ()):
776 self.config["dhcp_nets"].append(network_id)
777 return network_id
778 else:
779 raise ovimException(content, -result)
780
781 def delete_network(self, network_id):
782 """
783 Delete network by network id
784 :param network_id: network id
785 :return:
786 """
787 net_data = self.show_network(network_id)
788
789 # delete from the data base
790 result, content = self.db.delete_row('nets', network_id)
791
792 if result == 0:
793 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
794 elif result > 0:
795 for brnet in self.config['bridge_nets']:
796 if brnet[3] == network_id:
797 brnet[3] = None
798 break
799 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
800 self.config["dhcp_nets"].remove(network_id)
801
802 if net_data.get('enable_dhcp'):
803 dhcp_path = self.config['ovs_controller_file_path']
804 dhcp_controller = self.get_dhcp_controller()
805 dhcp_controller.delete_dhcp_server(net_data['vlan'], network_id, dhcp_path)
806 dhcp_controller.delete_dhcp_port(net_data['vlan'], network_id, dhcp_path)
807 links = yaml.load(net_data.get('links'))
808 if links:
809 links = yaml.load(net_data.get('links'))
810 self.delete_link_bridge_to_ovs(net_data['vlan'], links)
811
812 return content
813 else:
814 raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
815
816 def get_openflow_rules(self, network_id=None):
817 """
818 Get openflow id from DB
819 :param network_id: Network id, if none all networks will be retrieved
820 :return: Return a list with Openflow rules per net
821 """
822 # ignore input data
823 if not network_id:
824 where_ = {}
825 else:
826 where_ = {"net_id": network_id}
827 result, content = self.db.get_table(
828 SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
829 WHERE=where_, FROM='of_flows')
830
831 if result < 0:
832 raise ovimException(str(content), -result)
833 return content
834
835 def edit_openflow_rules(self, network_id=None):
836
837 """
838 To make actions over the net. The action is to reinstall the openflow rules
839 network_id can be 'all'
840 :param network_id: Network id, if none all networks will be retrieved
841 :return : Number of nets updated
842 """
843
844 # ignore input data
845 if not network_id:
846 where_ = {}
847 else:
848 where_ = {"uuid": network_id}
849 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
850
851 if result < 0:
852 raise ovimException(str(content), -result)
853
854 for net in content:
855 if net["type"] != "ptp" and net["type"] != "data":
856 result -= 1
857 continue
858
859 try:
860 self.net_update_ofc_thread(net['uuid'])
861 except ovimException as e:
862 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
863 HTTP_Internal_Server_Error)
864 except Exception as e:
865 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
866 HTTP_Internal_Server_Error)
867
868 return result
869
870 def delete_openflow_rules(self, ofc_id=None):
871 """
872 To make actions over the net. The action is to delete ALL openflow rules
873 :return: return operation result
874 """
875
876 if not ofc_id:
877 if 'Default' in self.config['ofcs_thread']:
878 r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
879 else:
880 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
881
882 elif ofc_id in self.config['ofcs_thread']:
883 r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
884
885 # ignore input data
886 if r < 0:
887 raise ovimException(str(c), -r)
888 else:
889 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
890 return r
891
892 def get_openflow_ports(self, ofc_id=None):
893 """
894 Obtain switch ports names of openflow controller
895 :return: Return flow ports in DB
896 """
897 if not ofc_id:
898 if 'Default' in self.config['ofcs_thread']:
899 conn = self.config['ofcs_thread']['Default'].OF_connector
900 else:
901 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
902
903 elif ofc_id in self.config['ofcs_thread']:
904 conn = self.config['ofcs_thread'][ofc_id].OF_connector
905 else:
906 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
907 return conn.pp2ofi
908
909 def get_ports(self, columns=None, filter={}, limit=None):
910 # result, content = my.db.get_ports(where_)
911 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
912 if result < 0:
913 self.logger.error("http_get_ports Error %d %s", result, content)
914 raise ovimException(str(content), -result)
915 else:
916 convert_boolean(content, ('admin_state_up',))
917 return content
918
919 def new_port(self, port_data):
920 port_data['type'] = 'external'
921 if port_data.get('net_id'):
922 # check that new net has the correct type
923 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
924 if result < 0:
925 raise ovimException(str(new_net), -result)
926 # insert in data base
927 result, uuid = self.db.new_row('ports', port_data, True, True)
928 if result > 0:
929 if 'net_id' in port_data:
930 try:
931 self.net_update_ofc_thread(port_data['net_id'])
932 except ovimException as e:
933 raise ovimException("Cannot insert a task for updating network '{}' {}"
934 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
935 except Exception as e:
936 raise ovimException("Cannot insert a task for updating network '{}' {}"
937 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
938
939 return uuid
940 else:
941 raise ovimException(str(uuid), -result)
942
943 def new_external_port(self, port_data):
944 """
945 Create new external port and check port mapping correspondence
946 :param port_data: port_data = {
947 'region': 'datacenter region',
948 'compute_node': 'compute node id',
949 'pci': 'pci port address',
950 'vlan': 'net vlan',
951 'net_id': 'net id',
952 'tenant_id': 'tenant id',
953 'mac': 'switch mac',
954 'name': 'port name'
955 'ip_address': 'ip address - optional'}
956 :return:
957 """
958
959 port_data['type'] = 'external'
960
961 if port_data.get('net_id'):
962 # check that new net has the correct type
963 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
964 if result < 0:
965 raise ovimException(str(new_net), -result)
966 # insert in data base
967 db_filter = {}
968
969 if port_data.get('region'):
970 db_filter['region'] = port_data['region']
971 if port_data.get('pci'):
972 db_filter['pci'] = port_data['pci']
973 if port_data.get('compute_node'):
974 db_filter['compute_node'] = port_data['compute_node']
975
976 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
977 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
978
979 if not len(port_mapping_data):
980 raise ovimException("No port mapping founded for '{}'".format(str(db_filter)),
981 HTTP_Not_Found)
982 elif len(port_mapping_data) > 1:
983 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
984 HTTP_Conflict)
985
986 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
987 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
988 port_data['switch_port'] = port_mapping_data[0]['switch_port']
989 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
990
991 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
992 if 'region' in port_data:
993 del port_data['region']
994 if 'pci' in port_data:
995 del port_data['pci']
996 if 'compute_node' in port_data:
997 del port_data['compute_node']
998
999 result, uuid = self.db.new_row('ports', port_data, True, True)
1000 if result > 0:
1001 try:
1002 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
1003 except ovimException as e:
1004 raise ovimException("Cannot insert a task for updating network '{}' {}".
1005 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
1006 except Exception as e:
1007 raise ovimException("Cannot insert a task for updating network '{}' {}"
1008 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
1009 return uuid
1010 else:
1011 raise ovimException(str(uuid), -result)
1012
1013 def net_update_ofc_thread(self, net_id, ofc_id=None, switch_dpid=None):
1014 """
1015 Insert a update net task by net id or ofc_id for each ofc thread
1016 :param net_id: network id
1017 :param ofc_id: openflow controller id
1018 :param switch_dpid: switch dpid
1019 :return:
1020 """
1021 if not net_id:
1022 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
1023
1024 r = -1
1025 c = 'No valid ofc_id or switch_dpid received'
1026
1027 if not ofc_id:
1028 ports = self.get_ports(filter={"net_id": net_id})
1029 for port in ports:
1030 port_ofc_id = port.get('ofc_id', None)
1031 if port_ofc_id:
1032 ofc_id = port['ofc_id']
1033 switch_dpid = port['switch_dpid']
1034 break
1035 #TODO if not ofc_id: look at database table ofcs
1036
1037
1038 # If no ofc_id found it, default ofc_id is used.
1039 if not ofc_id and not switch_dpid:
1040 ofc_id = "Default"
1041
1042 if ofc_id and ofc_id in self.config['ofcs_thread']:
1043 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
1044 elif switch_dpid:
1045
1046 ofcs_dpid_list = self.config['ofcs_thread_dpid']
1047 for ofc_t in ofcs_dpid_list:
1048 if switch_dpid in ofc_t:
1049 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
1050
1051 if r < 0:
1052 message = "Cannot insert a task for updating network '{}', {}".format(net_id, c)
1053 self.logger.error(message)
1054 raise ovimException(message, HTTP_Internal_Server_Error)
1055
1056 def delete_port(self, port_id):
1057 # Look for the previous port data
1058 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
1059 if result < 0:
1060 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
1061 # delete from the data base
1062 result, content = self.db.delete_row('ports', port_id)
1063 if result == 0:
1064 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1065 elif result < 0:
1066 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
1067 # update network
1068 network = ports[0].get('net_id', None)
1069 if network:
1070 # change of net.
1071
1072 try:
1073 self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
1074 except ovimException as e:
1075 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1076 HTTP_Internal_Server_Error)
1077 except Exception as e:
1078 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1079 HTTP_Internal_Server_Error)
1080
1081 return content
1082
1083 def edit_port(self, port_id, port_data, admin=True):
1084 # Look for the previous port data
1085 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
1086 if result < 0:
1087 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
1088 elif result == 0:
1089 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1090 port = content[0]
1091 nets = []
1092 host_id = None
1093 result = 1
1094 if 'net_id' in port_data:
1095 # change of net.
1096 old_net = port.get('net_id', None)
1097 new_net = port_data['net_id']
1098 if old_net != new_net:
1099
1100 if new_net:
1101 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
1102 if old_net:
1103 nets.append(old_net)
1104 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
1105 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
1106 elif port['type'] == 'external' and not admin:
1107 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
1108 if new_net:
1109 # check that new net has the correct type
1110 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
1111 if result < 0:
1112 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
1113 # change VLAN for SR-IOV ports
1114 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
1115 if new_net:
1116 port_data["vlan"] = None
1117 else:
1118 port_data["vlan"] = new_net_dict["vlan"]
1119 # get host where this VM is allocated
1120 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
1121 if result > 0:
1122 host_id = content[0]["host_id"]
1123
1124 # insert in data base
1125 if result >= 0:
1126 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
1127 port.update(port_data)
1128
1129 # Insert task to complete actions
1130 if result > 0:
1131 for net_id in nets:
1132 try:
1133 self.net_update_ofc_thread(net_id, port["ofc_id"], switch_dpid=port["switch_dpid"])
1134 except ovimException as e:
1135 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1136 HTTP_Internal_Server_Error)
1137 except Exception as e:
1138 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1139 HTTP_Internal_Server_Error)
1140
1141 if host_id:
1142 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1143 if r < 0:
1144 self.logger.error("Error updating network '{}' {}".format(r,v))
1145 # TODO Do something if fails
1146 if result >= 0:
1147 return port_id
1148 else:
1149 raise ovimException("Error {}".format(content), http_code=-result)
1150
1151 def new_of_controller(self, ofc_data):
1152 """
1153 Create a new openflow controller into DB
1154 :param ofc_data: Dict openflow controller data
1155 :return: openflow controller dpid
1156 """
1157
1158 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
1159 if result < 0:
1160 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1161
1162 ofc_data['uuid'] = ofc_uuid
1163 of_conn = self._load_of_module(ofc_data)
1164 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1165
1166 return ofc_uuid
1167
1168 def edit_of_controller(self, of_id, ofc_data):
1169 """
1170 Edit an openflow controller entry from DB
1171 :return:
1172 """
1173 if not ofc_data:
1174 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1175
1176 old_of_controller = self.show_of_controller(of_id)
1177
1178 if old_of_controller:
1179 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1180 if result >= 0:
1181 return ofc_data
1182 else:
1183 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1184 http_code=-result)
1185 else:
1186 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1187 http_code=HTTP_Internal_Server_Error)
1188
1189 def delete_of_controller(self, of_id):
1190 """
1191 Delete an openflow controller from DB.
1192 :param of_id: openflow controller dpid
1193 :return:
1194 """
1195
1196 ofc = self.show_of_controller(of_id)
1197
1198 result, content = self.db.delete_row("ofcs", of_id)
1199 if result < 0:
1200 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1201 elif result == 0:
1202 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
1203
1204 ofc_thread = self.config['ofcs_thread'][of_id]
1205 del self.config['ofcs_thread'][of_id]
1206 for ofc_th in self.config['ofcs_thread_dpid']:
1207 if ofc['dpid'] in ofc_th:
1208 self.config['ofcs_thread_dpid'].remove(ofc_th)
1209
1210 ofc_thread.insert_task("exit")
1211 #ofc_thread.join()
1212
1213 return content
1214
1215 def show_of_controller(self, uuid):
1216 """
1217 Show an openflow controller by dpid from DB.
1218 :param db_filter: List with where query parameters
1219 :return:
1220 """
1221
1222 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1223
1224 if result == 0:
1225 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1226 http_code=HTTP_Not_Found)
1227 elif result < 0:
1228 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1229 http_code=HTTP_Internal_Server_Error)
1230 return content[0]
1231
1232 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
1233 """
1234 Show an openflow controllers from DB.
1235 :param columns: List with SELECT query parameters
1236 :param db_filter: List with where query parameters
1237 :param limit: result Limit
1238 :return:
1239 """
1240 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
1241
1242 if result < 0:
1243 raise ovimException(str(content), -result)
1244
1245 return content
1246
1247 def get_tenants(self, columns=None, db_filter={}, limit=None):
1248 """
1249 Retrieve tenant list from DB
1250 :param columns: List with SELECT query parameters
1251 :param db_filter: List with where query parameters
1252 :param limit: result limit
1253 :return:
1254 """
1255 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1256 if result < 0:
1257 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1258 else:
1259 convert_boolean(content, ('enabled',))
1260 return content
1261
1262 def show_tenant_id(self, tenant_id):
1263 """
1264 Get tenant from DB by id
1265 :param tenant_id: tenant id
1266 :return:
1267 """
1268 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1269 WHERE={"uuid": tenant_id})
1270 if result < 0:
1271 raise ovimException(str(content), -result)
1272 elif result == 0:
1273 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1274 else:
1275 convert_boolean(content, ('enabled',))
1276 return content[0]
1277
1278 def new_tentant(self, tenant):
1279 """
1280 Create a tenant and store in DB
1281 :param tenant: Dictionary with tenant data
1282 :return: the uuid of created tenant. Raise exception upon error
1283 """
1284
1285 # insert in data base
1286 result, tenant_uuid = self.db.new_tenant(tenant)
1287
1288 if result >= 0:
1289 return tenant_uuid
1290 else:
1291 raise ovimException(str(tenant_uuid), -result)
1292
1293 def delete_tentant(self, tenant_id):
1294 """
1295 Delete a tenant from the database.
1296 :param tenant_id: Tenant id
1297 :return: delete tenant id
1298 """
1299
1300 # check permissions
1301 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1302 WHERE={'tenant_id': tenant_id})
1303 if r <= 0:
1304 tenants_flavors = ()
1305 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1306 WHERE={'tenant_id': tenant_id})
1307 if r <= 0:
1308 tenants_images = ()
1309
1310 result, content = self.db.delete_row('tenants', tenant_id)
1311 if result == 0:
1312 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1313 elif result > 0:
1314 for flavor in tenants_flavors:
1315 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1316 for image in tenants_images:
1317 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1318 return content
1319 else:
1320 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1321
1322 def edit_tenant(self, tenant_id, tenant_data):
1323 """
1324 Update a tenant data identified by tenant id
1325 :param tenant_id: tenant id
1326 :param tenant_data: Dictionary with tenant data
1327 :return:
1328 """
1329
1330 # Look for the previous data
1331 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1332 if result < 0:
1333 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1334 HTTP_Internal_Server_Error)
1335 elif result == 0:
1336 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1337
1338 # insert in data base
1339 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1340 if result >= 0:
1341 return content
1342 else:
1343 raise ovimException(str(content), -result)
1344
1345 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1346 """
1347 Create new port mapping entry
1348 :param of_maps: List with port mapping information
1349 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1350 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1351 :param ofc_id: ofc id
1352 :param switch_dpid: switch dpid
1353 :param region: datacenter region id
1354 :return:
1355 """
1356
1357 for map in of_maps:
1358 if ofc_id:
1359 map['ofc_id'] = ofc_id
1360 if switch_dpid:
1361 map['switch_dpid'] = switch_dpid
1362 if region:
1363 map['region'] = region
1364
1365 for of_map in of_maps:
1366 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1367 if result > 0:
1368 of_map["uuid"] = uuid
1369 else:
1370 raise ovimException(str(uuid), -result)
1371 return of_maps
1372
1373 def clear_of_port_mapping(self, db_filter={}):
1374 """
1375 Clear port mapping filtering using db_filter dict
1376 :param db_filter: Parameter to filter during remove process
1377 :return:
1378 """
1379 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1380 # delete_row_by_key
1381 if result >= 0:
1382 return content
1383 else:
1384 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1385 HTTP_Internal_Server_Error)
1386
1387 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1388 """
1389 Retrive port mapping from DB
1390 :param column:
1391 :param db_filter:
1392 :return:
1393 """
1394 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1395
1396 if result < 0:
1397 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1398 raise ovimException(str(content), -result)
1399 else:
1400 return content
1401
1402 def get_dhcp_controller(self):
1403 """
1404 Create an host_thread object for manage openvim controller and not create a thread for itself
1405 :return: dhcp_host openvim controller object
1406 """
1407
1408 if 'openvim_controller' in self.config['host_threads']:
1409 return self.config['host_threads']['openvim_controller']
1410
1411 bridge_ifaces = []
1412 controller_ip = self.config['ovs_controller_ip']
1413 ovs_controller_user = self.config.get('ovs_controller_user')
1414
1415 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1416 host_develop_mode = True if self.config['mode'] == 'development' else False
1417
1418 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
1419 password=self.config.get('ovs_controller_password'),
1420 keyfile=self.config.get('ovs_controller_keyfile'),
1421 db=self.config["db"], db_lock=self.config["db_lock"], test=host_test_mode,
1422 image_path=self.config['host_image_path'], version=self.config['version'],
1423 host_id='openvim_controller', develop_mode=host_develop_mode,
1424 develop_bridge_iface=bridge_ifaces,
1425 logger_name=self.logger_name + ".host.controller",
1426 debug=self.config.get('log_level_host'))
1427 # dhcp_host.start()
1428 self.config['host_threads']['openvim_controller'] = dhcp_host
1429 try:
1430 dhcp_host.check_connectivity()
1431 except Exception as e:
1432 pass
1433
1434 return dhcp_host
1435
1436 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway, dns, routes):
1437 """
1438 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1439 :param vlan: vlan identifier
1440 :param first_ip: First dhcp range ip
1441 :param last_ip: Last dhcp range ip
1442 :param cidr: net cidr
1443 :param gateway: net gateway
1444 :return:
1445 """
1446 ip_tools = IPNetwork(cidr)
1447 dhcp_netmask = str(ip_tools.netmask)
1448 ip_range = [first_ip, last_ip]
1449
1450 dhcp_path = self.config['ovs_controller_file_path']
1451
1452 controller_host = self.get_dhcp_controller()
1453
1454 # controller_host.create_linux_bridge(vlan)
1455 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
1456 dhcp_path = self.config['ovs_controller_file_path']
1457 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway, dns, routes)
1458
1459 def launch_link_bridge_to_ovs(self, vlan, gateway, dhcp_cidr, links=None, routes=None):
1460 """
1461 Launch creating of connections (veth) between user bridge (link) and OVS
1462 :param vlan:
1463 :param gateway:
1464 :param links:
1465 :return:
1466 """
1467
1468 if links:
1469 controller_host = self.get_dhcp_controller()
1470 for link in links:
1471 if 'iface' in link and 'nat' not in link:
1472 controller_host.create_link_bridge_to_ovs(vlan, link['iface'])
1473 elif 'nat' in link:
1474 controller_host.create_qrouter_ovs_connection(vlan, gateway, dhcp_cidr)
1475 controller_host.create_qrouter_br_connection(vlan, dhcp_cidr, link)
1476
1477 if len(routes):
1478 controller_host.add_ns_routes(vlan, routes)
1479
1480 def delete_link_bridge_to_ovs(self, vlan, links=None):
1481 """
1482 Delete connections (veth) between user bridge (link) and OVS
1483 :param vlan:
1484 :param links:
1485 :return:
1486 """
1487 if links:
1488 controller_host = self.get_dhcp_controller()
1489
1490 for link in links:
1491 if 'iface' in link and 'nat' not in link:
1492 controller_host.remove_link_bridge_to_ovs(vlan, link['iface'])
1493 elif 'nat' in link:
1494 controller_host.delete_qrouter_connection(vlan, link['iface'])
1495
1496
1497 if __name__ == "__main__":
1498
1499 parser = argparse.ArgumentParser()
1500 parser.add_argument("-v","--version", help="show ovim library version", action="store_true")
1501 parser.add_argument("--database-version", help="show required database version", action="store_true")
1502 args = parser.parse_args()
1503 if args.version:
1504 print ('openvimd version {} {}'.format(ovim.get_version(), ovim.get_version_date()))
1505 print ('(c) Copyright Telefonica')
1506 elif args.database_version:
1507 print ('required database version: {}'.format(ovim.get_database_version()))
1508