blob: 16b3f03e1b0a0dfe20afb97a93a6e387f4c5fdbc [file] [log] [blame]
tierno57f7bda2017-02-09 12:01:55 +01001# -*- coding: utf-8 -*-
2
3##
4# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5# This file is part of openvim
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
24'''
25This is the thread for the http server North API.
26Two thread will be launched, with normal and administrative permissions.
27'''
28
29__author__ = "Alfonso Tierno, Leonardo Mirabal"
30__date__ = "$06-Feb-2017 12:07:15$"
31
32import threading
33import vim_db
34import logging
35import threading
36import imp
37import host_thread as ht
38import dhcp_thread as dt
39import openflow_thread as oft
mirabal65ba8f82017-02-15 12:36:33 +010040from netaddr import IPNetwork
41from jsonschema import validate as js_v, exceptions as js_e
tierno57f7bda2017-02-09 12:01:55 +010042
43HTTP_Bad_Request = 400
44HTTP_Unauthorized = 401
45HTTP_Not_Found = 404
46HTTP_Forbidden = 403
47HTTP_Method_Not_Allowed = 405
48HTTP_Not_Acceptable = 406
49HTTP_Request_Timeout = 408
50HTTP_Conflict = 409
51HTTP_Service_Unavailable = 503
52HTTP_Internal_Server_Error= 500
53
54
55def convert_boolean(data, items):
56 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
57 It assumes that bandwidth is well formed
58 Attributes:
59 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
60 'items': tuple of keys to convert
61 Return:
62 None
63 '''
64 if type(data) is dict:
65 for k in data.keys():
66 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
67 convert_boolean(data[k], items)
68 if k in items:
69 if type(data[k]) is str:
70 if data[k] == "false":
71 data[k] = False
72 elif data[k] == "true":
73 data[k] = True
74 if type(data) is tuple or type(data) is list:
75 for k in data:
76 if type(k) is dict or type(k) is tuple or type(k) is list:
77 convert_boolean(k, items)
78
79
80
81class ovimException(Exception):
82 def __init__(self, message, http_code=HTTP_Bad_Request):
83 self.http_code = http_code
84 Exception.__init__(self, message)
85
86
87class ovim():
88 running_info = {} #TODO OVIM move the info of running threads from config_dic to this static variable
mirabal580435e2017-03-01 16:17:10 +010089 of_module = {}
90
tierno57f7bda2017-02-09 12:01:55 +010091 def __init__(self, configuration):
92 self.config = configuration
93 self.logger = logging.getLogger(configuration["logger_name"])
94 self.db = None
95 self.db = self._create_database_connection()
mirabal580435e2017-03-01 16:17:10 +010096 self.db_lock = None
97 self.db_of = None
98 self.of_test_mode = False
tierno57f7bda2017-02-09 12:01:55 +010099
100 def _create_database_connection(self):
101 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
102 self.config['log_level_db']);
103 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
104 self.config['db_name']) == -1:
105 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
106 # self.config['db_host'])
107 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
108 self.config['db_user'],
109 self.config['db_host']) )
110 return db
111
mirabale9f6f1a2017-02-16 17:57:35 +0100112 @staticmethod
113 def _check_dhcp_data_integrity(network):
114 """
115 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
116 :param network: list with user nets paramters
117 :return:
118 """
119 if "cidr" in network:
120 cidr = network["cidr"]
121 ip_tools = IPNetwork(cidr)
122 cidr_len = ip_tools.prefixlen
123 if cidr_len > 29:
124 return False
125
126 ips = IPNetwork(cidr)
127 if "dhcp_first_ip" not in network:
128 network["dhcp_first_ip"] = str(ips[2])
129 if "dhcp_last_ip" not in network:
130 network["dhcp_last_ip"] = str(ips[-2])
131 if "gateway_ip" not in network:
132 network["gateway_ip"] = str(ips[1])
133
134 return True
135 else:
136 return False
137
138 @staticmethod
139 def _check_valid_uuid(uuid):
140 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
141 try:
142 js_v(uuid, id_schema)
143 return True
144 except js_e.ValidationError:
145 return False
tierno57f7bda2017-02-09 12:01:55 +0100146
147 def start_service(self):
mirabal580435e2017-03-01 16:17:10 +0100148 """
149 Start ovim services
150 :return:
151 """
152 # if self.running_info:
tierno57f7bda2017-02-09 12:01:55 +0100153 # return #TODO service can be checked and rebuild broken threads
154 r = self.db.get_db_version()
mirabal580435e2017-03-01 16:17:10 +0100155 if r[0] < 0:
tierno57f7bda2017-02-09 12:01:55 +0100156 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
157 "'./database_utils/migrate_vim_db.sh'".format(self.config["database_version"]) )
mirabal580435e2017-03-01 16:17:10 +0100158 elif r[1] != self.config["database_version"]:
tierno57f7bda2017-02-09 12:01:55 +0100159 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
160 "'./database_utils/migrate_vim_db.sh'".format(r[1], self.config["database_version"]) )
161
162 # create database connection for openflow threads
mirabal580435e2017-03-01 16:17:10 +0100163 self.db_of = self._create_database_connection()
164 self.config["db"] = self.db_of
165 self.db_lock = threading.Lock()
166 self.config["db_lock"] = self.db_lock
tierno57f7bda2017-02-09 12:01:55 +0100167
mirabal580435e2017-03-01 16:17:10 +0100168 self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
169 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
170 # speed in Gbit/s
171
tierno57f7bda2017-02-09 12:01:55 +0100172 self.config['dhcp_nets'] = []
173 self.config['bridge_nets'] = []
174 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
mirabal580435e2017-03-01 16:17:10 +0100175 # skip 'development_bridge'
tierno57f7bda2017-02-09 12:01:55 +0100176 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
177 continue
178 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
179
180 # check if this bridge is already used (present at database) for a network)
181 used_bridge_nets = []
182 for brnet in self.config['bridge_nets']:
tierno686b3952017-03-10 13:57:24 +0100183 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
tierno57f7bda2017-02-09 12:01:55 +0100184 if r > 0:
185 brnet[3] = nets[0]['uuid']
186 used_bridge_nets.append(brnet[0])
187 if self.config.get("dhcp_server"):
188 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
189 self.config['dhcp_nets'].append(nets[0]['uuid'])
190 if len(used_bridge_nets) > 0:
191 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
192 # get nets used by dhcp
193 if self.config.get("dhcp_server"):
194 for net in self.config["dhcp_server"].get("nets", ()):
tierno686b3952017-03-10 13:57:24 +0100195 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
tierno57f7bda2017-02-09 12:01:55 +0100196 if r > 0:
197 self.config['dhcp_nets'].append(nets[0]['uuid'])
198
mirabal580435e2017-03-01 16:17:10 +0100199 # OFC default
200 self._start_ofc_default_task()
tierno57f7bda2017-02-09 12:01:55 +0100201
mirabal580435e2017-03-01 16:17:10 +0100202 # OFC per tenant in DB
203 self._start_of_db_tasks()
tierno57f7bda2017-02-09 12:01:55 +0100204
205 # create dhcp_server thread
206 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
207 dhcp_params = self.config.get("dhcp_server")
208 if dhcp_params:
209 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
mirabal580435e2017-03-01 16:17:10 +0100210 db=self.db_of, db_lock=self.db_lock, debug=self.config['log_level_of'])
tierno57f7bda2017-02-09 12:01:55 +0100211 thread.start()
212 self.config['dhcp_thread'] = thread
213
214 # Create one thread for each host
215 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
216 host_develop_mode = True if self.config['mode'] == 'development' else False
217 host_develop_bridge_iface = self.config.get('development_bridge', None)
mirabal580435e2017-03-01 16:17:10 +0100218
219 # get host list from data base before starting threads
tierno686b3952017-03-10 13:57:24 +0100220 r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid'), FROM='hosts', WHERE={'status': 'ok'})
mirabal580435e2017-03-01 16:17:10 +0100221 if r < 0:
222 raise ovimException("Cannot get hosts from database {}".format(hosts))
223
tierno57f7bda2017-02-09 12:01:55 +0100224 self.config['host_threads'] = {}
225 for host in hosts:
226 host['image_path'] = '/opt/VNF/images/openvim'
mirabal580435e2017-03-01 16:17:10 +0100227 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.db_of,
228 db_lock=self.db_lock, test=host_test_mode, image_path=self.config['image_path'],
229 version=self.config['version'], host_id=host['uuid'], develop_mode=host_develop_mode,
tierno57f7bda2017-02-09 12:01:55 +0100230 develop_bridge_iface=host_develop_bridge_iface)
231 thread.start()
232 self.config['host_threads'][host['uuid']] = thread
233
mirabalb716ac52017-02-10 14:47:53 +0100234 # create ovs dhcp thread
235 result, content = self.db.get_table(FROM='nets')
236 if result < 0:
237 self.logger.error("http_get_ports Error %d %s", result, content)
238 raise ovimException(str(content), -result)
239
240 for net in content:
241 net_type = net['type']
mirabale9f6f1a2017-02-16 17:57:35 +0100242 if (net_type == 'bridge_data' or net_type == 'bridge_man') \
243 and net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
mirabal65ba8f82017-02-15 12:36:33 +0100244 self.launch_dhcp_server(net['vlan'],
245 net['dhcp_first_ip'],
246 net['dhcp_last_ip'],
247 net['cidr'],
248 net['gateway_ip'])
mirabalb716ac52017-02-10 14:47:53 +0100249
mirabal580435e2017-03-01 16:17:10 +0100250 def _start_of_db_tasks(self):
251 """
252 Start ofc task for existing ofcs in database
253 :param db_of:
254 :param db_lock:
255 :return:
256 """
257 ofcs = self.get_of_controllers()
258
259 for ofc in ofcs:
260 of_conn = self._load_of_module(ofc)
261 # create ofc thread per of controller
262 self._create_ofc_task(ofc['uuid'], ofc['dpid'], of_conn)
263
264 def _create_ofc_task(self, ofc_uuid, dpid, of_conn):
265 """
266 Create an ofc thread for handle each sdn controllers
267 :param ofc_uuid: sdn controller uuid
268 :param dpid: sdn controller dpid
269 :param of_conn: OF_conn module
270 :return:
271 """
272 if 'ofcs_thread' not in self.config and 'ofcs_thread_dpid' not in self.config:
273 ofcs_threads = {}
274 ofcs_thread_dpid = []
275 else:
276 ofcs_threads = self.config['ofcs_thread']
277 ofcs_thread_dpid = self.config['ofcs_thread_dpid']
278
279 if ofc_uuid not in ofcs_threads:
280 ofc_thread = self._create_ofc_thread(of_conn, ofc_uuid)
281 if ofc_uuid == "Default":
282 self.config['of_thread'] = ofc_thread
283
284 ofcs_threads[ofc_uuid] = ofc_thread
285 self.config['ofcs_thread'] = ofcs_threads
286
287 ofcs_thread_dpid.append({dpid: ofc_thread})
288 self.config['ofcs_thread_dpid'] = ofcs_thread_dpid
289
290 def _start_ofc_default_task(self):
291 """
292 Create default ofc thread
293 """
294 if 'of_controller' not in self.config \
295 and 'of_controller_ip' not in self.config \
296 and 'of_controller_port' not in self.config \
297 and 'of_controller_dpid' not in self.config:
298 return
299
300 # OF THREAD
301 db_config = {}
302 db_config['ip'] = self.config.get('of_controller_ip')
303 db_config['port'] = self.config.get('of_controller_port')
304 db_config['dpid'] = self.config.get('of_controller_dpid')
305 db_config['type'] = self.config.get('of_controller')
306 db_config['user'] = self.config.get('of_user')
307 db_config['password'] = self.config.get('of_password')
308
309 # create connector to the openflow controller
310 # load other parameters starting by of_ from config dict in a temporal dict
311
312 of_conn = self._load_of_module(db_config)
313 # create openflow thread
314 self._create_ofc_task("Default", db_config['dpid'], of_conn)
315
316 def _load_of_module(self, db_config):
317 """
318 import python module for each SDN controller supported
319 :param default: SDN dn information
320 :return: Module
321 """
322 if not db_config:
323 raise ovimException("No module found it", HTTP_Internal_Server_Error)
324
325 module_info = None
326
327 try:
328 if self.of_test_mode:
329 return oft.of_test_connector({"name": db_config['type'], "dpid": db_config['dpid'],
330 "of_debug": self.config['log_level_of']})
331 temp_dict = {}
332
333 if db_config:
334 temp_dict['of_ip'] = db_config['ip']
335 temp_dict['of_port'] = db_config['port']
336 temp_dict['of_dpid'] = db_config['dpid']
337 temp_dict['of_controller'] = db_config['type']
338
339 temp_dict['of_debug'] = self.config['log_level_of']
340
341 if temp_dict['of_controller'] == 'opendaylight':
342 module = "ODL"
343 else:
344 module = temp_dict['of_controller']
345
346 if module not in ovim.of_module:
347 module_info = imp.find_module(module)
348 of_conn_module = imp.load_module("OF_conn", *module_info)
349 ovim.of_module[module] = of_conn_module
350 else:
351 of_conn_module = ovim.of_module[module]
352
353 try:
354 return of_conn_module.OF_conn(temp_dict)
355 except Exception as e:
356 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
357 if module_info and module_info[0]:
358 file.close(module_info[0])
359 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e).__name__, str(e)),
360 HTTP_Internal_Server_Error)
361 except (IOError, ImportError) as e:
362 if module_info and module_info[0]:
363 file.close(module_info[0])
364 self.logger.error("Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' "
365 "field of configuration file.", module, type(e).__name__, str(e))
366 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' "
367 "field of configuration file.".format(module, type(e).__name__, str(e)),
368 HTTP_Internal_Server_Error)
369
370 def _create_ofc_thread(self, of_conn, ofc_uuid="Default"):
371 """
372 Create and launch a of thread
373 :return: thread obj
374 """
375 # create openflow thread
376
377 if 'of_controller_nets_with_same_vlan' in self.config:
378 ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
379 else:
380 ofc_net_same_vlan = False
381
382 thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.db_of, db_lock=self.db_lock,
383 pmp_with_same_vlan=ofc_net_same_vlan, debug=self.config['log_level_of'])
384 #r, c = thread.OF_connector.obtain_port_correspondence()
385 #if r < 0:
386 # raise ovimException("Cannot get openflow information %s", c)
387 thread.start()
388 return thread
389
tierno57f7bda2017-02-09 12:01:55 +0100390 def stop_service(self):
391 threads = self.config.get('host_threads', {})
392 if 'of_thread' in self.config:
393 threads['of'] = (self.config['of_thread'])
mirabal580435e2017-03-01 16:17:10 +0100394 if 'ofcs_thread' in self.config:
395 ofcs_thread = self.config['ofcs_thread']
396 for ofc in ofcs_thread:
397 threads[ofc] = ofcs_thread[ofc]
398
tierno57f7bda2017-02-09 12:01:55 +0100399 if 'dhcp_thread' in self.config:
400 threads['dhcp'] = (self.config['dhcp_thread'])
401
402 for thread in threads.values():
403 thread.insert_task("exit")
404 for thread in threads.values():
405 thread.join()
tierno57f7bda2017-02-09 12:01:55 +0100406
mirabal9e194592017-02-17 11:03:25 +0100407 def get_networks(self, columns=None, db_filter={}, limit=None):
mirabal65ba8f82017-02-15 12:36:33 +0100408 """
409 Retreive networks available
mirabale9f6f1a2017-02-16 17:57:35 +0100410 :param columns: List with select query parameters
mirabal9e194592017-02-17 11:03:25 +0100411 :param db_filter: List with where query parameters
mirabale9f6f1a2017-02-16 17:57:35 +0100412 :param limit: Query limit result
mirabal65ba8f82017-02-15 12:36:33 +0100413 :return:
414 """
mirabal9e194592017-02-17 11:03:25 +0100415 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=db_filter, LIMIT=limit)
mirabal65ba8f82017-02-15 12:36:33 +0100416
417 if result < 0:
418 raise ovimException(str(content), -result)
419
420 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
421
422 return content
423
mirabal9e194592017-02-17 11:03:25 +0100424 def show_network(self, network_id, db_filter={}):
mirabal65ba8f82017-02-15 12:36:33 +0100425 """
mirabale9f6f1a2017-02-16 17:57:35 +0100426 Get network from DB by id
427 :param network_id: net Id
mirabal9e194592017-02-17 11:03:25 +0100428 :param db_filter: List with where query parameters
mirabal65ba8f82017-02-15 12:36:33 +0100429 :return:
430 """
431 # obtain data
mirabale9f6f1a2017-02-16 17:57:35 +0100432 if not network_id:
433 raise ovimException("Not network id was not found")
mirabal9e194592017-02-17 11:03:25 +0100434 db_filter['uuid'] = network_id
mirabal65ba8f82017-02-15 12:36:33 +0100435
mirabal9e194592017-02-17 11:03:25 +0100436 result, content = self.db.get_table(FROM='nets', WHERE=db_filter, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100437
438 if result < 0:
439 raise ovimException(str(content), -result)
440 elif result == 0:
mirabale9f6f1a2017-02-16 17:57:35 +0100441 raise ovimException("show_network network '%s' not found" % network_id, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100442 else:
443 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
mirabale9f6f1a2017-02-16 17:57:35 +0100444 # get ports from DB
mirabal65ba8f82017-02-15 12:36:33 +0100445 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
mirabale9f6f1a2017-02-16 17:57:35 +0100446 WHERE={'net_id': network_id}, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100447 if len(ports) > 0:
448 content[0]['ports'] = ports
mirabal65ba8f82017-02-15 12:36:33 +0100449
mirabale9f6f1a2017-02-16 17:57:35 +0100450 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
451 return content[0]
452
453 def new_network(self, network):
mirabal65ba8f82017-02-15 12:36:33 +0100454 """
mirabale9f6f1a2017-02-16 17:57:35 +0100455 Create a net in DB
mirabal65ba8f82017-02-15 12:36:33 +0100456 :return:
457 """
458 tenant_id = network.get('tenant_id')
459
460 if tenant_id:
461 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
462 if result <= 0:
463 raise ovimException("set_network error, no tenant founded", -result)
464
465 bridge_net = None
466 # check valid params
467 net_provider = network.get('provider')
468 net_type = network.get('type')
mirabal65ba8f82017-02-15 12:36:33 +0100469 net_vlan = network.get("vlan")
470 net_bind_net = network.get("bind_net")
471 net_bind_type = network.get("bind_type")
472 name = network["name"]
473
474 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
475 vlan_index = name.rfind(":")
476 if not net_bind_net and not net_bind_type and vlan_index > 1:
477 try:
478 vlan_tag = int(name[vlan_index + 1:])
479 if not vlan_tag and vlan_tag < 4096:
480 net_bind_net = name[:vlan_index]
481 net_bind_type = "vlan:" + name[vlan_index + 1:]
482 except:
483 pass
484
485 if net_bind_net:
486 # look for a valid net
487 if self._check_valid_uuid(net_bind_net):
488 net_bind_key = "uuid"
489 else:
490 net_bind_key = "name"
491 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
492 if result < 0:
493 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
494 elif result == 0:
495 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
496 elif result > 1:
497 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
498 network["bind_net"] = content[0]["uuid"]
499
500 if net_bind_type:
501 if net_bind_type[0:5] != "vlan:":
502 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
503 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
504 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
505 HTTP_Bad_Request)
506 network["bind_type"] = net_bind_type
507
508 if net_provider:
509 if net_provider[:9] == "openflow:":
510 if net_type:
511 if net_type != "ptp" and net_type != "data":
512 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
513 HTTP_Bad_Request)
514 else:
515 net_type = 'data'
516 else:
517 if net_type:
518 if net_type != "bridge_man" and net_type != "bridge_data":
519 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
520 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
521 else:
522 net_type = 'bridge_man'
523
524 if not net_type:
525 net_type = 'bridge_man'
526
527 if net_provider:
528 if net_provider[:7] == 'bridge:':
529 # check it is one of the pre-provisioned bridges
530 bridge_net_name = net_provider[7:]
531 for brnet in self.config['bridge_nets']:
532 if brnet[0] == bridge_net_name: # free
533 if not brnet[3]:
534 raise ovimException("invalid 'provider:physical', "
535 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
536 bridge_net = brnet
537 net_vlan = brnet[1]
538 break
mirabale9f6f1a2017-02-16 17:57:35 +0100539 # if bridge_net==None:
540 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
541 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
542 # return
543
mirabal65ba8f82017-02-15 12:36:33 +0100544 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
545 # look for a free precreated nets
546 for brnet in self.config['bridge_nets']:
547 if not brnet[3]: # free
548 if not bridge_net:
549 if net_type == 'bridge_man': # look for the smaller speed
550 if brnet[2] < bridge_net[2]:
551 bridge_net = brnet
552 else: # look for the larger speed
553 if brnet[2] > bridge_net[2]:
554 bridge_net = brnet
555 else:
556 bridge_net = brnet
557 net_vlan = brnet[1]
558 if not bridge_net:
559 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
560 "will overcome this limit", HTTP_Bad_Request)
561 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100562 self.logger.debug("using net " + bridge_net)
mirabal65ba8f82017-02-15 12:36:33 +0100563 net_provider = "bridge:" + bridge_net[0]
564 net_vlan = bridge_net[1]
565 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
566 net_provider = 'OVS'
567 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
568 net_vlan = self.db.get_free_net_vlan()
569 if net_vlan < 0:
570 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
571 if net_provider == 'OVS':
572 net_provider = 'OVS' + ":" + str(net_vlan)
573
574 network['provider'] = net_provider
575 network['type'] = net_type
576 network['vlan'] = net_vlan
577 dhcp_integrity = True
578 if 'enable_dhcp' in network and network['enable_dhcp']:
579 dhcp_integrity = self._check_dhcp_data_integrity(network)
580
581 result, content = self.db.new_row('nets', network, True, True)
582
583 if result >= 0 and dhcp_integrity:
584 if bridge_net:
585 bridge_net[3] = content
586 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
587 if network["name"] in self.config["dhcp_server"].get("nets", ()):
588 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100589 self.logger.debug("dhcp_server: add new net", content)
mirabal65ba8f82017-02-15 12:36:33 +0100590 elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
591 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100592 self.logger.debug("dhcp_server: add new net", content, content)
mirabal65ba8f82017-02-15 12:36:33 +0100593 return content
594 else:
mirabal65ba8f82017-02-15 12:36:33 +0100595 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
mirabale9f6f1a2017-02-16 17:57:35 +0100596# TODO kei change update->edit
mirabal65ba8f82017-02-15 12:36:33 +0100597
mirabale9f6f1a2017-02-16 17:57:35 +0100598 def edit_network(self, network_id, network):
mirabal65ba8f82017-02-15 12:36:33 +0100599 """
mirabale9f6f1a2017-02-16 17:57:35 +0100600 Update entwork data byt id
mirabal65ba8f82017-02-15 12:36:33 +0100601 :return:
602 """
mirabale9f6f1a2017-02-16 17:57:35 +0100603 # Look for the previous data
604 where_ = {'uuid': network_id}
605 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
606 if result < 0:
607 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
608 elif result == 0:
609 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
610 # get ports
611 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
612 WHERE={'net_id': network_id}, LIMIT=100)
613 if result < 0:
614 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
615 if nbports > 0:
616 if 'type' in network and network['type'] != network_old[0]['type']:
617 raise ovimException("Can not change type of network while having ports attached",
618 HTTP_Method_Not_Allowed)
619 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
620 raise ovimException("Can not change vlan of network while having ports attached",
621 HTTP_Method_Not_Allowed)
mirabal65ba8f82017-02-15 12:36:33 +0100622
mirabale9f6f1a2017-02-16 17:57:35 +0100623 # check valid params
624 net_provider = network.get('provider', network_old[0]['provider'])
625 net_type = network.get('type', network_old[0]['type'])
626 net_bind_net = network.get("bind_net")
627 net_bind_type = network.get("bind_type")
628 if net_bind_net:
629 # look for a valid net
630 if self._check_valid_uuid(net_bind_net):
631 net_bind_key = "uuid"
632 else:
633 net_bind_key = "name"
634 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
635 if result < 0:
636 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
637 elif result == 0:
638 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
639 elif result > 1:
640 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
641 HTTP_Bad_Request)
642 network["bind_net"] = content[0]["uuid"]
643 if net_bind_type:
644 if net_bind_type[0:5] != "vlan:":
645 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
646 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
647 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
648 HTTP_Bad_Request)
649 if net_provider:
650 if net_provider[:9] == "openflow:":
651 if net_type != "ptp" and net_type != "data":
652 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
653 else:
654 if net_type != "bridge_man" and net_type != "bridge_data":
655 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
656 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
mirabal65ba8f82017-02-15 12:36:33 +0100657
mirabale9f6f1a2017-02-16 17:57:35 +0100658 # insert in data base
659 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
660 if result >= 0:
661 # if result > 0 and nbports>0 and 'admin_state_up' in network
662 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
663 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100664
665 try:
666 self.net_update_ofc_thread(network_id)
667 except ovimException as e:
668 raise ovimException("Error while launching openflow rules in network '{}' {}"
669 .format(network_id, str(e)), HTTP_Internal_Server_Error)
670 except Exception as e:
671 raise ovimException("Error while launching openflow rules in network '{}' {}"
672 .format(network_id, str(e)), HTTP_Internal_Server_Error)
673
mirabale9f6f1a2017-02-16 17:57:35 +0100674 if self.config.get("dhcp_server"):
675 if network_id in self.config["dhcp_nets"]:
676 self.config["dhcp_nets"].remove(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100677 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
mirabale9f6f1a2017-02-16 17:57:35 +0100678 self.config["dhcp_nets"].append(network_id)
679 else:
mirabal7bbf50e2017-03-13 15:15:18 +0100680 net_bind = network.get("bind_type", network_old[0]["bind_type"])
681 if net_bind and net_bind and net_bind[:7] == "bridge:" and net_bind[7:] in self.config["dhcp_server"].get(
mirabale9f6f1a2017-02-16 17:57:35 +0100682 "bridge_ifaces", ()):
683 self.config["dhcp_nets"].append(network_id)
684 return network_id
mirabal65ba8f82017-02-15 12:36:33 +0100685 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100686 raise ovimException(content, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100687
mirabale9f6f1a2017-02-16 17:57:35 +0100688 def delete_network(self, network_id):
mirabal9e194592017-02-17 11:03:25 +0100689 """
690 Delete network by network id
691 :param network_id: network id
692 :return:
693 """
mirabale9f6f1a2017-02-16 17:57:35 +0100694
695 # delete from the data base
696 result, content = self.db.delete_row('nets', network_id)
697
698 if result == 0:
699 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
700 elif result > 0:
701 for brnet in self.config['bridge_nets']:
702 if brnet[3] == network_id:
703 brnet[3] = None
704 break
705 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
706 self.config["dhcp_nets"].remove(network_id)
707 return content
708 else:
709 raise ovimException("Error deleting network %s" % network_id, HTTP_Internal_Server_Error)
mirabal65ba8f82017-02-15 12:36:33 +0100710
711 def get_openflow_rules(self, network_id=None):
712 """
713 Get openflow id from DB
714 :param network_id: Network id, if none all networks will be retrieved
715 :return: Return a list with Openflow rules per net
716 """
717 # ignore input data
718 if not network_id:
719 where_ = {}
720 else:
721 where_ = {"net_id": network_id}
722
723 result, content = self.db.get_table(
724 SELECT=("name", "net_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
725 WHERE=where_, FROM='of_flows')
726
727 if result < 0:
728 raise ovimException(str(content), -result)
729 return content
730
mirabale9f6f1a2017-02-16 17:57:35 +0100731 def edit_openflow_rules(self, network_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100732
733 """
734 To make actions over the net. The action is to reinstall the openflow rules
735 network_id can be 'all'
736 :param network_id: Network id, if none all networks will be retrieved
737 :return : Number of nets updated
738 """
739
740 # ignore input data
741 if not network_id:
742 where_ = {}
743 else:
744 where_ = {"uuid": network_id}
745 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
746
747 if result < 0:
748 raise ovimException(str(content), -result)
749
750 for net in content:
751 if net["type"] != "ptp" and net["type"] != "data":
752 result -= 1
753 continue
mirabal7bbf50e2017-03-13 15:15:18 +0100754
755 try:
756 self.net_update_ofc_thread(net['uuid'])
757 except ovimException as e:
758 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
759 HTTP_Internal_Server_Error)
760 except Exception as e:
761 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
762 HTTP_Internal_Server_Error)
763
mirabal65ba8f82017-02-15 12:36:33 +0100764 return result
765
mirabale9f6f1a2017-02-16 17:57:35 +0100766 def delete_openflow_rules(self):
mirabal65ba8f82017-02-15 12:36:33 +0100767 """
768 To make actions over the net. The action is to delete ALL openflow rules
769 :return: return operation result
770 """
771 # ignore input data
772 r, c = self.config['of_thread'].insert_task("clear-all")
773 if r < 0:
774 raise ovimException(str(c), -r)
775 return r
776
777 def get_openflow_ports(self):
778 """
779 Obtain switch ports names of openflow controller
780 :return: Return flow ports in DB
781 """
782 data = {'ports': self.config['of_thread'].OF_connector.pp2ofi}
783 return data
tierno57f7bda2017-02-09 12:01:55 +0100784
785 def get_ports(self, columns=None, filter={}, limit=None):
786 # result, content = my.db.get_ports(where_)
787 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
788 if result < 0:
789 self.logger.error("http_get_ports Error %d %s", result, content)
790 raise ovimException(str(content), -result)
791 else:
792 convert_boolean(content, ('admin_state_up',))
793 return content
794
tierno57f7bda2017-02-09 12:01:55 +0100795 def new_port(self, port_data):
796 port_data['type'] = 'external'
797 if port_data.get('net_id'):
798 # check that new net has the correct type
799 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
800 if result < 0:
801 raise ovimException(str(new_net), -result)
802 # insert in data base
803 result, uuid = self.db.new_row('ports', port_data, True, True)
804 if result > 0:
805 if 'net_id' in port_data:
mirabal7bbf50e2017-03-13 15:15:18 +0100806 try:
807 self.net_update_ofc_thread(port_data['net_id'])
808 except ovimException as e:
809 raise ovimException("Cannot insert a task for updating network '{}' {}"
810 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
811 except Exception as e:
812 raise ovimException("Cannot insert a task for updating network '{}' {}"
813 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
814
tierno57f7bda2017-02-09 12:01:55 +0100815 return uuid
816 else:
817 raise ovimException(str(uuid), -result)
818
mirabal37829452017-03-09 14:41:21 +0100819 def new_external_port(self, port_data):
820 """
821 Create new external port and check port mapping correspondence
822 :param port_data: port_data = {
823 'region': 'datacenter region',
824 'compute_node': 'compute node id',
825 'pci': 'pci port address',
826 'vlan': 'net vlan',
827 'net_id': 'net id',
828 'tenant_id': 'tenant id',
829 'mac': 'switch mac',
830 'name': 'port name'
831 'ip_address': 'ip address - optional'}
832 :return:
833 """
834
835 port_data['type'] = 'external'
836
837 if port_data.get('net_id'):
838 # check that new net has the correct type
839 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
840 if result < 0:
841 raise ovimException(str(new_net), -result)
842 # insert in data base
843 db_filter = {}
844
845 if port_data.get('region'):
846 db_filter['region'] = port_data['region']
847 if port_data.get('pci'):
848 db_filter['pci'] = port_data['pci']
849 if port_data.get('compute_node'):
850 db_filter['compute_node'] = port_data['compute_node']
851
852 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
853 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
854
855 if not len(port_mapping_data):
856 raise ovimException("No port mapping founded for region='{}', compute id='{}' and pci='{}'".
857 format(db_filter['region'], db_filter['compute_node'], db_filter['pci']),
858 HTTP_Not_Found)
859 elif len(port_mapping_data) > 1:
860 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
861 HTTP_Conflict)
862
863 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
864 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
865 port_data['switch_port'] = port_mapping_data[0]['switch_port']
866 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
867
868 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
869 del port_data['compute_node']
870 del port_data['region']
871 del port_data['pci']
872
873 result, uuid = self.db.new_row('ports', port_data, True, True)
874 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100875 try:
876 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
877 except ovimException as e:
878 raise ovimException("Cannot insert a task for updating network '{}' {}".
879 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
880 except Exception as e:
881 raise ovimException("Cannot insert a task for updating network '{}' {}"
882 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
mirabal37829452017-03-09 14:41:21 +0100883 return uuid
884 else:
885 raise ovimException(str(uuid), -result)
886
mirabal7bbf50e2017-03-13 15:15:18 +0100887 def net_update_ofc_thread(self, net_id, ofc_id=None):
888 """
889 Insert a update net task by net id or ofc_id for each ofc thread
890 :param net_id: network id
891 :param ofc_id: openflow controller id
892 :return:
893 """
894 if not net_id:
895 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
896
897 switch_dpid = None
898 r = -1
899 c = 'No valid ofc_id or switch_dpid received'
900
901 if not ofc_id:
902 ports = self.get_ports(filter={"net_id": net_id})
903 for port in ports:
904 port_ofc_id = port.get('ofc_id', None)
905 if port_ofc_id:
906 ofc_id = port['ofc_id']
907 switch_dpid = port['switch_dpid']
908 break
909
910 # If no ofc_id found it, default ofc_id is used.
911 if not ofc_id and not switch_dpid:
912 ofc_id = "Default"
913
914 if ofc_id and ofc_id in self.config['ofcs_thread']:
915 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
916 elif switch_dpid:
917
918 ofcs_dpid_list = self.config['ofcs_thread_dpid']
919 for ofc_t in ofcs_dpid_list:
920 if switch_dpid in ofc_t:
921 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
922
923 if r < 0:
924 message = "Cannot insert a task for updating network '$s', %s", net_id, c
925 self.logger.error(message)
926 raise ovimException(message, HTTP_Internal_Server_Error)
927
tierno57f7bda2017-02-09 12:01:55 +0100928 def delete_port(self, port_id):
929 # Look for the previous port data
930 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
931 if result < 0:
932 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
933 # delete from the data base
934 result, content = self.db.delete_row('ports', port_id)
935 if result == 0:
936 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
937 elif result < 0:
938 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
939 # update network
940 network = ports[0].get('net_id', None)
941 if network:
942 # change of net.
mirabal7bbf50e2017-03-13 15:15:18 +0100943
944 try:
945 self.net_update_ofc_thread(network)
946 except ovimException as e:
947 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
948 HTTP_Internal_Server_Error)
949 except Exception as e:
950 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
951 HTTP_Internal_Server_Error)
952
tierno57f7bda2017-02-09 12:01:55 +0100953 return content
954
tierno57f7bda2017-02-09 12:01:55 +0100955 def edit_port(self, port_id, port_data, admin=True):
956 # Look for the previous port data
957 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
958 if result < 0:
959 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
960 elif result == 0:
961 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
962 port = content[0]
963 nets = []
964 host_id = None
965 result = 1
966 if 'net_id' in port_data:
967 # change of net.
968 old_net = port.get('net_id', None)
969 new_net = port_data['net_id']
970 if old_net != new_net:
971
972 if new_net:
973 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
974 if old_net:
975 nets.append(old_net)
976 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
977 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
978 elif port['type'] == 'external' and not admin:
979 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
980 if new_net:
981 # check that new net has the correct type
982 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
983 if result < 0:
984 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
985 # change VLAN for SR-IOV ports
986 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
987 if new_net:
988 port_data["vlan"] = None
989 else:
990 port_data["vlan"] = new_net_dict["vlan"]
991 # get host where this VM is allocated
992 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
993 if result > 0:
994 host_id = content[0]["host_id"]
995
996 # insert in data base
997 if result >= 0:
998 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
999
1000 # Insert task to complete actions
1001 if result > 0:
1002 for net_id in nets:
mirabal7bbf50e2017-03-13 15:15:18 +01001003 try:
1004 self.net_update_ofc_thread(net_id)
1005 except ovimException as e:
1006 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1007 HTTP_Internal_Server_Error)
1008 except Exception as e:
1009 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1010 HTTP_Internal_Server_Error)
1011
tierno57f7bda2017-02-09 12:01:55 +01001012 if host_id:
1013 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1014 if r < 0:
1015 self.logger.error("Error updating network '{}' {}".format(r,v))
1016 # TODO Do something if fails
1017 if result >= 0:
1018 return port_id
1019 else:
1020 raise ovimException("Error {}".format(content), http_code=-result)
mirabalb716ac52017-02-10 14:47:53 +01001021
mirabal9e194592017-02-17 11:03:25 +01001022 def new_of_controller(self, ofc_data):
1023 """
1024 Create a new openflow controller into DB
1025 :param ofc_data: Dict openflow controller data
1026 :return: openflow controller dpid
1027 """
1028
mirabal580435e2017-03-01 16:17:10 +01001029 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
mirabal9e194592017-02-17 11:03:25 +01001030 if result < 0:
mirabal580435e2017-03-01 16:17:10 +01001031 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1032
1033 ofc_data['uuid'] = ofc_uuid
1034 of_conn = self._load_of_module(ofc_data)
1035 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1036
1037 return ofc_uuid
mirabal9e194592017-02-17 11:03:25 +01001038
1039 def edit_of_controller(self, of_id, ofc_data):
1040 """
1041 Edit an openflow controller entry from DB
1042 :return:
1043 """
1044 if not ofc_data:
1045 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1046
1047 old_of_controller = self.show_of_controller(of_id)
1048
1049 if old_of_controller:
1050 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1051 if result >= 0:
1052 return ofc_data
1053 else:
1054 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1055 http_code=-result)
1056 else:
1057 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1058 http_code=HTTP_Internal_Server_Error)
1059
1060 def delete_of_controller(self, of_id):
1061 """
1062 Delete an openflow controller from DB.
1063 :param of_id: openflow controller dpid
1064 :return:
1065 """
1066
mirabal580435e2017-03-01 16:17:10 +01001067 ofc = self.show_of_controller(of_id)
1068
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001069 result, content = self.db.delete_row("ofcs", of_id)
mirabal9e194592017-02-17 11:03:25 +01001070 if result < 0:
1071 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1072 elif result == 0:
1073 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
mirabal580435e2017-03-01 16:17:10 +01001074
1075 ofc_thread = self.config['ofcs_thread'][of_id]
1076 del self.config['ofcs_thread'][of_id]
1077 for ofc_th in self.config['ofcs_thread_dpid']:
1078 if ofc['dpid'] in ofc_th:
1079 self.config['ofcs_thread_dpid'].remove(ofc_th)
1080
1081 ofc_thread.insert_task("exit")
1082 #ofc_thread.join()
1083
mirabal9e194592017-02-17 11:03:25 +01001084 return content
1085
1086 def show_of_controller(self, uuid):
1087 """
1088 Show an openflow controller by dpid from DB.
1089 :param db_filter: List with where query parameters
1090 :return:
1091 """
1092
1093 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1094
1095 if result == 0:
1096 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1097 http_code=HTTP_Not_Found)
1098 elif result < 0:
1099 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1100 http_code=HTTP_Internal_Server_Error)
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001101 return content[0]
mirabal9e194592017-02-17 11:03:25 +01001102
mirabalfbfb7972017-02-27 17:36:17 +01001103 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
mirabal9e194592017-02-17 11:03:25 +01001104 """
1105 Show an openflow controllers from DB.
1106 :param columns: List with SELECT query parameters
1107 :param db_filter: List with where query parameters
mirabalfbfb7972017-02-27 17:36:17 +01001108 :param limit: result Limit
mirabal9e194592017-02-17 11:03:25 +01001109 :return:
1110 """
mirabalfbfb7972017-02-27 17:36:17 +01001111 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
mirabal9e194592017-02-17 11:03:25 +01001112
1113 if result < 0:
1114 raise ovimException(str(content), -result)
1115
1116 return content
1117
mirabalfbfb7972017-02-27 17:36:17 +01001118 def get_tenants(self, columns=None, db_filter={}, limit=None):
1119 """
1120 Retrieve tenant list from DB
1121 :param columns: List with SELECT query parameters
1122 :param db_filter: List with where query parameters
1123 :param limit: result limit
1124 :return:
1125 """
1126 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1127 if result < 0:
1128 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1129 else:
1130 convert_boolean(content, ('enabled',))
1131 return content
1132
1133 def show_tenant_id(self, tenant_id):
1134 """
1135 Get tenant from DB by id
1136 :param tenant_id: tenant id
1137 :return:
1138 """
1139 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1140 WHERE={"uuid": tenant_id})
1141 if result < 0:
1142 raise ovimException(str(content), -result)
1143 elif result == 0:
1144 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1145 else:
1146 convert_boolean(content, ('enabled',))
1147 return content[0]
1148
1149 def new_tentant(self, tenant):
1150 """
1151 Create a tenant and store in DB
1152 :param tenant: Dictionary with tenant data
1153 :return: the uuid of created tenant. Raise exception upon error
1154 """
1155
1156 # insert in data base
1157 result, tenant_uuid = self.db.new_tenant(tenant)
1158
1159 if result >= 0:
1160 return tenant_uuid
1161 else:
1162 raise ovimException(str(tenant_uuid), -result)
1163
1164 def delete_tentant(self, tenant_id):
1165 """
1166 Delete a tenant from the database.
1167 :param tenant_id: Tenant id
1168 :return: delete tenant id
1169 """
1170
1171 # check permissions
1172 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1173 WHERE={'tenant_id': tenant_id})
1174 if r <= 0:
1175 tenants_flavors = ()
1176 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1177 WHERE={'tenant_id': tenant_id})
1178 if r <= 0:
1179 tenants_images = ()
1180
1181 result, content = self.db.delete_row('tenants', tenant_id)
1182 if result == 0:
1183 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1184 elif result > 0:
1185 for flavor in tenants_flavors:
1186 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1187 for image in tenants_images:
1188 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1189 return content
1190 else:
1191 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1192
1193 def edit_tenant(self, tenant_id, tenant_data):
1194 """
1195 Update a tenant data identified by tenant id
1196 :param tenant_id: tenant id
1197 :param tenant_data: Dictionary with tenant data
1198 :return:
1199 """
1200
1201 # Look for the previous data
1202 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1203 if result < 0:
1204 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1205 HTTP_Internal_Server_Error)
1206 elif result == 0:
1207 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1208
1209 # insert in data base
1210 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1211 if result >= 0:
1212 return content
1213 else:
1214 raise ovimException(str(content), -result)
1215
mirabal6045a9d2017-03-06 11:36:55 +01001216 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1217 """
1218 Create new port mapping entry
1219 :param of_maps: List with port mapping information
1220 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1221 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1222 :param ofc_id: ofc id
1223 :param switch_dpid: switch dpid
1224 :param region: datacenter region id
1225 :return:
1226 """
1227
1228 for map in of_maps:
1229 if ofc_id:
1230 map['ofc_id'] = ofc_id
1231 if switch_dpid:
1232 map['switch_dpid'] = switch_dpid
1233 if region:
1234 map['region'] = region
1235
1236 for of_map in of_maps:
1237 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1238 if result > 0:
1239 of_map["uuid"] = uuid
1240 else:
1241 raise ovimException(str(uuid), -result)
1242 return of_maps
1243
1244 def clear_of_port_mapping(self, db_filter={}):
1245 """
1246 Clear port mapping filtering using db_filter dict
1247 :param db_filter: Parameter to filter during remove process
1248 :return:
1249 """
1250 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1251 # delete_row_by_key
1252 if result >= 0:
1253 return content
1254 else:
1255 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1256 HTTP_Internal_Server_Error)
1257
1258 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1259 """
1260 Retrive port mapping from DB
1261 :param column:
1262 :param db_filter:
1263 :return:
1264 """
1265 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1266
1267 if result < 0:
1268 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1269 raise ovimException(str(content), -result)
1270 else:
1271 return content
1272
mirabalb716ac52017-02-10 14:47:53 +01001273 def get_dhcp_controller(self):
1274 """
1275 Create an host_thread object for manage openvim controller and not create a thread for itself
1276 :return: dhcp_host openvim controller object
1277 """
1278
1279 if 'openvim_controller' in self.config['host_threads']:
1280 return self.config['host_threads']['openvim_controller']
1281
1282 bridge_ifaces = []
1283 controller_ip = self.config['ovs_controller_ip']
1284 ovs_controller_user = self.config['ovs_controller_user']
1285
1286 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1287 host_develop_mode = True if self.config['mode'] == 'development' else False
1288
1289 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
tierno686b3952017-03-10 13:57:24 +01001290 db=self.db_of,
1291 db_lock=self.db_lock, test=host_test_mode,
mirabalb716ac52017-02-10 14:47:53 +01001292 image_path=self.config['image_path'], version=self.config['version'],
1293 host_id='openvim_controller', develop_mode=host_develop_mode,
1294 develop_bridge_iface=bridge_ifaces)
1295
1296 self.config['host_threads']['openvim_controller'] = dhcp_host
1297 if not host_test_mode:
1298 dhcp_host.ssh_connect()
1299 return dhcp_host
1300
mirabal18f5de32017-02-13 12:41:49 +01001301 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
mirabalb716ac52017-02-10 14:47:53 +01001302 """
1303 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1304 :param vlan: vlan identifier
1305 :param first_ip: First dhcp range ip
1306 :param last_ip: Last dhcp range ip
1307 :param cidr: net cidr
mirabale9f6f1a2017-02-16 17:57:35 +01001308 :param gateway: net gateway
mirabalb716ac52017-02-10 14:47:53 +01001309 :return:
1310 """
1311 ip_tools = IPNetwork(cidr)
1312 dhcp_netmask = str(ip_tools.netmask)
1313 ip_range = [first_ip, last_ip]
1314
1315 dhcp_path = self.config['ovs_controller_file_path']
1316
1317 controller_host = self.get_dhcp_controller()
1318 controller_host.create_linux_bridge(vlan)
1319 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
mirabal18f5de32017-02-13 12:41:49 +01001320 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
mirabalb716ac52017-02-10 14:47:53 +01001321
1322