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