blob: 6472cc1df8fda8c61a28273b26866642b162b3e2 [file] [log] [blame]
mirabald87877c2017-03-31 15:15:52 +02001#!/usr/bin/env python
tierno57f7bda2017-02-09 12:01:55 +01002# -*- 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
tierno51068952017-04-26 15:09:48 +020025"""
tierno57f7bda2017-02-09 12:01:55 +010026This is the thread for the http server North API.
27Two thread will be launched, with normal and administrative permissions.
tierno51068952017-04-26 15:09:48 +020028"""
tierno57f7bda2017-02-09 12:01:55 +010029
30import threading
garciadeblas071df952017-04-21 11:12:50 +020031import vim_db
tierno57f7bda2017-02-09 12:01:55 +010032import logging
tierno29d80242017-04-25 18:07:08 +020033# import imp
tierno95a9e832017-04-27 18:49:37 +020034import os.path
mirabal9f657102017-04-10 20:05:40 +020035import argparse
mirabal65ba8f82017-02-15 12:36:33 +010036from netaddr import IPNetwork
37from jsonschema import validate as js_v, exceptions as js_e
garciadeblas071df952017-04-21 11:12:50 +020038import host_thread as ht
39import dhcp_thread as dt
40import openflow_thread as oft
41import openflow_conn
mirabal9f657102017-04-10 20:05:40 +020042
tierno51068952017-04-26 15:09:48 +020043__author__ = "Alfonso Tierno, Leonardo Mirabal"
44__date__ = "$06-Feb-2017 12:07:15$"
mirabalcaeb2242017-05-31 10:52:22 -050045__version__ = "0.5.17-r533"
46version_date = "Jun 2017"
47database_version = 20 #needed database schema version
tierno57f7bda2017-02-09 12:01:55 +010048
49HTTP_Bad_Request = 400
50HTTP_Unauthorized = 401
51HTTP_Not_Found = 404
52HTTP_Forbidden = 403
53HTTP_Method_Not_Allowed = 405
54HTTP_Not_Acceptable = 406
55HTTP_Request_Timeout = 408
56HTTP_Conflict = 409
57HTTP_Service_Unavailable = 503
58HTTP_Internal_Server_Error= 500
59
60
61def convert_boolean(data, items):
62 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
63 It assumes that bandwidth is well formed
64 Attributes:
65 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
66 'items': tuple of keys to convert
67 Return:
68 None
69 '''
70 if type(data) is dict:
71 for k in data.keys():
72 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
73 convert_boolean(data[k], items)
74 if k in items:
75 if type(data[k]) is str:
76 if data[k] == "false":
77 data[k] = False
78 elif data[k] == "true":
79 data[k] = True
80 if type(data) is tuple or type(data) is list:
81 for k in data:
82 if type(k) is dict or type(k) is tuple or type(k) is list:
83 convert_boolean(k, items)
84
85
86
87class ovimException(Exception):
88 def __init__(self, message, http_code=HTTP_Bad_Request):
89 self.http_code = http_code
90 Exception.__init__(self, message)
91
92
93class ovim():
94 running_info = {} #TODO OVIM move the info of running threads from config_dic to this static variable
mirabal580435e2017-03-01 16:17:10 +010095 of_module = {}
96
tierno57f7bda2017-02-09 12:01:55 +010097 def __init__(self, configuration):
98 self.config = configuration
tierno46ca3a92017-04-05 19:49:24 +020099 self.logger_name = configuration.get("logger_name", "openvim")
100 self.logger = logging.getLogger(self.logger_name)
tierno57f7bda2017-02-09 12:01:55 +0100101 self.db = None
tierno46ca3a92017-04-05 19:49:24 +0200102 self.db = self._create_database_connection()
mirabal580435e2017-03-01 16:17:10 +0100103 self.of_test_mode = False
tierno57f7bda2017-02-09 12:01:55 +0100104
105 def _create_database_connection(self):
106 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
tierno46ca3a92017-04-05 19:49:24 +0200107 self.logger_name + ".db", self.config.get('log_level_db'))
tierno57f7bda2017-02-09 12:01:55 +0100108 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
109 self.config['db_name']) == -1:
110 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
111 # self.config['db_host'])
112 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
113 self.config['db_user'],
114 self.config['db_host']) )
115 return db
116
mirabale9f6f1a2017-02-16 17:57:35 +0100117 @staticmethod
mirabal50a052f2017-03-27 18:08:07 +0200118 def get_version():
119 return __version__
120
121 @staticmethod
122 def get_version_date():
123 return version_date
124
125 @staticmethod
126 def get_database_version():
127 return database_version
128
129 @staticmethod
mirabale9f6f1a2017-02-16 17:57:35 +0100130 def _check_dhcp_data_integrity(network):
131 """
132 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
133 :param network: list with user nets paramters
134 :return:
135 """
136 if "cidr" in network:
137 cidr = network["cidr"]
138 ip_tools = IPNetwork(cidr)
139 cidr_len = ip_tools.prefixlen
140 if cidr_len > 29:
141 return False
142
143 ips = IPNetwork(cidr)
144 if "dhcp_first_ip" not in network:
mirabal72fcda72017-05-09 11:01:06 +0200145 network["dhcp_first_ip"] = str(ips[3])
mirabale9f6f1a2017-02-16 17:57:35 +0100146 if "dhcp_last_ip" not in network:
147 network["dhcp_last_ip"] = str(ips[-2])
148 if "gateway_ip" not in network:
mirabal72fcda72017-05-09 11:01:06 +0200149 network["gateway_ip"] = str(ips[2])
mirabale9f6f1a2017-02-16 17:57:35 +0100150
151 return True
152 else:
153 return False
154
155 @staticmethod
156 def _check_valid_uuid(uuid):
157 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
158 try:
159 js_v(uuid, id_schema)
160 return True
161 except js_e.ValidationError:
162 return False
tierno57f7bda2017-02-09 12:01:55 +0100163
164 def start_service(self):
mirabal580435e2017-03-01 16:17:10 +0100165 """
166 Start ovim services
167 :return:
168 """
tierno2db743b2017-03-28 17:23:15 +0200169 global database_version
mirabal580435e2017-03-01 16:17:10 +0100170 # if self.running_info:
tierno57f7bda2017-02-09 12:01:55 +0100171 # return #TODO service can be checked and rebuild broken threads
172 r = self.db.get_db_version()
tierno95a9e832017-04-27 18:49:37 +0200173 db_path = __file__
174 db_path = db_path[:db_path.rfind("/")]
175 if os.path.exists(db_path + "/database_utils/migrate_vim_db.sh"):
176 db_path += "/database_utils"
177 else:
178 db_path += "/../database_utils"
179
mirabal580435e2017-03-01 16:17:10 +0100180 if r[0] < 0:
tierno95a9e832017-04-27 18:49:37 +0200181 raise ovimException("DATABASE is not valid. If you think it is corrupted, you can init it with"
182 " '{db_path}/init_vim_db.sh' script".format(db_path=db_path))
183 elif r[0] != database_version:
184 raise ovimException("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
185 " with '{db_path}/migrate_vim_db.sh {target}'".format(
186 current=r[0], target=database_version, db_path=db_path))
tierno46ca3a92017-04-05 19:49:24 +0200187 self.logger.critical("Starting ovim server version: '{} {}' database version '{}'".format(
188 self.get_version(), self.get_version_date(), self.get_database_version()))
tierno57f7bda2017-02-09 12:01:55 +0100189 # create database connection for openflow threads
tiernoe0c28c12017-05-04 18:44:40 +0200190 self.config["db"] = self._create_database_connection()
191 self.config["db_lock"] = threading.Lock()
tierno57f7bda2017-02-09 12:01:55 +0100192
mirabal580435e2017-03-01 16:17:10 +0100193 self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
mirabal42ca0092017-06-14 05:42:30 -0500194
195 # Create one thread for each host
196 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
197 host_develop_mode = True if self.config['mode'] == 'development' else False
198 host_develop_bridge_iface = self.config.get('development_bridge', None)
199
200 # get host list from data base before starting threads
201 r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid', 'password', 'keyfile'),
202 FROM='hosts', WHERE={'status': 'ok'})
203 if r < 0:
204 raise ovimException("Cannot get hosts from database {}".format(hosts))
205
206 self.config['host_threads'] = {}
207
208 for host in hosts:
209 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.config["db"],
210 password=host['password'],
211 keyfile=host.get('keyfile', self.config["host_ssh_keyfile"]),
212 db_lock=self.config["db_lock"], test=host_test_mode,
213 image_path=self.config['host_image_path'],
214 version=self.config['version'], host_id=host['uuid'],
215 develop_mode=host_develop_mode,
216 develop_bridge_iface=host_develop_bridge_iface,
217 logger_name=self.logger_name + ".host." + host['name'],
218 debug=self.config.get('log_level_host'))
219
220 try:
221 thread.check_connectivity()
222 except Exception as e:
223 self.logger.critical('Error detected for compute = {} with ip = {}'
224 .format(host['name'], host['ip_name']))
mirabald48f4f92017-06-29 10:04:21 -0500225 thread.start()
mirabal42ca0092017-06-14 05:42:30 -0500226 self.config['host_threads'][host['uuid']] = thread
227
mirabal580435e2017-03-01 16:17:10 +0100228 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
229 # speed in Gbit/s
230
tierno57f7bda2017-02-09 12:01:55 +0100231 self.config['dhcp_nets'] = []
232 self.config['bridge_nets'] = []
233 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
mirabal580435e2017-03-01 16:17:10 +0100234 # skip 'development_bridge'
tierno57f7bda2017-02-09 12:01:55 +0100235 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
236 continue
237 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
238
239 # check if this bridge is already used (present at database) for a network)
240 used_bridge_nets = []
241 for brnet in self.config['bridge_nets']:
tierno686b3952017-03-10 13:57:24 +0100242 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
tierno57f7bda2017-02-09 12:01:55 +0100243 if r > 0:
244 brnet[3] = nets[0]['uuid']
245 used_bridge_nets.append(brnet[0])
246 if self.config.get("dhcp_server"):
247 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
248 self.config['dhcp_nets'].append(nets[0]['uuid'])
249 if len(used_bridge_nets) > 0:
250 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
251 # get nets used by dhcp
252 if self.config.get("dhcp_server"):
253 for net in self.config["dhcp_server"].get("nets", ()):
tierno686b3952017-03-10 13:57:24 +0100254 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
tierno57f7bda2017-02-09 12:01:55 +0100255 if r > 0:
256 self.config['dhcp_nets'].append(nets[0]['uuid'])
257
mirabal580435e2017-03-01 16:17:10 +0100258 # OFC default
259 self._start_ofc_default_task()
tierno57f7bda2017-02-09 12:01:55 +0100260
mirabal580435e2017-03-01 16:17:10 +0100261 # OFC per tenant in DB
262 self._start_of_db_tasks()
tierno57f7bda2017-02-09 12:01:55 +0100263
264 # create dhcp_server thread
265 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
266 dhcp_params = self.config.get("dhcp_server")
267 if dhcp_params:
268 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
tiernoe0c28c12017-05-04 18:44:40 +0200269 db=self.config["db"], db_lock=self.config["db_lock"],
270 logger_name=self.logger_name + ".dhcp",
tierno43044b02017-04-06 18:58:24 +0200271 debug=self.config.get('log_level_of'))
tierno57f7bda2017-02-09 12:01:55 +0100272 thread.start()
273 self.config['dhcp_thread'] = thread
274
mirabal580435e2017-03-01 16:17:10 +0100275
tierno57f7bda2017-02-09 12:01:55 +0100276
mirabalb716ac52017-02-10 14:47:53 +0100277 # create ovs dhcp thread
278 result, content = self.db.get_table(FROM='nets')
279 if result < 0:
280 self.logger.error("http_get_ports Error %d %s", result, content)
281 raise ovimException(str(content), -result)
282
283 for net in content:
284 net_type = net['type']
tiernoa6933042017-05-24 16:54:33 +0200285 if (net_type == 'bridge_data' or net_type == 'bridge_man') and \
286 net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
287 try:
mirabal65ba8f82017-02-15 12:36:33 +0100288 self.launch_dhcp_server(net['vlan'],
289 net['dhcp_first_ip'],
290 net['dhcp_last_ip'],
291 net['cidr'],
292 net['gateway_ip'])
tiernoa6933042017-05-24 16:54:33 +0200293 except Exception as e:
294 self.logger.error("Fail at launching dhcp server for net_id='%s' net_name='%s': %s",
295 net["uuid"], net["name"], str(e))
296 self.db.update_rows("nets", {"status": "ERROR",
297 "last_error": "Fail at launching dhcp server: " + str(e)},
298 {"uuid": net["uuid"]})
mirabalb716ac52017-02-10 14:47:53 +0100299
mirabal580435e2017-03-01 16:17:10 +0100300 def _start_of_db_tasks(self):
301 """
302 Start ofc task for existing ofcs in database
303 :param db_of:
304 :param db_lock:
305 :return:
306 """
307 ofcs = self.get_of_controllers()
308
309 for ofc in ofcs:
310 of_conn = self._load_of_module(ofc)
311 # create ofc thread per of controller
312 self._create_ofc_task(ofc['uuid'], ofc['dpid'], of_conn)
313
314 def _create_ofc_task(self, ofc_uuid, dpid, of_conn):
315 """
316 Create an ofc thread for handle each sdn controllers
317 :param ofc_uuid: sdn controller uuid
318 :param dpid: sdn controller dpid
319 :param of_conn: OF_conn module
320 :return:
321 """
322 if 'ofcs_thread' not in self.config and 'ofcs_thread_dpid' not in self.config:
323 ofcs_threads = {}
324 ofcs_thread_dpid = []
325 else:
326 ofcs_threads = self.config['ofcs_thread']
327 ofcs_thread_dpid = self.config['ofcs_thread_dpid']
328
329 if ofc_uuid not in ofcs_threads:
330 ofc_thread = self._create_ofc_thread(of_conn, ofc_uuid)
331 if ofc_uuid == "Default":
332 self.config['of_thread'] = ofc_thread
333
334 ofcs_threads[ofc_uuid] = ofc_thread
335 self.config['ofcs_thread'] = ofcs_threads
336
337 ofcs_thread_dpid.append({dpid: ofc_thread})
338 self.config['ofcs_thread_dpid'] = ofcs_thread_dpid
339
340 def _start_ofc_default_task(self):
341 """
342 Create default ofc thread
343 """
344 if 'of_controller' not in self.config \
345 and 'of_controller_ip' not in self.config \
346 and 'of_controller_port' not in self.config \
347 and 'of_controller_dpid' not in self.config:
348 return
349
350 # OF THREAD
351 db_config = {}
352 db_config['ip'] = self.config.get('of_controller_ip')
353 db_config['port'] = self.config.get('of_controller_port')
354 db_config['dpid'] = self.config.get('of_controller_dpid')
355 db_config['type'] = self.config.get('of_controller')
356 db_config['user'] = self.config.get('of_user')
357 db_config['password'] = self.config.get('of_password')
358
359 # create connector to the openflow controller
360 # load other parameters starting by of_ from config dict in a temporal dict
361
362 of_conn = self._load_of_module(db_config)
363 # create openflow thread
364 self._create_ofc_task("Default", db_config['dpid'], of_conn)
365
366 def _load_of_module(self, db_config):
367 """
368 import python module for each SDN controller supported
mirabalf9a1a8d2017-03-15 12:42:27 +0100369 :param db_config: SDN dn information
mirabal580435e2017-03-01 16:17:10 +0100370 :return: Module
371 """
372 if not db_config:
373 raise ovimException("No module found it", HTTP_Internal_Server_Error)
374
375 module_info = None
376
377 try:
378 if self.of_test_mode:
mirabal6c600652017-03-16 17:22:57 +0100379 return openflow_conn.OfTestConnector({"name": db_config['type'],
380 "dpid": db_config['dpid'],
381 "of_debug": self.config['log_level_of']})
mirabal580435e2017-03-01 16:17:10 +0100382 temp_dict = {}
383
384 if db_config:
385 temp_dict['of_ip'] = db_config['ip']
386 temp_dict['of_port'] = db_config['port']
387 temp_dict['of_dpid'] = db_config['dpid']
388 temp_dict['of_controller'] = db_config['type']
montesmorenoba1862b2017-04-06 10:07:44 +0000389 temp_dict['of_user'] = db_config.get('user')
390 temp_dict['of_password'] = db_config.get('password')
mirabal580435e2017-03-01 16:17:10 +0100391
392 temp_dict['of_debug'] = self.config['log_level_of']
393
394 if temp_dict['of_controller'] == 'opendaylight':
395 module = "ODL"
396 else:
397 module = temp_dict['of_controller']
398
399 if module not in ovim.of_module:
garciadeblas7fa5a652017-04-26 17:55:43 +0200400 try:
401 pkg = __import__("osm_openvim." + module)
402 of_conn_module = getattr(pkg, module)
403 ovim.of_module[module] = of_conn_module
404 self.logger.debug("Module load from {}".format("osm_openvim." + module))
405 except Exception as e:
tierno29d80242017-04-25 18:07:08 +0200406 self.logger.error("Cannot open openflow controller module of type '%s'", module)
407 raise ovimException("Cannot open openflow controller of type module '{}'"
408 "Revise it is installed".format(module),
409 HTTP_Internal_Server_Error)
mirabal580435e2017-03-01 16:17:10 +0100410 else:
411 of_conn_module = ovim.of_module[module]
tierno29d80242017-04-25 18:07:08 +0200412 return of_conn_module.OF_conn(temp_dict)
413 except Exception as e:
414 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
415 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e).__name__, str(e)),
mirabal580435e2017-03-01 16:17:10 +0100416 HTTP_Internal_Server_Error)
417
418 def _create_ofc_thread(self, of_conn, ofc_uuid="Default"):
419 """
420 Create and launch a of thread
421 :return: thread obj
422 """
423 # create openflow thread
424
montesmoreno92827552017-03-30 13:24:17 +0200425 #if 'of_controller_nets_with_same_vlan' in self.config:
426 # ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
427 #else:
428 # ofc_net_same_vlan = False
429 ofc_net_same_vlan = False
mirabal580435e2017-03-01 16:17:10 +0100430
tiernoe0c28c12017-05-04 18:44:40 +0200431 thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.config["db"],
432 db_lock=self.config["db_lock"],
433 pmp_with_same_vlan=ofc_net_same_vlan,
434 logger_name=self.logger_name + ".ofc." + ofc_uuid,
435 debug=self.config.get('log_level_of'))
mirabal580435e2017-03-01 16:17:10 +0100436 #r, c = thread.OF_connector.obtain_port_correspondence()
437 #if r < 0:
438 # raise ovimException("Cannot get openflow information %s", c)
439 thread.start()
440 return thread
441
tierno57f7bda2017-02-09 12:01:55 +0100442 def stop_service(self):
443 threads = self.config.get('host_threads', {})
444 if 'of_thread' in self.config:
445 threads['of'] = (self.config['of_thread'])
mirabal580435e2017-03-01 16:17:10 +0100446 if 'ofcs_thread' in self.config:
447 ofcs_thread = self.config['ofcs_thread']
448 for ofc in ofcs_thread:
449 threads[ofc] = ofcs_thread[ofc]
450
tierno57f7bda2017-02-09 12:01:55 +0100451 if 'dhcp_thread' in self.config:
452 threads['dhcp'] = (self.config['dhcp_thread'])
453
tiernoa6933042017-05-24 16:54:33 +0200454 for thread_id, thread in threads.items():
455 if thread_id == 'openvim_controller':
456 continue
tierno57f7bda2017-02-09 12:01:55 +0100457 thread.insert_task("exit")
tiernoa6933042017-05-24 16:54:33 +0200458 for thread_id, thread in threads.items():
459 if thread_id == 'openvim_controller':
460 continue
tierno57f7bda2017-02-09 12:01:55 +0100461 thread.join()
tierno57f7bda2017-02-09 12:01:55 +0100462
mirabal9e194592017-02-17 11:03:25 +0100463 def get_networks(self, columns=None, db_filter={}, limit=None):
mirabal65ba8f82017-02-15 12:36:33 +0100464 """
465 Retreive networks available
mirabale9f6f1a2017-02-16 17:57:35 +0100466 :param columns: List with select query parameters
mirabal9e194592017-02-17 11:03:25 +0100467 :param db_filter: List with where query parameters
mirabale9f6f1a2017-02-16 17:57:35 +0100468 :param limit: Query limit result
mirabal65ba8f82017-02-15 12:36:33 +0100469 :return:
470 """
mirabal9e194592017-02-17 11:03:25 +0100471 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=db_filter, LIMIT=limit)
mirabal65ba8f82017-02-15 12:36:33 +0100472
473 if result < 0:
474 raise ovimException(str(content), -result)
475
476 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
477
478 return content
479
mirabal9e194592017-02-17 11:03:25 +0100480 def show_network(self, network_id, db_filter={}):
mirabal65ba8f82017-02-15 12:36:33 +0100481 """
mirabale9f6f1a2017-02-16 17:57:35 +0100482 Get network from DB by id
483 :param network_id: net Id
mirabal9e194592017-02-17 11:03:25 +0100484 :param db_filter: List with where query parameters
mirabal65ba8f82017-02-15 12:36:33 +0100485 :return:
486 """
487 # obtain data
mirabale9f6f1a2017-02-16 17:57:35 +0100488 if not network_id:
489 raise ovimException("Not network id was not found")
mirabal9e194592017-02-17 11:03:25 +0100490 db_filter['uuid'] = network_id
mirabal65ba8f82017-02-15 12:36:33 +0100491
mirabal9e194592017-02-17 11:03:25 +0100492 result, content = self.db.get_table(FROM='nets', WHERE=db_filter, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100493
494 if result < 0:
495 raise ovimException(str(content), -result)
496 elif result == 0:
mirabale9f6f1a2017-02-16 17:57:35 +0100497 raise ovimException("show_network network '%s' not found" % network_id, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100498 else:
499 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
mirabale9f6f1a2017-02-16 17:57:35 +0100500 # get ports from DB
mirabal65ba8f82017-02-15 12:36:33 +0100501 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
mirabale9f6f1a2017-02-16 17:57:35 +0100502 WHERE={'net_id': network_id}, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100503 if len(ports) > 0:
504 content[0]['ports'] = ports
mirabal65ba8f82017-02-15 12:36:33 +0100505
mirabale9f6f1a2017-02-16 17:57:35 +0100506 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
507 return content[0]
508
509 def new_network(self, network):
mirabal65ba8f82017-02-15 12:36:33 +0100510 """
mirabale9f6f1a2017-02-16 17:57:35 +0100511 Create a net in DB
mirabal65ba8f82017-02-15 12:36:33 +0100512 :return:
513 """
514 tenant_id = network.get('tenant_id')
515
516 if tenant_id:
517 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
518 if result <= 0:
519 raise ovimException("set_network error, no tenant founded", -result)
520
521 bridge_net = None
522 # check valid params
523 net_provider = network.get('provider')
524 net_type = network.get('type')
mirabal65ba8f82017-02-15 12:36:33 +0100525 net_vlan = network.get("vlan")
526 net_bind_net = network.get("bind_net")
527 net_bind_type = network.get("bind_type")
tiernoa290d8f2017-05-03 17:42:52 +0200528 net_region = network.get("region")
mirabal65ba8f82017-02-15 12:36:33 +0100529 name = network["name"]
530
531 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
532 vlan_index = name.rfind(":")
533 if not net_bind_net and not net_bind_type and vlan_index > 1:
534 try:
535 vlan_tag = int(name[vlan_index + 1:])
536 if not vlan_tag and vlan_tag < 4096:
537 net_bind_net = name[:vlan_index]
538 net_bind_type = "vlan:" + name[vlan_index + 1:]
539 except:
540 pass
541
542 if net_bind_net:
543 # look for a valid net
544 if self._check_valid_uuid(net_bind_net):
545 net_bind_key = "uuid"
546 else:
547 net_bind_key = "name"
548 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
549 if result < 0:
550 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
551 elif result == 0:
552 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
553 elif result > 1:
554 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
555 network["bind_net"] = content[0]["uuid"]
556
557 if net_bind_type:
558 if net_bind_type[0:5] != "vlan:":
559 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
560 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
561 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
562 HTTP_Bad_Request)
563 network["bind_type"] = net_bind_type
564
565 if net_provider:
566 if net_provider[:9] == "openflow:":
567 if net_type:
568 if net_type != "ptp" and net_type != "data":
569 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
570 HTTP_Bad_Request)
571 else:
572 net_type = 'data'
573 else:
574 if net_type:
575 if net_type != "bridge_man" and net_type != "bridge_data":
576 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
577 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
578 else:
579 net_type = 'bridge_man'
580
581 if not net_type:
582 net_type = 'bridge_man'
583
584 if net_provider:
585 if net_provider[:7] == 'bridge:':
586 # check it is one of the pre-provisioned bridges
587 bridge_net_name = net_provider[7:]
588 for brnet in self.config['bridge_nets']:
589 if brnet[0] == bridge_net_name: # free
tierno11c45e72017-05-17 12:04:05 +0000590 if brnet[3]:
mirabal65ba8f82017-02-15 12:36:33 +0100591 raise ovimException("invalid 'provider:physical', "
592 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
593 bridge_net = brnet
594 net_vlan = brnet[1]
595 break
mirabale9f6f1a2017-02-16 17:57:35 +0100596 # if bridge_net==None:
597 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
598 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
599 # return
600
mirabal65ba8f82017-02-15 12:36:33 +0100601 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
602 # look for a free precreated nets
603 for brnet in self.config['bridge_nets']:
604 if not brnet[3]: # free
605 if not bridge_net:
606 if net_type == 'bridge_man': # look for the smaller speed
607 if brnet[2] < bridge_net[2]:
608 bridge_net = brnet
609 else: # look for the larger speed
610 if brnet[2] > bridge_net[2]:
611 bridge_net = brnet
612 else:
613 bridge_net = brnet
614 net_vlan = brnet[1]
615 if not bridge_net:
616 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
617 "will overcome this limit", HTTP_Bad_Request)
618 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100619 self.logger.debug("using net " + bridge_net)
mirabal65ba8f82017-02-15 12:36:33 +0100620 net_provider = "bridge:" + bridge_net[0]
621 net_vlan = bridge_net[1]
622 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
623 net_provider = 'OVS'
tiernoa290d8f2017-05-03 17:42:52 +0200624 if not net_region:
625 if net_type == "data" or net_type == "ptp":
626 net_region = "__DATA__"
627 elif net_provider == "OVS":
628 net_region = "__OVS__"
mirabal65ba8f82017-02-15 12:36:33 +0100629 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
tiernoa290d8f2017-05-03 17:42:52 +0200630 net_vlan = self.db.get_free_net_vlan(net_region)
mirabal65ba8f82017-02-15 12:36:33 +0100631 if net_vlan < 0:
632 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
633 if net_provider == 'OVS':
634 net_provider = 'OVS' + ":" + str(net_vlan)
635
636 network['provider'] = net_provider
637 network['type'] = net_type
638 network['vlan'] = net_vlan
tiernoa290d8f2017-05-03 17:42:52 +0200639 network['region'] = net_region
mirabal65ba8f82017-02-15 12:36:33 +0100640 dhcp_integrity = True
641 if 'enable_dhcp' in network and network['enable_dhcp']:
642 dhcp_integrity = self._check_dhcp_data_integrity(network)
643
644 result, content = self.db.new_row('nets', network, True, True)
645
646 if result >= 0 and dhcp_integrity:
647 if bridge_net:
648 bridge_net[3] = content
649 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
650 if network["name"] in self.config["dhcp_server"].get("nets", ()):
651 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100652 self.logger.debug("dhcp_server: add new net", content)
tierno11c45e72017-05-17 12:04:05 +0000653 elif bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
mirabal65ba8f82017-02-15 12:36:33 +0100654 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100655 self.logger.debug("dhcp_server: add new net", content, content)
mirabal65ba8f82017-02-15 12:36:33 +0100656 return content
657 else:
mirabal65ba8f82017-02-15 12:36:33 +0100658 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
mirabale9f6f1a2017-02-16 17:57:35 +0100659# TODO kei change update->edit
mirabal65ba8f82017-02-15 12:36:33 +0100660
mirabale9f6f1a2017-02-16 17:57:35 +0100661 def edit_network(self, network_id, network):
mirabal65ba8f82017-02-15 12:36:33 +0100662 """
mirabale9f6f1a2017-02-16 17:57:35 +0100663 Update entwork data byt id
mirabal65ba8f82017-02-15 12:36:33 +0100664 :return:
665 """
mirabale9f6f1a2017-02-16 17:57:35 +0100666 # Look for the previous data
667 where_ = {'uuid': network_id}
668 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
669 if result < 0:
670 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
671 elif result == 0:
672 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
673 # get ports
674 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
675 WHERE={'net_id': network_id}, LIMIT=100)
676 if result < 0:
677 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
678 if nbports > 0:
679 if 'type' in network and network['type'] != network_old[0]['type']:
680 raise ovimException("Can not change type of network while having ports attached",
681 HTTP_Method_Not_Allowed)
682 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
683 raise ovimException("Can not change vlan of network while having ports attached",
684 HTTP_Method_Not_Allowed)
mirabal65ba8f82017-02-15 12:36:33 +0100685
mirabale9f6f1a2017-02-16 17:57:35 +0100686 # check valid params
687 net_provider = network.get('provider', network_old[0]['provider'])
688 net_type = network.get('type', network_old[0]['type'])
689 net_bind_net = network.get("bind_net")
690 net_bind_type = network.get("bind_type")
691 if net_bind_net:
692 # look for a valid net
693 if self._check_valid_uuid(net_bind_net):
694 net_bind_key = "uuid"
695 else:
696 net_bind_key = "name"
697 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
698 if result < 0:
699 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
700 elif result == 0:
701 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
702 elif result > 1:
703 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
704 HTTP_Bad_Request)
705 network["bind_net"] = content[0]["uuid"]
706 if net_bind_type:
707 if net_bind_type[0:5] != "vlan:":
708 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
709 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
710 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
711 HTTP_Bad_Request)
712 if net_provider:
713 if net_provider[:9] == "openflow:":
714 if net_type != "ptp" and net_type != "data":
715 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
716 else:
717 if net_type != "bridge_man" and net_type != "bridge_data":
718 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
719 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
mirabal65ba8f82017-02-15 12:36:33 +0100720
mirabale9f6f1a2017-02-16 17:57:35 +0100721 # insert in data base
722 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
723 if result >= 0:
724 # if result > 0 and nbports>0 and 'admin_state_up' in network
725 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
726 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100727
728 try:
tiernoaa941462017-03-29 15:10:28 +0200729 if nbports:
730 self.net_update_ofc_thread(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100731 except ovimException as e:
732 raise ovimException("Error while launching openflow rules in network '{}' {}"
733 .format(network_id, str(e)), HTTP_Internal_Server_Error)
734 except Exception as e:
735 raise ovimException("Error while launching openflow rules in network '{}' {}"
736 .format(network_id, str(e)), HTTP_Internal_Server_Error)
737
mirabale9f6f1a2017-02-16 17:57:35 +0100738 if self.config.get("dhcp_server"):
739 if network_id in self.config["dhcp_nets"]:
740 self.config["dhcp_nets"].remove(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100741 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
mirabale9f6f1a2017-02-16 17:57:35 +0100742 self.config["dhcp_nets"].append(network_id)
743 else:
mirabal7bbf50e2017-03-13 15:15:18 +0100744 net_bind = network.get("bind_type", network_old[0]["bind_type"])
745 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 +0100746 "bridge_ifaces", ()):
747 self.config["dhcp_nets"].append(network_id)
748 return network_id
mirabal65ba8f82017-02-15 12:36:33 +0100749 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100750 raise ovimException(content, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100751
mirabale9f6f1a2017-02-16 17:57:35 +0100752 def delete_network(self, network_id):
mirabal9e194592017-02-17 11:03:25 +0100753 """
754 Delete network by network id
755 :param network_id: network id
756 :return:
757 """
mirabale9f6f1a2017-02-16 17:57:35 +0100758
759 # delete from the data base
760 result, content = self.db.delete_row('nets', network_id)
761
762 if result == 0:
763 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
764 elif result > 0:
765 for brnet in self.config['bridge_nets']:
766 if brnet[3] == network_id:
767 brnet[3] = None
768 break
769 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
770 self.config["dhcp_nets"].remove(network_id)
771 return content
772 else:
tierno11c45e72017-05-17 12:04:05 +0000773 raise ovimException("Error deleting network '{}': {}".format(network_id, content), -result)
mirabal65ba8f82017-02-15 12:36:33 +0100774
775 def get_openflow_rules(self, network_id=None):
776 """
777 Get openflow id from DB
778 :param network_id: Network id, if none all networks will be retrieved
779 :return: Return a list with Openflow rules per net
780 """
781 # ignore input data
782 if not network_id:
783 where_ = {}
784 else:
785 where_ = {"net_id": network_id}
mirabal65ba8f82017-02-15 12:36:33 +0100786 result, content = self.db.get_table(
mirabalf9a1a8d2017-03-15 12:42:27 +0100787 SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
mirabal65ba8f82017-02-15 12:36:33 +0100788 WHERE=where_, FROM='of_flows')
789
790 if result < 0:
791 raise ovimException(str(content), -result)
792 return content
793
mirabale9f6f1a2017-02-16 17:57:35 +0100794 def edit_openflow_rules(self, network_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100795
796 """
797 To make actions over the net. The action is to reinstall the openflow rules
798 network_id can be 'all'
799 :param network_id: Network id, if none all networks will be retrieved
800 :return : Number of nets updated
801 """
802
803 # ignore input data
804 if not network_id:
805 where_ = {}
806 else:
807 where_ = {"uuid": network_id}
808 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
809
810 if result < 0:
811 raise ovimException(str(content), -result)
812
813 for net in content:
814 if net["type"] != "ptp" and net["type"] != "data":
815 result -= 1
816 continue
mirabal7bbf50e2017-03-13 15:15:18 +0100817
818 try:
819 self.net_update_ofc_thread(net['uuid'])
820 except ovimException as e:
821 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
822 HTTP_Internal_Server_Error)
823 except Exception as e:
824 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
825 HTTP_Internal_Server_Error)
826
mirabal65ba8f82017-02-15 12:36:33 +0100827 return result
828
mirabalf9a1a8d2017-03-15 12:42:27 +0100829 def delete_openflow_rules(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100830 """
831 To make actions over the net. The action is to delete ALL openflow rules
832 :return: return operation result
833 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100834
835 if not ofc_id:
836 if 'Default' in self.config['ofcs_thread']:
837 r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
838 else:
839 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
840
841 elif ofc_id in self.config['ofcs_thread']:
842 r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
843
844 # ignore input data
845 if r < 0:
846 raise ovimException(str(c), -r)
847 else:
848 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
mirabal65ba8f82017-02-15 12:36:33 +0100849 return r
850
mirabalf9a1a8d2017-03-15 12:42:27 +0100851 def get_openflow_ports(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100852 """
853 Obtain switch ports names of openflow controller
854 :return: Return flow ports in DB
855 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100856 if not ofc_id:
857 if 'Default' in self.config['ofcs_thread']:
858 conn = self.config['ofcs_thread']['Default'].OF_connector
859 else:
860 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
861
tierno11c45e72017-05-17 12:04:05 +0000862 elif ofc_id in self.config['ofcs_thread']:
mirabalf9a1a8d2017-03-15 12:42:27 +0100863 conn = self.config['ofcs_thread'][ofc_id].OF_connector
864 else:
865 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
866 return conn.pp2ofi
tierno57f7bda2017-02-09 12:01:55 +0100867
868 def get_ports(self, columns=None, filter={}, limit=None):
869 # result, content = my.db.get_ports(where_)
870 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
871 if result < 0:
872 self.logger.error("http_get_ports Error %d %s", result, content)
873 raise ovimException(str(content), -result)
874 else:
875 convert_boolean(content, ('admin_state_up',))
876 return content
877
tierno57f7bda2017-02-09 12:01:55 +0100878 def new_port(self, port_data):
879 port_data['type'] = 'external'
880 if port_data.get('net_id'):
881 # check that new net has the correct type
882 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
883 if result < 0:
884 raise ovimException(str(new_net), -result)
885 # insert in data base
886 result, uuid = self.db.new_row('ports', port_data, True, True)
887 if result > 0:
888 if 'net_id' in port_data:
mirabal7bbf50e2017-03-13 15:15:18 +0100889 try:
890 self.net_update_ofc_thread(port_data['net_id'])
891 except ovimException as e:
892 raise ovimException("Cannot insert a task for updating network '{}' {}"
893 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
894 except Exception as e:
895 raise ovimException("Cannot insert a task for updating network '{}' {}"
896 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
897
tierno57f7bda2017-02-09 12:01:55 +0100898 return uuid
899 else:
900 raise ovimException(str(uuid), -result)
901
mirabal37829452017-03-09 14:41:21 +0100902 def new_external_port(self, port_data):
903 """
904 Create new external port and check port mapping correspondence
905 :param port_data: port_data = {
906 'region': 'datacenter region',
907 'compute_node': 'compute node id',
908 'pci': 'pci port address',
909 'vlan': 'net vlan',
910 'net_id': 'net id',
911 'tenant_id': 'tenant id',
912 'mac': 'switch mac',
913 'name': 'port name'
914 'ip_address': 'ip address - optional'}
915 :return:
916 """
917
918 port_data['type'] = 'external'
919
920 if port_data.get('net_id'):
921 # check that new net has the correct type
922 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
923 if result < 0:
924 raise ovimException(str(new_net), -result)
925 # insert in data base
926 db_filter = {}
927
928 if port_data.get('region'):
929 db_filter['region'] = port_data['region']
930 if port_data.get('pci'):
931 db_filter['pci'] = port_data['pci']
932 if port_data.get('compute_node'):
933 db_filter['compute_node'] = port_data['compute_node']
934
935 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
936 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
937
938 if not len(port_mapping_data):
tierno16007502017-03-27 16:48:32 +0200939 raise ovimException("No port mapping founded for '{}'".format(str(db_filter)),
mirabal37829452017-03-09 14:41:21 +0100940 HTTP_Not_Found)
941 elif len(port_mapping_data) > 1:
942 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
943 HTTP_Conflict)
944
945 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
946 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
947 port_data['switch_port'] = port_mapping_data[0]['switch_port']
948 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
949
950 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
montesmoreno275b1992017-03-28 15:45:02 +0200951 if 'region' in port_data:
952 del port_data['region']
953 if 'pci' in port_data:
954 del port_data['pci']
955 if 'compute_node' in port_data:
956 del port_data['compute_node']
mirabal37829452017-03-09 14:41:21 +0100957
958 result, uuid = self.db.new_row('ports', port_data, True, True)
959 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100960 try:
961 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
962 except ovimException as e:
963 raise ovimException("Cannot insert a task for updating network '{}' {}".
964 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
965 except Exception as e:
966 raise ovimException("Cannot insert a task for updating network '{}' {}"
967 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
mirabal37829452017-03-09 14:41:21 +0100968 return uuid
969 else:
970 raise ovimException(str(uuid), -result)
971
tiernoaa941462017-03-29 15:10:28 +0200972 def net_update_ofc_thread(self, net_id, ofc_id=None, switch_dpid=None):
mirabal7bbf50e2017-03-13 15:15:18 +0100973 """
974 Insert a update net task by net id or ofc_id for each ofc thread
975 :param net_id: network id
976 :param ofc_id: openflow controller id
tiernoaa941462017-03-29 15:10:28 +0200977 :param switch_dpid: switch dpid
mirabal7bbf50e2017-03-13 15:15:18 +0100978 :return:
979 """
980 if not net_id:
981 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
982
mirabal7bbf50e2017-03-13 15:15:18 +0100983 r = -1
984 c = 'No valid ofc_id or switch_dpid received'
985
986 if not ofc_id:
987 ports = self.get_ports(filter={"net_id": net_id})
988 for port in ports:
989 port_ofc_id = port.get('ofc_id', None)
990 if port_ofc_id:
991 ofc_id = port['ofc_id']
992 switch_dpid = port['switch_dpid']
993 break
tiernoaa941462017-03-29 15:10:28 +0200994 #TODO if not ofc_id: look at database table ofcs
995
mirabal7bbf50e2017-03-13 15:15:18 +0100996
997 # If no ofc_id found it, default ofc_id is used.
998 if not ofc_id and not switch_dpid:
999 ofc_id = "Default"
1000
1001 if ofc_id and ofc_id in self.config['ofcs_thread']:
1002 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
1003 elif switch_dpid:
1004
1005 ofcs_dpid_list = self.config['ofcs_thread_dpid']
1006 for ofc_t in ofcs_dpid_list:
1007 if switch_dpid in ofc_t:
1008 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
1009
1010 if r < 0:
tierno82232582017-03-15 18:09:16 +01001011 message = "Cannot insert a task for updating network '{}', {}".format(net_id, c)
mirabal7bbf50e2017-03-13 15:15:18 +01001012 self.logger.error(message)
1013 raise ovimException(message, HTTP_Internal_Server_Error)
1014
tierno57f7bda2017-02-09 12:01:55 +01001015 def delete_port(self, port_id):
1016 # Look for the previous port data
1017 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
1018 if result < 0:
1019 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
1020 # delete from the data base
1021 result, content = self.db.delete_row('ports', port_id)
1022 if result == 0:
1023 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1024 elif result < 0:
1025 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
1026 # update network
1027 network = ports[0].get('net_id', None)
1028 if network:
1029 # change of net.
mirabal7bbf50e2017-03-13 15:15:18 +01001030
1031 try:
tiernoaa941462017-03-29 15:10:28 +02001032 self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +01001033 except ovimException as e:
1034 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1035 HTTP_Internal_Server_Error)
1036 except Exception as e:
1037 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1038 HTTP_Internal_Server_Error)
1039
tierno57f7bda2017-02-09 12:01:55 +01001040 return content
1041
tierno57f7bda2017-02-09 12:01:55 +01001042 def edit_port(self, port_id, port_data, admin=True):
1043 # Look for the previous port data
1044 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
1045 if result < 0:
1046 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
1047 elif result == 0:
1048 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1049 port = content[0]
1050 nets = []
1051 host_id = None
1052 result = 1
1053 if 'net_id' in port_data:
1054 # change of net.
1055 old_net = port.get('net_id', None)
1056 new_net = port_data['net_id']
1057 if old_net != new_net:
1058
1059 if new_net:
1060 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
1061 if old_net:
1062 nets.append(old_net)
1063 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
1064 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
1065 elif port['type'] == 'external' and not admin:
1066 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
1067 if new_net:
1068 # check that new net has the correct type
1069 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
1070 if result < 0:
1071 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
1072 # change VLAN for SR-IOV ports
1073 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
1074 if new_net:
1075 port_data["vlan"] = None
1076 else:
1077 port_data["vlan"] = new_net_dict["vlan"]
1078 # get host where this VM is allocated
1079 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
1080 if result > 0:
1081 host_id = content[0]["host_id"]
1082
1083 # insert in data base
1084 if result >= 0:
1085 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
tiernoaa941462017-03-29 15:10:28 +02001086 port.update(port_data)
tierno57f7bda2017-02-09 12:01:55 +01001087
1088 # Insert task to complete actions
1089 if result > 0:
1090 for net_id in nets:
mirabal7bbf50e2017-03-13 15:15:18 +01001091 try:
tiernoaa941462017-03-29 15:10:28 +02001092 self.net_update_ofc_thread(net_id, port["ofc_id"], switch_dpid=port["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +01001093 except ovimException as e:
1094 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1095 HTTP_Internal_Server_Error)
1096 except Exception as e:
1097 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1098 HTTP_Internal_Server_Error)
1099
tierno57f7bda2017-02-09 12:01:55 +01001100 if host_id:
1101 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1102 if r < 0:
1103 self.logger.error("Error updating network '{}' {}".format(r,v))
1104 # TODO Do something if fails
1105 if result >= 0:
1106 return port_id
1107 else:
1108 raise ovimException("Error {}".format(content), http_code=-result)
mirabalb716ac52017-02-10 14:47:53 +01001109
mirabal9e194592017-02-17 11:03:25 +01001110 def new_of_controller(self, ofc_data):
1111 """
1112 Create a new openflow controller into DB
1113 :param ofc_data: Dict openflow controller data
1114 :return: openflow controller dpid
1115 """
1116
mirabal580435e2017-03-01 16:17:10 +01001117 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
mirabal9e194592017-02-17 11:03:25 +01001118 if result < 0:
mirabal580435e2017-03-01 16:17:10 +01001119 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1120
1121 ofc_data['uuid'] = ofc_uuid
1122 of_conn = self._load_of_module(ofc_data)
1123 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1124
1125 return ofc_uuid
mirabal9e194592017-02-17 11:03:25 +01001126
1127 def edit_of_controller(self, of_id, ofc_data):
1128 """
1129 Edit an openflow controller entry from DB
1130 :return:
1131 """
1132 if not ofc_data:
1133 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1134
1135 old_of_controller = self.show_of_controller(of_id)
1136
1137 if old_of_controller:
1138 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1139 if result >= 0:
1140 return ofc_data
1141 else:
1142 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1143 http_code=-result)
1144 else:
1145 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1146 http_code=HTTP_Internal_Server_Error)
1147
1148 def delete_of_controller(self, of_id):
1149 """
1150 Delete an openflow controller from DB.
1151 :param of_id: openflow controller dpid
1152 :return:
1153 """
1154
mirabal580435e2017-03-01 16:17:10 +01001155 ofc = self.show_of_controller(of_id)
1156
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001157 result, content = self.db.delete_row("ofcs", of_id)
mirabal9e194592017-02-17 11:03:25 +01001158 if result < 0:
1159 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1160 elif result == 0:
1161 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
mirabal580435e2017-03-01 16:17:10 +01001162
1163 ofc_thread = self.config['ofcs_thread'][of_id]
1164 del self.config['ofcs_thread'][of_id]
1165 for ofc_th in self.config['ofcs_thread_dpid']:
1166 if ofc['dpid'] in ofc_th:
1167 self.config['ofcs_thread_dpid'].remove(ofc_th)
1168
1169 ofc_thread.insert_task("exit")
1170 #ofc_thread.join()
1171
mirabal9e194592017-02-17 11:03:25 +01001172 return content
1173
1174 def show_of_controller(self, uuid):
1175 """
1176 Show an openflow controller by dpid from DB.
1177 :param db_filter: List with where query parameters
1178 :return:
1179 """
1180
1181 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1182
1183 if result == 0:
1184 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1185 http_code=HTTP_Not_Found)
1186 elif result < 0:
1187 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1188 http_code=HTTP_Internal_Server_Error)
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001189 return content[0]
mirabal9e194592017-02-17 11:03:25 +01001190
mirabalfbfb7972017-02-27 17:36:17 +01001191 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
mirabal9e194592017-02-17 11:03:25 +01001192 """
1193 Show an openflow controllers from DB.
1194 :param columns: List with SELECT query parameters
1195 :param db_filter: List with where query parameters
mirabalfbfb7972017-02-27 17:36:17 +01001196 :param limit: result Limit
mirabal9e194592017-02-17 11:03:25 +01001197 :return:
1198 """
mirabalfbfb7972017-02-27 17:36:17 +01001199 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
mirabal9e194592017-02-17 11:03:25 +01001200
1201 if result < 0:
1202 raise ovimException(str(content), -result)
1203
1204 return content
1205
mirabalfbfb7972017-02-27 17:36:17 +01001206 def get_tenants(self, columns=None, db_filter={}, limit=None):
1207 """
1208 Retrieve tenant list from DB
1209 :param columns: List with SELECT query parameters
1210 :param db_filter: List with where query parameters
1211 :param limit: result limit
1212 :return:
1213 """
1214 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1215 if result < 0:
1216 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1217 else:
1218 convert_boolean(content, ('enabled',))
1219 return content
1220
1221 def show_tenant_id(self, tenant_id):
1222 """
1223 Get tenant from DB by id
1224 :param tenant_id: tenant id
1225 :return:
1226 """
1227 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1228 WHERE={"uuid": tenant_id})
1229 if result < 0:
1230 raise ovimException(str(content), -result)
1231 elif result == 0:
1232 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1233 else:
1234 convert_boolean(content, ('enabled',))
1235 return content[0]
1236
1237 def new_tentant(self, tenant):
1238 """
1239 Create a tenant and store in DB
1240 :param tenant: Dictionary with tenant data
1241 :return: the uuid of created tenant. Raise exception upon error
1242 """
1243
1244 # insert in data base
1245 result, tenant_uuid = self.db.new_tenant(tenant)
1246
1247 if result >= 0:
1248 return tenant_uuid
1249 else:
1250 raise ovimException(str(tenant_uuid), -result)
1251
1252 def delete_tentant(self, tenant_id):
1253 """
1254 Delete a tenant from the database.
1255 :param tenant_id: Tenant id
1256 :return: delete tenant id
1257 """
1258
1259 # check permissions
1260 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1261 WHERE={'tenant_id': tenant_id})
1262 if r <= 0:
1263 tenants_flavors = ()
1264 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1265 WHERE={'tenant_id': tenant_id})
1266 if r <= 0:
1267 tenants_images = ()
1268
1269 result, content = self.db.delete_row('tenants', tenant_id)
1270 if result == 0:
1271 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1272 elif result > 0:
1273 for flavor in tenants_flavors:
1274 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1275 for image in tenants_images:
1276 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1277 return content
1278 else:
1279 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1280
1281 def edit_tenant(self, tenant_id, tenant_data):
1282 """
1283 Update a tenant data identified by tenant id
1284 :param tenant_id: tenant id
1285 :param tenant_data: Dictionary with tenant data
1286 :return:
1287 """
1288
1289 # Look for the previous data
1290 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1291 if result < 0:
1292 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1293 HTTP_Internal_Server_Error)
1294 elif result == 0:
1295 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1296
1297 # insert in data base
1298 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1299 if result >= 0:
1300 return content
1301 else:
1302 raise ovimException(str(content), -result)
1303
mirabal6045a9d2017-03-06 11:36:55 +01001304 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1305 """
1306 Create new port mapping entry
1307 :param of_maps: List with port mapping information
1308 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1309 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1310 :param ofc_id: ofc id
1311 :param switch_dpid: switch dpid
1312 :param region: datacenter region id
1313 :return:
1314 """
1315
1316 for map in of_maps:
1317 if ofc_id:
1318 map['ofc_id'] = ofc_id
1319 if switch_dpid:
1320 map['switch_dpid'] = switch_dpid
1321 if region:
1322 map['region'] = region
1323
1324 for of_map in of_maps:
1325 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1326 if result > 0:
1327 of_map["uuid"] = uuid
1328 else:
1329 raise ovimException(str(uuid), -result)
1330 return of_maps
1331
1332 def clear_of_port_mapping(self, db_filter={}):
1333 """
1334 Clear port mapping filtering using db_filter dict
1335 :param db_filter: Parameter to filter during remove process
1336 :return:
1337 """
1338 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1339 # delete_row_by_key
1340 if result >= 0:
1341 return content
1342 else:
1343 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1344 HTTP_Internal_Server_Error)
1345
1346 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1347 """
1348 Retrive port mapping from DB
1349 :param column:
1350 :param db_filter:
1351 :return:
1352 """
1353 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1354
1355 if result < 0:
1356 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1357 raise ovimException(str(content), -result)
1358 else:
1359 return content
1360
mirabalb716ac52017-02-10 14:47:53 +01001361 def get_dhcp_controller(self):
1362 """
1363 Create an host_thread object for manage openvim controller and not create a thread for itself
1364 :return: dhcp_host openvim controller object
1365 """
1366
1367 if 'openvim_controller' in self.config['host_threads']:
1368 return self.config['host_threads']['openvim_controller']
1369
1370 bridge_ifaces = []
1371 controller_ip = self.config['ovs_controller_ip']
tiernoa6933042017-05-24 16:54:33 +02001372 ovs_controller_user = self.config.get('ovs_controller_user')
mirabalb716ac52017-02-10 14:47:53 +01001373
1374 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1375 host_develop_mode = True if self.config['mode'] == 'development' else False
1376
1377 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
tiernoa6933042017-05-24 16:54:33 +02001378 password=self.config.get('ovs_controller_password'),
1379 keyfile=self.config.get('ovs_controller_keyfile'),
tiernoe0c28c12017-05-04 18:44:40 +02001380 db=self.config["db"], db_lock=self.config["db_lock"], test=host_test_mode,
tiernoa6933042017-05-24 16:54:33 +02001381 image_path=self.config['host_image_path'], version=self.config['version'],
mirabalb716ac52017-02-10 14:47:53 +01001382 host_id='openvim_controller', develop_mode=host_develop_mode,
tiernoe0c28c12017-05-04 18:44:40 +02001383 develop_bridge_iface=bridge_ifaces,
1384 logger_name=self.logger_name + ".host.controller",
tiernof135eff2017-04-19 19:11:53 +02001385 debug=self.config.get('log_level_host'))
tiernoa6933042017-05-24 16:54:33 +02001386 # dhcp_host.start()
mirabalb716ac52017-02-10 14:47:53 +01001387 self.config['host_threads']['openvim_controller'] = dhcp_host
mirabal42ca0092017-06-14 05:42:30 -05001388 try:
1389 dhcp_host.check_connectivity()
1390 except Exception as e:
1391 pass
1392
mirabalb716ac52017-02-10 14:47:53 +01001393 return dhcp_host
1394
mirabal18f5de32017-02-13 12:41:49 +01001395 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
mirabalb716ac52017-02-10 14:47:53 +01001396 """
1397 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1398 :param vlan: vlan identifier
1399 :param first_ip: First dhcp range ip
1400 :param last_ip: Last dhcp range ip
1401 :param cidr: net cidr
mirabale9f6f1a2017-02-16 17:57:35 +01001402 :param gateway: net gateway
mirabalb716ac52017-02-10 14:47:53 +01001403 :return:
1404 """
1405 ip_tools = IPNetwork(cidr)
1406 dhcp_netmask = str(ip_tools.netmask)
1407 ip_range = [first_ip, last_ip]
1408
1409 dhcp_path = self.config['ovs_controller_file_path']
1410
1411 controller_host = self.get_dhcp_controller()
1412 controller_host.create_linux_bridge(vlan)
mirabal72fcda72017-05-09 11:01:06 +02001413 controller_host.create_dhcp_interfaces(vlan, gateway, dhcp_netmask)
mirabal18f5de32017-02-13 12:41:49 +01001414 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
mirabalb716ac52017-02-10 14:47:53 +01001415
mirabald87877c2017-03-31 15:15:52 +02001416if __name__ == "__main__":
1417
1418 parser = argparse.ArgumentParser()
tierno46ca3a92017-04-05 19:49:24 +02001419 parser.add_argument("-v","--version", help="show ovim library version", action="store_true")
1420 parser.add_argument("--database-version", help="show required database version", action="store_true")
mirabald87877c2017-03-31 15:15:52 +02001421 args = parser.parse_args()
1422 if args.version:
1423 print ('openvimd version {} {}'.format(ovim.get_version(), ovim.get_version_date()))
1424 print ('(c) Copyright Telefonica')
tierno46ca3a92017-04-05 19:49:24 +02001425 elif args.database_version:
1426 print ('required database version: {}'.format(ovim.get_database_version()))
mirabalb716ac52017-02-10 14:47:53 +01001427