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