blob: b0fa78f70d8565cd79ebf9bcc0defd6a568368d2 [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$"
tiernoa290d8f2017-05-03 17:42:52 +020045__version__ = "0.5.13-r529"
tierno95a9e832017-04-27 18:49:37 +020046version_date = "May 2017"
tiernoa290d8f2017-05-03 17:42:52 +020047database_version = 18 #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
194 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
195 # speed in Gbit/s
196
tierno57f7bda2017-02-09 12:01:55 +0100197 self.config['dhcp_nets'] = []
198 self.config['bridge_nets'] = []
199 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
mirabal580435e2017-03-01 16:17:10 +0100200 # skip 'development_bridge'
tierno57f7bda2017-02-09 12:01:55 +0100201 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
202 continue
203 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
204
205 # check if this bridge is already used (present at database) for a network)
206 used_bridge_nets = []
207 for brnet in self.config['bridge_nets']:
tierno686b3952017-03-10 13:57:24 +0100208 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
tierno57f7bda2017-02-09 12:01:55 +0100209 if r > 0:
210 brnet[3] = nets[0]['uuid']
211 used_bridge_nets.append(brnet[0])
212 if self.config.get("dhcp_server"):
213 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
214 self.config['dhcp_nets'].append(nets[0]['uuid'])
215 if len(used_bridge_nets) > 0:
216 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
217 # get nets used by dhcp
218 if self.config.get("dhcp_server"):
219 for net in self.config["dhcp_server"].get("nets", ()):
tierno686b3952017-03-10 13:57:24 +0100220 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
tierno57f7bda2017-02-09 12:01:55 +0100221 if r > 0:
222 self.config['dhcp_nets'].append(nets[0]['uuid'])
223
mirabal580435e2017-03-01 16:17:10 +0100224 # OFC default
225 self._start_ofc_default_task()
tierno57f7bda2017-02-09 12:01:55 +0100226
mirabal580435e2017-03-01 16:17:10 +0100227 # OFC per tenant in DB
228 self._start_of_db_tasks()
tierno57f7bda2017-02-09 12:01:55 +0100229
230 # create dhcp_server thread
231 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
232 dhcp_params = self.config.get("dhcp_server")
233 if dhcp_params:
234 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
tiernoe0c28c12017-05-04 18:44:40 +0200235 db=self.config["db"], db_lock=self.config["db_lock"],
236 logger_name=self.logger_name + ".dhcp",
tierno43044b02017-04-06 18:58:24 +0200237 debug=self.config.get('log_level_of'))
tierno57f7bda2017-02-09 12:01:55 +0100238 thread.start()
239 self.config['dhcp_thread'] = thread
240
241 # Create one thread for each host
242 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
243 host_develop_mode = True if self.config['mode'] == 'development' else False
244 host_develop_bridge_iface = self.config.get('development_bridge', None)
mirabal580435e2017-03-01 16:17:10 +0100245
246 # get host list from data base before starting threads
tierno686b3952017-03-10 13:57:24 +0100247 r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid'), FROM='hosts', WHERE={'status': 'ok'})
mirabal580435e2017-03-01 16:17:10 +0100248 if r < 0:
249 raise ovimException("Cannot get hosts from database {}".format(hosts))
250
tierno57f7bda2017-02-09 12:01:55 +0100251 self.config['host_threads'] = {}
252 for host in hosts:
253 host['image_path'] = '/opt/VNF/images/openvim'
tiernoe0c28c12017-05-04 18:44:40 +0200254 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.config["db"],
255 db_lock=self.config["db_lock"], test=host_test_mode,
256 image_path=self.config['image_path'],
257 version=self.config['version'], host_id=host['uuid'],
258 develop_mode=host_develop_mode,
tiernof135eff2017-04-19 19:11:53 +0200259 develop_bridge_iface=host_develop_bridge_iface,
260 logger_name=self.logger_name + ".host." + host['name'],
261 debug=self.config.get('log_level_host'))
tierno57f7bda2017-02-09 12:01:55 +0100262 thread.start()
263 self.config['host_threads'][host['uuid']] = thread
264
mirabalb716ac52017-02-10 14:47:53 +0100265 # create ovs dhcp thread
266 result, content = self.db.get_table(FROM='nets')
267 if result < 0:
268 self.logger.error("http_get_ports Error %d %s", result, content)
269 raise ovimException(str(content), -result)
270
271 for net in content:
272 net_type = net['type']
mirabale9f6f1a2017-02-16 17:57:35 +0100273 if (net_type == 'bridge_data' or net_type == 'bridge_man') \
274 and net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
mirabal65ba8f82017-02-15 12:36:33 +0100275 self.launch_dhcp_server(net['vlan'],
276 net['dhcp_first_ip'],
277 net['dhcp_last_ip'],
278 net['cidr'],
279 net['gateway_ip'])
mirabalb716ac52017-02-10 14:47:53 +0100280
mirabal580435e2017-03-01 16:17:10 +0100281 def _start_of_db_tasks(self):
282 """
283 Start ofc task for existing ofcs in database
284 :param db_of:
285 :param db_lock:
286 :return:
287 """
288 ofcs = self.get_of_controllers()
289
290 for ofc in ofcs:
291 of_conn = self._load_of_module(ofc)
292 # create ofc thread per of controller
293 self._create_ofc_task(ofc['uuid'], ofc['dpid'], of_conn)
294
295 def _create_ofc_task(self, ofc_uuid, dpid, of_conn):
296 """
297 Create an ofc thread for handle each sdn controllers
298 :param ofc_uuid: sdn controller uuid
299 :param dpid: sdn controller dpid
300 :param of_conn: OF_conn module
301 :return:
302 """
303 if 'ofcs_thread' not in self.config and 'ofcs_thread_dpid' not in self.config:
304 ofcs_threads = {}
305 ofcs_thread_dpid = []
306 else:
307 ofcs_threads = self.config['ofcs_thread']
308 ofcs_thread_dpid = self.config['ofcs_thread_dpid']
309
310 if ofc_uuid not in ofcs_threads:
311 ofc_thread = self._create_ofc_thread(of_conn, ofc_uuid)
312 if ofc_uuid == "Default":
313 self.config['of_thread'] = ofc_thread
314
315 ofcs_threads[ofc_uuid] = ofc_thread
316 self.config['ofcs_thread'] = ofcs_threads
317
318 ofcs_thread_dpid.append({dpid: ofc_thread})
319 self.config['ofcs_thread_dpid'] = ofcs_thread_dpid
320
321 def _start_ofc_default_task(self):
322 """
323 Create default ofc thread
324 """
325 if 'of_controller' not in self.config \
326 and 'of_controller_ip' not in self.config \
327 and 'of_controller_port' not in self.config \
328 and 'of_controller_dpid' not in self.config:
329 return
330
331 # OF THREAD
332 db_config = {}
333 db_config['ip'] = self.config.get('of_controller_ip')
334 db_config['port'] = self.config.get('of_controller_port')
335 db_config['dpid'] = self.config.get('of_controller_dpid')
336 db_config['type'] = self.config.get('of_controller')
337 db_config['user'] = self.config.get('of_user')
338 db_config['password'] = self.config.get('of_password')
339
340 # create connector to the openflow controller
341 # load other parameters starting by of_ from config dict in a temporal dict
342
343 of_conn = self._load_of_module(db_config)
344 # create openflow thread
345 self._create_ofc_task("Default", db_config['dpid'], of_conn)
346
347 def _load_of_module(self, db_config):
348 """
349 import python module for each SDN controller supported
mirabalf9a1a8d2017-03-15 12:42:27 +0100350 :param db_config: SDN dn information
mirabal580435e2017-03-01 16:17:10 +0100351 :return: Module
352 """
353 if not db_config:
354 raise ovimException("No module found it", HTTP_Internal_Server_Error)
355
356 module_info = None
357
358 try:
359 if self.of_test_mode:
mirabal6c600652017-03-16 17:22:57 +0100360 return openflow_conn.OfTestConnector({"name": db_config['type'],
361 "dpid": db_config['dpid'],
362 "of_debug": self.config['log_level_of']})
mirabal580435e2017-03-01 16:17:10 +0100363 temp_dict = {}
364
365 if db_config:
366 temp_dict['of_ip'] = db_config['ip']
367 temp_dict['of_port'] = db_config['port']
368 temp_dict['of_dpid'] = db_config['dpid']
369 temp_dict['of_controller'] = db_config['type']
montesmorenoba1862b2017-04-06 10:07:44 +0000370 temp_dict['of_user'] = db_config.get('user')
371 temp_dict['of_password'] = db_config.get('password')
mirabal580435e2017-03-01 16:17:10 +0100372
373 temp_dict['of_debug'] = self.config['log_level_of']
374
375 if temp_dict['of_controller'] == 'opendaylight':
376 module = "ODL"
377 else:
378 module = temp_dict['of_controller']
379
380 if module not in ovim.of_module:
garciadeblas7fa5a652017-04-26 17:55:43 +0200381 try:
382 pkg = __import__("osm_openvim." + module)
383 of_conn_module = getattr(pkg, module)
384 ovim.of_module[module] = of_conn_module
385 self.logger.debug("Module load from {}".format("osm_openvim." + module))
386 except Exception as e:
tierno29d80242017-04-25 18:07:08 +0200387 self.logger.error("Cannot open openflow controller module of type '%s'", module)
388 raise ovimException("Cannot open openflow controller of type module '{}'"
389 "Revise it is installed".format(module),
390 HTTP_Internal_Server_Error)
mirabal580435e2017-03-01 16:17:10 +0100391 else:
392 of_conn_module = ovim.of_module[module]
tierno29d80242017-04-25 18:07:08 +0200393 return of_conn_module.OF_conn(temp_dict)
394 except Exception as e:
395 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
396 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e).__name__, str(e)),
mirabal580435e2017-03-01 16:17:10 +0100397 HTTP_Internal_Server_Error)
398
399 def _create_ofc_thread(self, of_conn, ofc_uuid="Default"):
400 """
401 Create and launch a of thread
402 :return: thread obj
403 """
404 # create openflow thread
405
montesmoreno92827552017-03-30 13:24:17 +0200406 #if 'of_controller_nets_with_same_vlan' in self.config:
407 # ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
408 #else:
409 # ofc_net_same_vlan = False
410 ofc_net_same_vlan = False
mirabal580435e2017-03-01 16:17:10 +0100411
tiernoe0c28c12017-05-04 18:44:40 +0200412 thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.config["db"],
413 db_lock=self.config["db_lock"],
414 pmp_with_same_vlan=ofc_net_same_vlan,
415 logger_name=self.logger_name + ".ofc." + ofc_uuid,
416 debug=self.config.get('log_level_of'))
mirabal580435e2017-03-01 16:17:10 +0100417 #r, c = thread.OF_connector.obtain_port_correspondence()
418 #if r < 0:
419 # raise ovimException("Cannot get openflow information %s", c)
420 thread.start()
421 return thread
422
tierno57f7bda2017-02-09 12:01:55 +0100423 def stop_service(self):
424 threads = self.config.get('host_threads', {})
425 if 'of_thread' in self.config:
426 threads['of'] = (self.config['of_thread'])
mirabal580435e2017-03-01 16:17:10 +0100427 if 'ofcs_thread' in self.config:
428 ofcs_thread = self.config['ofcs_thread']
429 for ofc in ofcs_thread:
430 threads[ofc] = ofcs_thread[ofc]
431
tierno57f7bda2017-02-09 12:01:55 +0100432 if 'dhcp_thread' in self.config:
433 threads['dhcp'] = (self.config['dhcp_thread'])
434
435 for thread in threads.values():
436 thread.insert_task("exit")
437 for thread in threads.values():
438 thread.join()
tierno57f7bda2017-02-09 12:01:55 +0100439
mirabal9e194592017-02-17 11:03:25 +0100440 def get_networks(self, columns=None, db_filter={}, limit=None):
mirabal65ba8f82017-02-15 12:36:33 +0100441 """
442 Retreive networks available
mirabale9f6f1a2017-02-16 17:57:35 +0100443 :param columns: List with select query parameters
mirabal9e194592017-02-17 11:03:25 +0100444 :param db_filter: List with where query parameters
mirabale9f6f1a2017-02-16 17:57:35 +0100445 :param limit: Query limit result
mirabal65ba8f82017-02-15 12:36:33 +0100446 :return:
447 """
mirabal9e194592017-02-17 11:03:25 +0100448 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=db_filter, LIMIT=limit)
mirabal65ba8f82017-02-15 12:36:33 +0100449
450 if result < 0:
451 raise ovimException(str(content), -result)
452
453 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
454
455 return content
456
mirabal9e194592017-02-17 11:03:25 +0100457 def show_network(self, network_id, db_filter={}):
mirabal65ba8f82017-02-15 12:36:33 +0100458 """
mirabale9f6f1a2017-02-16 17:57:35 +0100459 Get network from DB by id
460 :param network_id: net Id
mirabal9e194592017-02-17 11:03:25 +0100461 :param db_filter: List with where query parameters
mirabal65ba8f82017-02-15 12:36:33 +0100462 :return:
463 """
464 # obtain data
mirabale9f6f1a2017-02-16 17:57:35 +0100465 if not network_id:
466 raise ovimException("Not network id was not found")
mirabal9e194592017-02-17 11:03:25 +0100467 db_filter['uuid'] = network_id
mirabal65ba8f82017-02-15 12:36:33 +0100468
mirabal9e194592017-02-17 11:03:25 +0100469 result, content = self.db.get_table(FROM='nets', WHERE=db_filter, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100470
471 if result < 0:
472 raise ovimException(str(content), -result)
473 elif result == 0:
mirabale9f6f1a2017-02-16 17:57:35 +0100474 raise ovimException("show_network network '%s' not found" % network_id, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100475 else:
476 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
mirabale9f6f1a2017-02-16 17:57:35 +0100477 # get ports from DB
mirabal65ba8f82017-02-15 12:36:33 +0100478 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
mirabale9f6f1a2017-02-16 17:57:35 +0100479 WHERE={'net_id': network_id}, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100480 if len(ports) > 0:
481 content[0]['ports'] = ports
mirabal65ba8f82017-02-15 12:36:33 +0100482
mirabale9f6f1a2017-02-16 17:57:35 +0100483 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
484 return content[0]
485
486 def new_network(self, network):
mirabal65ba8f82017-02-15 12:36:33 +0100487 """
mirabale9f6f1a2017-02-16 17:57:35 +0100488 Create a net in DB
mirabal65ba8f82017-02-15 12:36:33 +0100489 :return:
490 """
491 tenant_id = network.get('tenant_id')
492
493 if tenant_id:
494 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
495 if result <= 0:
496 raise ovimException("set_network error, no tenant founded", -result)
497
498 bridge_net = None
499 # check valid params
500 net_provider = network.get('provider')
501 net_type = network.get('type')
mirabal65ba8f82017-02-15 12:36:33 +0100502 net_vlan = network.get("vlan")
503 net_bind_net = network.get("bind_net")
504 net_bind_type = network.get("bind_type")
tiernoa290d8f2017-05-03 17:42:52 +0200505 net_region = network.get("region")
mirabal65ba8f82017-02-15 12:36:33 +0100506 name = network["name"]
507
508 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
509 vlan_index = name.rfind(":")
510 if not net_bind_net and not net_bind_type and vlan_index > 1:
511 try:
512 vlan_tag = int(name[vlan_index + 1:])
513 if not vlan_tag and vlan_tag < 4096:
514 net_bind_net = name[:vlan_index]
515 net_bind_type = "vlan:" + name[vlan_index + 1:]
516 except:
517 pass
518
519 if net_bind_net:
520 # look for a valid net
521 if self._check_valid_uuid(net_bind_net):
522 net_bind_key = "uuid"
523 else:
524 net_bind_key = "name"
525 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
526 if result < 0:
527 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
528 elif result == 0:
529 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
530 elif result > 1:
531 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
532 network["bind_net"] = content[0]["uuid"]
533
534 if net_bind_type:
535 if net_bind_type[0:5] != "vlan:":
536 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
537 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
538 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
539 HTTP_Bad_Request)
540 network["bind_type"] = net_bind_type
541
542 if net_provider:
543 if net_provider[:9] == "openflow:":
544 if net_type:
545 if net_type != "ptp" and net_type != "data":
546 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
547 HTTP_Bad_Request)
548 else:
549 net_type = 'data'
550 else:
551 if net_type:
552 if net_type != "bridge_man" and net_type != "bridge_data":
553 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
554 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
555 else:
556 net_type = 'bridge_man'
557
558 if not net_type:
559 net_type = 'bridge_man'
560
561 if net_provider:
562 if net_provider[:7] == 'bridge:':
563 # check it is one of the pre-provisioned bridges
564 bridge_net_name = net_provider[7:]
565 for brnet in self.config['bridge_nets']:
566 if brnet[0] == bridge_net_name: # free
567 if not brnet[3]:
568 raise ovimException("invalid 'provider:physical', "
569 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
570 bridge_net = brnet
571 net_vlan = brnet[1]
572 break
mirabale9f6f1a2017-02-16 17:57:35 +0100573 # if bridge_net==None:
574 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
575 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
576 # return
577
mirabal65ba8f82017-02-15 12:36:33 +0100578 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
579 # look for a free precreated nets
580 for brnet in self.config['bridge_nets']:
581 if not brnet[3]: # free
582 if not bridge_net:
583 if net_type == 'bridge_man': # look for the smaller speed
584 if brnet[2] < bridge_net[2]:
585 bridge_net = brnet
586 else: # look for the larger speed
587 if brnet[2] > bridge_net[2]:
588 bridge_net = brnet
589 else:
590 bridge_net = brnet
591 net_vlan = brnet[1]
592 if not bridge_net:
593 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
594 "will overcome this limit", HTTP_Bad_Request)
595 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100596 self.logger.debug("using net " + bridge_net)
mirabal65ba8f82017-02-15 12:36:33 +0100597 net_provider = "bridge:" + bridge_net[0]
598 net_vlan = bridge_net[1]
599 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
600 net_provider = 'OVS'
tiernoa290d8f2017-05-03 17:42:52 +0200601 if not net_region:
602 if net_type == "data" or net_type == "ptp":
603 net_region = "__DATA__"
604 elif net_provider == "OVS":
605 net_region = "__OVS__"
mirabal65ba8f82017-02-15 12:36:33 +0100606 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
tiernoa290d8f2017-05-03 17:42:52 +0200607 net_vlan = self.db.get_free_net_vlan(net_region)
mirabal65ba8f82017-02-15 12:36:33 +0100608 if net_vlan < 0:
609 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
610 if net_provider == 'OVS':
611 net_provider = 'OVS' + ":" + str(net_vlan)
612
613 network['provider'] = net_provider
614 network['type'] = net_type
615 network['vlan'] = net_vlan
tiernoa290d8f2017-05-03 17:42:52 +0200616 network['region'] = net_region
mirabal65ba8f82017-02-15 12:36:33 +0100617 dhcp_integrity = True
618 if 'enable_dhcp' in network and network['enable_dhcp']:
619 dhcp_integrity = self._check_dhcp_data_integrity(network)
620
621 result, content = self.db.new_row('nets', network, True, True)
622
623 if result >= 0 and dhcp_integrity:
624 if bridge_net:
625 bridge_net[3] = content
626 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
627 if network["name"] in self.config["dhcp_server"].get("nets", ()):
628 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100629 self.logger.debug("dhcp_server: add new net", content)
mirabal65ba8f82017-02-15 12:36:33 +0100630 elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
631 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100632 self.logger.debug("dhcp_server: add new net", content, content)
mirabal65ba8f82017-02-15 12:36:33 +0100633 return content
634 else:
mirabal65ba8f82017-02-15 12:36:33 +0100635 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
mirabale9f6f1a2017-02-16 17:57:35 +0100636# TODO kei change update->edit
mirabal65ba8f82017-02-15 12:36:33 +0100637
mirabale9f6f1a2017-02-16 17:57:35 +0100638 def edit_network(self, network_id, network):
mirabal65ba8f82017-02-15 12:36:33 +0100639 """
mirabale9f6f1a2017-02-16 17:57:35 +0100640 Update entwork data byt id
mirabal65ba8f82017-02-15 12:36:33 +0100641 :return:
642 """
mirabale9f6f1a2017-02-16 17:57:35 +0100643 # Look for the previous data
644 where_ = {'uuid': network_id}
645 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
646 if result < 0:
647 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
648 elif result == 0:
649 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
650 # get ports
651 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
652 WHERE={'net_id': network_id}, LIMIT=100)
653 if result < 0:
654 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
655 if nbports > 0:
656 if 'type' in network and network['type'] != network_old[0]['type']:
657 raise ovimException("Can not change type of network while having ports attached",
658 HTTP_Method_Not_Allowed)
659 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
660 raise ovimException("Can not change vlan of network while having ports attached",
661 HTTP_Method_Not_Allowed)
mirabal65ba8f82017-02-15 12:36:33 +0100662
mirabale9f6f1a2017-02-16 17:57:35 +0100663 # check valid params
664 net_provider = network.get('provider', network_old[0]['provider'])
665 net_type = network.get('type', network_old[0]['type'])
666 net_bind_net = network.get("bind_net")
667 net_bind_type = network.get("bind_type")
668 if net_bind_net:
669 # look for a valid net
670 if self._check_valid_uuid(net_bind_net):
671 net_bind_key = "uuid"
672 else:
673 net_bind_key = "name"
674 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
675 if result < 0:
676 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
677 elif result == 0:
678 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
679 elif result > 1:
680 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
681 HTTP_Bad_Request)
682 network["bind_net"] = content[0]["uuid"]
683 if net_bind_type:
684 if net_bind_type[0:5] != "vlan:":
685 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
686 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
687 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
688 HTTP_Bad_Request)
689 if net_provider:
690 if net_provider[:9] == "openflow:":
691 if net_type != "ptp" and net_type != "data":
692 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
693 else:
694 if net_type != "bridge_man" and net_type != "bridge_data":
695 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
696 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
mirabal65ba8f82017-02-15 12:36:33 +0100697
mirabale9f6f1a2017-02-16 17:57:35 +0100698 # insert in data base
699 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
700 if result >= 0:
701 # if result > 0 and nbports>0 and 'admin_state_up' in network
702 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
703 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100704
705 try:
tiernoaa941462017-03-29 15:10:28 +0200706 if nbports:
707 self.net_update_ofc_thread(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100708 except ovimException as e:
709 raise ovimException("Error while launching openflow rules in network '{}' {}"
710 .format(network_id, str(e)), HTTP_Internal_Server_Error)
711 except Exception as e:
712 raise ovimException("Error while launching openflow rules in network '{}' {}"
713 .format(network_id, str(e)), HTTP_Internal_Server_Error)
714
mirabale9f6f1a2017-02-16 17:57:35 +0100715 if self.config.get("dhcp_server"):
716 if network_id in self.config["dhcp_nets"]:
717 self.config["dhcp_nets"].remove(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100718 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
mirabale9f6f1a2017-02-16 17:57:35 +0100719 self.config["dhcp_nets"].append(network_id)
720 else:
mirabal7bbf50e2017-03-13 15:15:18 +0100721 net_bind = network.get("bind_type", network_old[0]["bind_type"])
722 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 +0100723 "bridge_ifaces", ()):
724 self.config["dhcp_nets"].append(network_id)
725 return network_id
mirabal65ba8f82017-02-15 12:36:33 +0100726 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100727 raise ovimException(content, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100728
mirabale9f6f1a2017-02-16 17:57:35 +0100729 def delete_network(self, network_id):
mirabal9e194592017-02-17 11:03:25 +0100730 """
731 Delete network by network id
732 :param network_id: network id
733 :return:
734 """
mirabale9f6f1a2017-02-16 17:57:35 +0100735
736 # delete from the data base
737 result, content = self.db.delete_row('nets', network_id)
738
739 if result == 0:
740 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
741 elif result > 0:
742 for brnet in self.config['bridge_nets']:
743 if brnet[3] == network_id:
744 brnet[3] = None
745 break
746 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
747 self.config["dhcp_nets"].remove(network_id)
748 return content
749 else:
750 raise ovimException("Error deleting network %s" % network_id, HTTP_Internal_Server_Error)
mirabal65ba8f82017-02-15 12:36:33 +0100751
752 def get_openflow_rules(self, network_id=None):
753 """
754 Get openflow id from DB
755 :param network_id: Network id, if none all networks will be retrieved
756 :return: Return a list with Openflow rules per net
757 """
758 # ignore input data
759 if not network_id:
760 where_ = {}
761 else:
762 where_ = {"net_id": network_id}
mirabal65ba8f82017-02-15 12:36:33 +0100763 result, content = self.db.get_table(
mirabalf9a1a8d2017-03-15 12:42:27 +0100764 SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
mirabal65ba8f82017-02-15 12:36:33 +0100765 WHERE=where_, FROM='of_flows')
766
767 if result < 0:
768 raise ovimException(str(content), -result)
769 return content
770
mirabale9f6f1a2017-02-16 17:57:35 +0100771 def edit_openflow_rules(self, network_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100772
773 """
774 To make actions over the net. The action is to reinstall the openflow rules
775 network_id can be 'all'
776 :param network_id: Network id, if none all networks will be retrieved
777 :return : Number of nets updated
778 """
779
780 # ignore input data
781 if not network_id:
782 where_ = {}
783 else:
784 where_ = {"uuid": network_id}
785 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
786
787 if result < 0:
788 raise ovimException(str(content), -result)
789
790 for net in content:
791 if net["type"] != "ptp" and net["type"] != "data":
792 result -= 1
793 continue
mirabal7bbf50e2017-03-13 15:15:18 +0100794
795 try:
796 self.net_update_ofc_thread(net['uuid'])
797 except ovimException as e:
798 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
799 HTTP_Internal_Server_Error)
800 except Exception as e:
801 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
802 HTTP_Internal_Server_Error)
803
mirabal65ba8f82017-02-15 12:36:33 +0100804 return result
805
mirabalf9a1a8d2017-03-15 12:42:27 +0100806 def delete_openflow_rules(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100807 """
808 To make actions over the net. The action is to delete ALL openflow rules
809 :return: return operation result
810 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100811
812 if not ofc_id:
813 if 'Default' in self.config['ofcs_thread']:
814 r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
815 else:
816 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
817
818 elif ofc_id in self.config['ofcs_thread']:
819 r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
820
821 # ignore input data
822 if r < 0:
823 raise ovimException(str(c), -r)
824 else:
825 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
mirabal65ba8f82017-02-15 12:36:33 +0100826 return r
827
mirabalf9a1a8d2017-03-15 12:42:27 +0100828 def get_openflow_ports(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100829 """
830 Obtain switch ports names of openflow controller
831 :return: Return flow ports in DB
832 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100833 if not ofc_id:
834 if 'Default' in self.config['ofcs_thread']:
835 conn = self.config['ofcs_thread']['Default'].OF_connector
836 else:
837 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
838
839 if ofc_id in self.config['ofcs_thread']:
840 conn = self.config['ofcs_thread'][ofc_id].OF_connector
841 else:
842 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
843 return conn.pp2ofi
tierno57f7bda2017-02-09 12:01:55 +0100844
845 def get_ports(self, columns=None, filter={}, limit=None):
846 # result, content = my.db.get_ports(where_)
847 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
848 if result < 0:
849 self.logger.error("http_get_ports Error %d %s", result, content)
850 raise ovimException(str(content), -result)
851 else:
852 convert_boolean(content, ('admin_state_up',))
853 return content
854
tierno57f7bda2017-02-09 12:01:55 +0100855 def new_port(self, port_data):
856 port_data['type'] = 'external'
857 if port_data.get('net_id'):
858 # check that new net has the correct type
859 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
860 if result < 0:
861 raise ovimException(str(new_net), -result)
862 # insert in data base
863 result, uuid = self.db.new_row('ports', port_data, True, True)
864 if result > 0:
865 if 'net_id' in port_data:
mirabal7bbf50e2017-03-13 15:15:18 +0100866 try:
867 self.net_update_ofc_thread(port_data['net_id'])
868 except ovimException as e:
869 raise ovimException("Cannot insert a task for updating network '{}' {}"
870 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
871 except Exception as e:
872 raise ovimException("Cannot insert a task for updating network '{}' {}"
873 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
874
tierno57f7bda2017-02-09 12:01:55 +0100875 return uuid
876 else:
877 raise ovimException(str(uuid), -result)
878
mirabal37829452017-03-09 14:41:21 +0100879 def new_external_port(self, port_data):
880 """
881 Create new external port and check port mapping correspondence
882 :param port_data: port_data = {
883 'region': 'datacenter region',
884 'compute_node': 'compute node id',
885 'pci': 'pci port address',
886 'vlan': 'net vlan',
887 'net_id': 'net id',
888 'tenant_id': 'tenant id',
889 'mac': 'switch mac',
890 'name': 'port name'
891 'ip_address': 'ip address - optional'}
892 :return:
893 """
894
895 port_data['type'] = 'external'
896
897 if port_data.get('net_id'):
898 # check that new net has the correct type
899 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
900 if result < 0:
901 raise ovimException(str(new_net), -result)
902 # insert in data base
903 db_filter = {}
904
905 if port_data.get('region'):
906 db_filter['region'] = port_data['region']
907 if port_data.get('pci'):
908 db_filter['pci'] = port_data['pci']
909 if port_data.get('compute_node'):
910 db_filter['compute_node'] = port_data['compute_node']
911
912 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
913 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
914
915 if not len(port_mapping_data):
tierno16007502017-03-27 16:48:32 +0200916 raise ovimException("No port mapping founded for '{}'".format(str(db_filter)),
mirabal37829452017-03-09 14:41:21 +0100917 HTTP_Not_Found)
918 elif len(port_mapping_data) > 1:
919 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
920 HTTP_Conflict)
921
922 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
923 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
924 port_data['switch_port'] = port_mapping_data[0]['switch_port']
925 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
926
927 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
montesmoreno275b1992017-03-28 15:45:02 +0200928 if 'region' in port_data:
929 del port_data['region']
930 if 'pci' in port_data:
931 del port_data['pci']
932 if 'compute_node' in port_data:
933 del port_data['compute_node']
mirabal37829452017-03-09 14:41:21 +0100934
935 result, uuid = self.db.new_row('ports', port_data, True, True)
936 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100937 try:
938 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
939 except ovimException as e:
940 raise ovimException("Cannot insert a task for updating network '{}' {}".
941 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
942 except Exception as e:
943 raise ovimException("Cannot insert a task for updating network '{}' {}"
944 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
mirabal37829452017-03-09 14:41:21 +0100945 return uuid
946 else:
947 raise ovimException(str(uuid), -result)
948
tiernoaa941462017-03-29 15:10:28 +0200949 def net_update_ofc_thread(self, net_id, ofc_id=None, switch_dpid=None):
mirabal7bbf50e2017-03-13 15:15:18 +0100950 """
951 Insert a update net task by net id or ofc_id for each ofc thread
952 :param net_id: network id
953 :param ofc_id: openflow controller id
tiernoaa941462017-03-29 15:10:28 +0200954 :param switch_dpid: switch dpid
mirabal7bbf50e2017-03-13 15:15:18 +0100955 :return:
956 """
957 if not net_id:
958 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
959
mirabal7bbf50e2017-03-13 15:15:18 +0100960 r = -1
961 c = 'No valid ofc_id or switch_dpid received'
962
963 if not ofc_id:
964 ports = self.get_ports(filter={"net_id": net_id})
965 for port in ports:
966 port_ofc_id = port.get('ofc_id', None)
967 if port_ofc_id:
968 ofc_id = port['ofc_id']
969 switch_dpid = port['switch_dpid']
970 break
tiernoaa941462017-03-29 15:10:28 +0200971 #TODO if not ofc_id: look at database table ofcs
972
mirabal7bbf50e2017-03-13 15:15:18 +0100973
974 # If no ofc_id found it, default ofc_id is used.
975 if not ofc_id and not switch_dpid:
976 ofc_id = "Default"
977
978 if ofc_id and ofc_id in self.config['ofcs_thread']:
979 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
980 elif switch_dpid:
981
982 ofcs_dpid_list = self.config['ofcs_thread_dpid']
983 for ofc_t in ofcs_dpid_list:
984 if switch_dpid in ofc_t:
985 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
986
987 if r < 0:
tierno82232582017-03-15 18:09:16 +0100988 message = "Cannot insert a task for updating network '{}', {}".format(net_id, c)
mirabal7bbf50e2017-03-13 15:15:18 +0100989 self.logger.error(message)
990 raise ovimException(message, HTTP_Internal_Server_Error)
991
tierno57f7bda2017-02-09 12:01:55 +0100992 def delete_port(self, port_id):
993 # Look for the previous port data
994 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
995 if result < 0:
996 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
997 # delete from the data base
998 result, content = self.db.delete_row('ports', port_id)
999 if result == 0:
1000 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1001 elif result < 0:
1002 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
1003 # update network
1004 network = ports[0].get('net_id', None)
1005 if network:
1006 # change of net.
mirabal7bbf50e2017-03-13 15:15:18 +01001007
1008 try:
tiernoaa941462017-03-29 15:10:28 +02001009 self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +01001010 except ovimException as e:
1011 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1012 HTTP_Internal_Server_Error)
1013 except Exception as e:
1014 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1015 HTTP_Internal_Server_Error)
1016
tierno57f7bda2017-02-09 12:01:55 +01001017 return content
1018
tierno57f7bda2017-02-09 12:01:55 +01001019 def edit_port(self, port_id, port_data, admin=True):
1020 # Look for the previous port data
1021 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
1022 if result < 0:
1023 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
1024 elif result == 0:
1025 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1026 port = content[0]
1027 nets = []
1028 host_id = None
1029 result = 1
1030 if 'net_id' in port_data:
1031 # change of net.
1032 old_net = port.get('net_id', None)
1033 new_net = port_data['net_id']
1034 if old_net != new_net:
1035
1036 if new_net:
1037 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
1038 if old_net:
1039 nets.append(old_net)
1040 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
1041 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
1042 elif port['type'] == 'external' and not admin:
1043 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
1044 if new_net:
1045 # check that new net has the correct type
1046 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
1047 if result < 0:
1048 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
1049 # change VLAN for SR-IOV ports
1050 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
1051 if new_net:
1052 port_data["vlan"] = None
1053 else:
1054 port_data["vlan"] = new_net_dict["vlan"]
1055 # get host where this VM is allocated
1056 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
1057 if result > 0:
1058 host_id = content[0]["host_id"]
1059
1060 # insert in data base
1061 if result >= 0:
1062 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
tiernoaa941462017-03-29 15:10:28 +02001063 port.update(port_data)
tierno57f7bda2017-02-09 12:01:55 +01001064
1065 # Insert task to complete actions
1066 if result > 0:
1067 for net_id in nets:
mirabal7bbf50e2017-03-13 15:15:18 +01001068 try:
tiernoaa941462017-03-29 15:10:28 +02001069 self.net_update_ofc_thread(net_id, port["ofc_id"], switch_dpid=port["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +01001070 except ovimException as e:
1071 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1072 HTTP_Internal_Server_Error)
1073 except Exception as e:
1074 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1075 HTTP_Internal_Server_Error)
1076
tierno57f7bda2017-02-09 12:01:55 +01001077 if host_id:
1078 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1079 if r < 0:
1080 self.logger.error("Error updating network '{}' {}".format(r,v))
1081 # TODO Do something if fails
1082 if result >= 0:
1083 return port_id
1084 else:
1085 raise ovimException("Error {}".format(content), http_code=-result)
mirabalb716ac52017-02-10 14:47:53 +01001086
mirabal9e194592017-02-17 11:03:25 +01001087 def new_of_controller(self, ofc_data):
1088 """
1089 Create a new openflow controller into DB
1090 :param ofc_data: Dict openflow controller data
1091 :return: openflow controller dpid
1092 """
1093
mirabal580435e2017-03-01 16:17:10 +01001094 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
mirabal9e194592017-02-17 11:03:25 +01001095 if result < 0:
mirabal580435e2017-03-01 16:17:10 +01001096 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1097
1098 ofc_data['uuid'] = ofc_uuid
1099 of_conn = self._load_of_module(ofc_data)
1100 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1101
1102 return ofc_uuid
mirabal9e194592017-02-17 11:03:25 +01001103
1104 def edit_of_controller(self, of_id, ofc_data):
1105 """
1106 Edit an openflow controller entry from DB
1107 :return:
1108 """
1109 if not ofc_data:
1110 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1111
1112 old_of_controller = self.show_of_controller(of_id)
1113
1114 if old_of_controller:
1115 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1116 if result >= 0:
1117 return ofc_data
1118 else:
1119 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1120 http_code=-result)
1121 else:
1122 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1123 http_code=HTTP_Internal_Server_Error)
1124
1125 def delete_of_controller(self, of_id):
1126 """
1127 Delete an openflow controller from DB.
1128 :param of_id: openflow controller dpid
1129 :return:
1130 """
1131
mirabal580435e2017-03-01 16:17:10 +01001132 ofc = self.show_of_controller(of_id)
1133
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001134 result, content = self.db.delete_row("ofcs", of_id)
mirabal9e194592017-02-17 11:03:25 +01001135 if result < 0:
1136 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1137 elif result == 0:
1138 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
mirabal580435e2017-03-01 16:17:10 +01001139
1140 ofc_thread = self.config['ofcs_thread'][of_id]
1141 del self.config['ofcs_thread'][of_id]
1142 for ofc_th in self.config['ofcs_thread_dpid']:
1143 if ofc['dpid'] in ofc_th:
1144 self.config['ofcs_thread_dpid'].remove(ofc_th)
1145
1146 ofc_thread.insert_task("exit")
1147 #ofc_thread.join()
1148
mirabal9e194592017-02-17 11:03:25 +01001149 return content
1150
1151 def show_of_controller(self, uuid):
1152 """
1153 Show an openflow controller by dpid from DB.
1154 :param db_filter: List with where query parameters
1155 :return:
1156 """
1157
1158 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1159
1160 if result == 0:
1161 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1162 http_code=HTTP_Not_Found)
1163 elif result < 0:
1164 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1165 http_code=HTTP_Internal_Server_Error)
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001166 return content[0]
mirabal9e194592017-02-17 11:03:25 +01001167
mirabalfbfb7972017-02-27 17:36:17 +01001168 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
mirabal9e194592017-02-17 11:03:25 +01001169 """
1170 Show an openflow controllers from DB.
1171 :param columns: List with SELECT query parameters
1172 :param db_filter: List with where query parameters
mirabalfbfb7972017-02-27 17:36:17 +01001173 :param limit: result Limit
mirabal9e194592017-02-17 11:03:25 +01001174 :return:
1175 """
mirabalfbfb7972017-02-27 17:36:17 +01001176 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
mirabal9e194592017-02-17 11:03:25 +01001177
1178 if result < 0:
1179 raise ovimException(str(content), -result)
1180
1181 return content
1182
mirabalfbfb7972017-02-27 17:36:17 +01001183 def get_tenants(self, columns=None, db_filter={}, limit=None):
1184 """
1185 Retrieve tenant list from DB
1186 :param columns: List with SELECT query parameters
1187 :param db_filter: List with where query parameters
1188 :param limit: result limit
1189 :return:
1190 """
1191 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1192 if result < 0:
1193 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1194 else:
1195 convert_boolean(content, ('enabled',))
1196 return content
1197
1198 def show_tenant_id(self, tenant_id):
1199 """
1200 Get tenant from DB by id
1201 :param tenant_id: tenant id
1202 :return:
1203 """
1204 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1205 WHERE={"uuid": tenant_id})
1206 if result < 0:
1207 raise ovimException(str(content), -result)
1208 elif result == 0:
1209 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1210 else:
1211 convert_boolean(content, ('enabled',))
1212 return content[0]
1213
1214 def new_tentant(self, tenant):
1215 """
1216 Create a tenant and store in DB
1217 :param tenant: Dictionary with tenant data
1218 :return: the uuid of created tenant. Raise exception upon error
1219 """
1220
1221 # insert in data base
1222 result, tenant_uuid = self.db.new_tenant(tenant)
1223
1224 if result >= 0:
1225 return tenant_uuid
1226 else:
1227 raise ovimException(str(tenant_uuid), -result)
1228
1229 def delete_tentant(self, tenant_id):
1230 """
1231 Delete a tenant from the database.
1232 :param tenant_id: Tenant id
1233 :return: delete tenant id
1234 """
1235
1236 # check permissions
1237 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1238 WHERE={'tenant_id': tenant_id})
1239 if r <= 0:
1240 tenants_flavors = ()
1241 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1242 WHERE={'tenant_id': tenant_id})
1243 if r <= 0:
1244 tenants_images = ()
1245
1246 result, content = self.db.delete_row('tenants', tenant_id)
1247 if result == 0:
1248 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1249 elif result > 0:
1250 for flavor in tenants_flavors:
1251 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1252 for image in tenants_images:
1253 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1254 return content
1255 else:
1256 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1257
1258 def edit_tenant(self, tenant_id, tenant_data):
1259 """
1260 Update a tenant data identified by tenant id
1261 :param tenant_id: tenant id
1262 :param tenant_data: Dictionary with tenant data
1263 :return:
1264 """
1265
1266 # Look for the previous data
1267 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1268 if result < 0:
1269 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1270 HTTP_Internal_Server_Error)
1271 elif result == 0:
1272 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1273
1274 # insert in data base
1275 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1276 if result >= 0:
1277 return content
1278 else:
1279 raise ovimException(str(content), -result)
1280
mirabal6045a9d2017-03-06 11:36:55 +01001281 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1282 """
1283 Create new port mapping entry
1284 :param of_maps: List with port mapping information
1285 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1286 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1287 :param ofc_id: ofc id
1288 :param switch_dpid: switch dpid
1289 :param region: datacenter region id
1290 :return:
1291 """
1292
1293 for map in of_maps:
1294 if ofc_id:
1295 map['ofc_id'] = ofc_id
1296 if switch_dpid:
1297 map['switch_dpid'] = switch_dpid
1298 if region:
1299 map['region'] = region
1300
1301 for of_map in of_maps:
1302 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1303 if result > 0:
1304 of_map["uuid"] = uuid
1305 else:
1306 raise ovimException(str(uuid), -result)
1307 return of_maps
1308
1309 def clear_of_port_mapping(self, db_filter={}):
1310 """
1311 Clear port mapping filtering using db_filter dict
1312 :param db_filter: Parameter to filter during remove process
1313 :return:
1314 """
1315 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1316 # delete_row_by_key
1317 if result >= 0:
1318 return content
1319 else:
1320 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1321 HTTP_Internal_Server_Error)
1322
1323 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1324 """
1325 Retrive port mapping from DB
1326 :param column:
1327 :param db_filter:
1328 :return:
1329 """
1330 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1331
1332 if result < 0:
1333 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1334 raise ovimException(str(content), -result)
1335 else:
1336 return content
1337
mirabalb716ac52017-02-10 14:47:53 +01001338 def get_dhcp_controller(self):
1339 """
1340 Create an host_thread object for manage openvim controller and not create a thread for itself
1341 :return: dhcp_host openvim controller object
1342 """
1343
1344 if 'openvim_controller' in self.config['host_threads']:
1345 return self.config['host_threads']['openvim_controller']
1346
1347 bridge_ifaces = []
1348 controller_ip = self.config['ovs_controller_ip']
1349 ovs_controller_user = self.config['ovs_controller_user']
1350
1351 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1352 host_develop_mode = True if self.config['mode'] == 'development' else False
1353
1354 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
tiernoe0c28c12017-05-04 18:44:40 +02001355 db=self.config["db"], db_lock=self.config["db_lock"], test=host_test_mode,
mirabalb716ac52017-02-10 14:47:53 +01001356 image_path=self.config['image_path'], version=self.config['version'],
1357 host_id='openvim_controller', develop_mode=host_develop_mode,
tiernoe0c28c12017-05-04 18:44:40 +02001358 develop_bridge_iface=bridge_ifaces,
1359 logger_name=self.logger_name + ".host.controller",
tiernof135eff2017-04-19 19:11:53 +02001360 debug=self.config.get('log_level_host'))
tiernoe0c28c12017-05-04 18:44:40 +02001361 dhcp_host.start()
mirabalb716ac52017-02-10 14:47:53 +01001362 self.config['host_threads']['openvim_controller'] = dhcp_host
1363 if not host_test_mode:
1364 dhcp_host.ssh_connect()
1365 return dhcp_host
1366
mirabal18f5de32017-02-13 12:41:49 +01001367 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
mirabalb716ac52017-02-10 14:47:53 +01001368 """
1369 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1370 :param vlan: vlan identifier
1371 :param first_ip: First dhcp range ip
1372 :param last_ip: Last dhcp range ip
1373 :param cidr: net cidr
mirabale9f6f1a2017-02-16 17:57:35 +01001374 :param gateway: net gateway
mirabalb716ac52017-02-10 14:47:53 +01001375 :return:
1376 """
1377 ip_tools = IPNetwork(cidr)
1378 dhcp_netmask = str(ip_tools.netmask)
1379 ip_range = [first_ip, last_ip]
1380
1381 dhcp_path = self.config['ovs_controller_file_path']
1382
1383 controller_host = self.get_dhcp_controller()
1384 controller_host.create_linux_bridge(vlan)
mirabal72fcda72017-05-09 11:01:06 +02001385 controller_host.create_dhcp_interfaces(vlan, gateway, dhcp_netmask)
mirabal18f5de32017-02-13 12:41:49 +01001386 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
mirabalb716ac52017-02-10 14:47:53 +01001387
mirabald87877c2017-03-31 15:15:52 +02001388if __name__ == "__main__":
1389
1390 parser = argparse.ArgumentParser()
tierno46ca3a92017-04-05 19:49:24 +02001391 parser.add_argument("-v","--version", help="show ovim library version", action="store_true")
1392 parser.add_argument("--database-version", help="show required database version", action="store_true")
mirabald87877c2017-03-31 15:15:52 +02001393 args = parser.parse_args()
1394 if args.version:
1395 print ('openvimd version {} {}'.format(ovim.get_version(), ovim.get_version_date()))
1396 print ('(c) Copyright Telefonica')
tierno46ca3a92017-04-05 19:49:24 +02001397 elif args.database_version:
1398 print ('required database version: {}'.format(ovim.get_database_version()))
mirabalb716ac52017-02-10 14:47:53 +01001399