blob: 39ab5789ea1abe0dbbcaf4c55c8eb9a5056e0989 [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
25'''
26This is the thread for the http server North API.
27Two thread will be launched, with normal and administrative permissions.
28'''
29
30__author__ = "Alfonso Tierno, Leonardo Mirabal"
31__date__ = "$06-Feb-2017 12:07:15$"
tierno46ca3a92017-04-05 19:49:24 +020032__version__ = "0.5.10-r526"
33version_date = "Apr 2017"
34database_version = "0.17" #expected database schema version
tierno57f7bda2017-02-09 12:01:55 +010035
36import threading
mirabal9f657102017-04-10 20:05:40 +020037import osm_openvim.vim_db as vim_db
tierno57f7bda2017-02-09 12:01:55 +010038import logging
tierno57f7bda2017-02-09 12:01:55 +010039import imp
mirabal9f657102017-04-10 20:05:40 +020040import argparse
mirabal65ba8f82017-02-15 12:36:33 +010041from netaddr import IPNetwork
42from jsonschema import validate as js_v, exceptions as js_e
mirabal9f657102017-04-10 20:05:40 +020043import osm_openvim.host_thread as ht
44import osm_openvim.dhcp_thread as dt
45import osm_openvim.openflow_thread as oft
46import osm_openvim.openflow_conn as openflow_conn
47
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.db_lock = None
104 self.db_of = None
105 self.of_test_mode = False
tierno57f7bda2017-02-09 12:01:55 +0100106
107 def _create_database_connection(self):
108 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
tierno46ca3a92017-04-05 19:49:24 +0200109 self.logger_name + ".db", self.config.get('log_level_db'))
tierno57f7bda2017-02-09 12:01:55 +0100110 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
111 self.config['db_name']) == -1:
112 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
113 # self.config['db_host'])
114 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
115 self.config['db_user'],
116 self.config['db_host']) )
117 return db
118
mirabale9f6f1a2017-02-16 17:57:35 +0100119 @staticmethod
mirabal50a052f2017-03-27 18:08:07 +0200120 def get_version():
121 return __version__
122
123 @staticmethod
124 def get_version_date():
125 return version_date
126
127 @staticmethod
128 def get_database_version():
129 return database_version
130
131 @staticmethod
mirabale9f6f1a2017-02-16 17:57:35 +0100132 def _check_dhcp_data_integrity(network):
133 """
134 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
135 :param network: list with user nets paramters
136 :return:
137 """
138 if "cidr" in network:
139 cidr = network["cidr"]
140 ip_tools = IPNetwork(cidr)
141 cidr_len = ip_tools.prefixlen
142 if cidr_len > 29:
143 return False
144
145 ips = IPNetwork(cidr)
146 if "dhcp_first_ip" not in network:
147 network["dhcp_first_ip"] = str(ips[2])
148 if "dhcp_last_ip" not in network:
149 network["dhcp_last_ip"] = str(ips[-2])
150 if "gateway_ip" not in network:
151 network["gateway_ip"] = str(ips[1])
152
153 return True
154 else:
155 return False
156
157 @staticmethod
158 def _check_valid_uuid(uuid):
159 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
160 try:
161 js_v(uuid, id_schema)
162 return True
163 except js_e.ValidationError:
164 return False
tierno57f7bda2017-02-09 12:01:55 +0100165
166 def start_service(self):
mirabal580435e2017-03-01 16:17:10 +0100167 """
168 Start ovim services
169 :return:
170 """
tierno2db743b2017-03-28 17:23:15 +0200171 global database_version
mirabal580435e2017-03-01 16:17:10 +0100172 # if self.running_info:
tierno57f7bda2017-02-09 12:01:55 +0100173 # return #TODO service can be checked and rebuild broken threads
174 r = self.db.get_db_version()
mirabal580435e2017-03-01 16:17:10 +0100175 if r[0] < 0:
tierno57f7bda2017-02-09 12:01:55 +0100176 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
tierno2db743b2017-03-28 17:23:15 +0200177 "'./database_utils/migrate_vim_db.sh'".format(database_version) )
178 elif r[1] != database_version:
tierno57f7bda2017-02-09 12:01:55 +0100179 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
tierno2db743b2017-03-28 17:23:15 +0200180 "'./database_utils/migrate_vim_db.sh'".format(r[1], database_version) )
tierno46ca3a92017-04-05 19:49:24 +0200181 self.logger.critical("Starting ovim server version: '{} {}' database version '{}'".format(
182 self.get_version(), self.get_version_date(), self.get_database_version()))
tierno57f7bda2017-02-09 12:01:55 +0100183 # create database connection for openflow threads
mirabal580435e2017-03-01 16:17:10 +0100184 self.db_of = self._create_database_connection()
185 self.config["db"] = self.db_of
186 self.db_lock = threading.Lock()
187 self.config["db_lock"] = self.db_lock
tierno57f7bda2017-02-09 12:01:55 +0100188
mirabal580435e2017-03-01 16:17:10 +0100189 self.of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
190 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
191 # speed in Gbit/s
192
tierno57f7bda2017-02-09 12:01:55 +0100193 self.config['dhcp_nets'] = []
194 self.config['bridge_nets'] = []
195 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
mirabal580435e2017-03-01 16:17:10 +0100196 # skip 'development_bridge'
tierno57f7bda2017-02-09 12:01:55 +0100197 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
198 continue
199 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
200
201 # check if this bridge is already used (present at database) for a network)
202 used_bridge_nets = []
203 for brnet in self.config['bridge_nets']:
tierno686b3952017-03-10 13:57:24 +0100204 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
tierno57f7bda2017-02-09 12:01:55 +0100205 if r > 0:
206 brnet[3] = nets[0]['uuid']
207 used_bridge_nets.append(brnet[0])
208 if self.config.get("dhcp_server"):
209 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
210 self.config['dhcp_nets'].append(nets[0]['uuid'])
211 if len(used_bridge_nets) > 0:
212 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
213 # get nets used by dhcp
214 if self.config.get("dhcp_server"):
215 for net in self.config["dhcp_server"].get("nets", ()):
tierno686b3952017-03-10 13:57:24 +0100216 r, nets = self.db.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
tierno57f7bda2017-02-09 12:01:55 +0100217 if r > 0:
218 self.config['dhcp_nets'].append(nets[0]['uuid'])
219
mirabal580435e2017-03-01 16:17:10 +0100220 # OFC default
221 self._start_ofc_default_task()
tierno57f7bda2017-02-09 12:01:55 +0100222
mirabal580435e2017-03-01 16:17:10 +0100223 # OFC per tenant in DB
224 self._start_of_db_tasks()
tierno57f7bda2017-02-09 12:01:55 +0100225
226 # create dhcp_server thread
227 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
228 dhcp_params = self.config.get("dhcp_server")
229 if dhcp_params:
230 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
tierno43044b02017-04-06 18:58:24 +0200231 db=self.db_of, db_lock=self.db_lock, logger_name=self.logger_name + ".dhcp",
232 debug=self.config.get('log_level_of'))
tierno57f7bda2017-02-09 12:01:55 +0100233 thread.start()
234 self.config['dhcp_thread'] = thread
235
236 # Create one thread for each host
237 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
238 host_develop_mode = True if self.config['mode'] == 'development' else False
239 host_develop_bridge_iface = self.config.get('development_bridge', None)
mirabal580435e2017-03-01 16:17:10 +0100240
241 # get host list from data base before starting threads
tierno686b3952017-03-10 13:57:24 +0100242 r, hosts = self.db.get_table(SELECT=('name', 'ip_name', 'user', 'uuid'), FROM='hosts', WHERE={'status': 'ok'})
mirabal580435e2017-03-01 16:17:10 +0100243 if r < 0:
244 raise ovimException("Cannot get hosts from database {}".format(hosts))
245
tierno57f7bda2017-02-09 12:01:55 +0100246 self.config['host_threads'] = {}
247 for host in hosts:
248 host['image_path'] = '/opt/VNF/images/openvim'
mirabal580435e2017-03-01 16:17:10 +0100249 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=self.db_of,
250 db_lock=self.db_lock, test=host_test_mode, image_path=self.config['image_path'],
251 version=self.config['version'], host_id=host['uuid'], develop_mode=host_develop_mode,
tierno57f7bda2017-02-09 12:01:55 +0100252 develop_bridge_iface=host_develop_bridge_iface)
253 thread.start()
254 self.config['host_threads'][host['uuid']] = thread
255
mirabalb716ac52017-02-10 14:47:53 +0100256 # create ovs dhcp thread
257 result, content = self.db.get_table(FROM='nets')
258 if result < 0:
259 self.logger.error("http_get_ports Error %d %s", result, content)
260 raise ovimException(str(content), -result)
261
262 for net in content:
263 net_type = net['type']
mirabale9f6f1a2017-02-16 17:57:35 +0100264 if (net_type == 'bridge_data' or net_type == 'bridge_man') \
265 and net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
mirabal65ba8f82017-02-15 12:36:33 +0100266 self.launch_dhcp_server(net['vlan'],
267 net['dhcp_first_ip'],
268 net['dhcp_last_ip'],
269 net['cidr'],
270 net['gateway_ip'])
mirabalb716ac52017-02-10 14:47:53 +0100271
mirabal580435e2017-03-01 16:17:10 +0100272 def _start_of_db_tasks(self):
273 """
274 Start ofc task for existing ofcs in database
275 :param db_of:
276 :param db_lock:
277 :return:
278 """
279 ofcs = self.get_of_controllers()
280
281 for ofc in ofcs:
282 of_conn = self._load_of_module(ofc)
283 # create ofc thread per of controller
284 self._create_ofc_task(ofc['uuid'], ofc['dpid'], of_conn)
285
286 def _create_ofc_task(self, ofc_uuid, dpid, of_conn):
287 """
288 Create an ofc thread for handle each sdn controllers
289 :param ofc_uuid: sdn controller uuid
290 :param dpid: sdn controller dpid
291 :param of_conn: OF_conn module
292 :return:
293 """
294 if 'ofcs_thread' not in self.config and 'ofcs_thread_dpid' not in self.config:
295 ofcs_threads = {}
296 ofcs_thread_dpid = []
297 else:
298 ofcs_threads = self.config['ofcs_thread']
299 ofcs_thread_dpid = self.config['ofcs_thread_dpid']
300
301 if ofc_uuid not in ofcs_threads:
302 ofc_thread = self._create_ofc_thread(of_conn, ofc_uuid)
303 if ofc_uuid == "Default":
304 self.config['of_thread'] = ofc_thread
305
306 ofcs_threads[ofc_uuid] = ofc_thread
307 self.config['ofcs_thread'] = ofcs_threads
308
309 ofcs_thread_dpid.append({dpid: ofc_thread})
310 self.config['ofcs_thread_dpid'] = ofcs_thread_dpid
311
312 def _start_ofc_default_task(self):
313 """
314 Create default ofc thread
315 """
316 if 'of_controller' not in self.config \
317 and 'of_controller_ip' not in self.config \
318 and 'of_controller_port' not in self.config \
319 and 'of_controller_dpid' not in self.config:
320 return
321
322 # OF THREAD
323 db_config = {}
324 db_config['ip'] = self.config.get('of_controller_ip')
325 db_config['port'] = self.config.get('of_controller_port')
326 db_config['dpid'] = self.config.get('of_controller_dpid')
327 db_config['type'] = self.config.get('of_controller')
328 db_config['user'] = self.config.get('of_user')
329 db_config['password'] = self.config.get('of_password')
330
331 # create connector to the openflow controller
332 # load other parameters starting by of_ from config dict in a temporal dict
333
334 of_conn = self._load_of_module(db_config)
335 # create openflow thread
336 self._create_ofc_task("Default", db_config['dpid'], of_conn)
337
338 def _load_of_module(self, db_config):
339 """
340 import python module for each SDN controller supported
mirabalf9a1a8d2017-03-15 12:42:27 +0100341 :param db_config: SDN dn information
mirabal580435e2017-03-01 16:17:10 +0100342 :return: Module
343 """
344 if not db_config:
345 raise ovimException("No module found it", HTTP_Internal_Server_Error)
346
347 module_info = None
348
349 try:
350 if self.of_test_mode:
mirabal6c600652017-03-16 17:22:57 +0100351 return openflow_conn.OfTestConnector({"name": db_config['type'],
352 "dpid": db_config['dpid'],
353 "of_debug": self.config['log_level_of']})
mirabal580435e2017-03-01 16:17:10 +0100354 temp_dict = {}
355
356 if db_config:
357 temp_dict['of_ip'] = db_config['ip']
358 temp_dict['of_port'] = db_config['port']
359 temp_dict['of_dpid'] = db_config['dpid']
360 temp_dict['of_controller'] = db_config['type']
montesmorenoba1862b2017-04-06 10:07:44 +0000361 temp_dict['of_user'] = db_config.get('user')
362 temp_dict['of_password'] = db_config.get('password')
mirabal580435e2017-03-01 16:17:10 +0100363
364 temp_dict['of_debug'] = self.config['log_level_of']
365
366 if temp_dict['of_controller'] == 'opendaylight':
367 module = "ODL"
368 else:
369 module = temp_dict['of_controller']
370
371 if module not in ovim.of_module:
372 module_info = imp.find_module(module)
373 of_conn_module = imp.load_module("OF_conn", *module_info)
374 ovim.of_module[module] = of_conn_module
375 else:
376 of_conn_module = ovim.of_module[module]
377
378 try:
379 return of_conn_module.OF_conn(temp_dict)
380 except Exception as e:
381 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
382 if module_info and module_info[0]:
383 file.close(module_info[0])
384 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e).__name__, str(e)),
385 HTTP_Internal_Server_Error)
386 except (IOError, ImportError) as e:
387 if module_info and module_info[0]:
388 file.close(module_info[0])
389 self.logger.error("Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' "
390 "field of configuration file.", module, type(e).__name__, str(e))
391 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' "
392 "field of configuration file.".format(module, type(e).__name__, str(e)),
393 HTTP_Internal_Server_Error)
394
395 def _create_ofc_thread(self, of_conn, ofc_uuid="Default"):
396 """
397 Create and launch a of thread
398 :return: thread obj
399 """
400 # create openflow thread
401
montesmoreno92827552017-03-30 13:24:17 +0200402 #if 'of_controller_nets_with_same_vlan' in self.config:
403 # ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
404 #else:
405 # ofc_net_same_vlan = False
406 ofc_net_same_vlan = False
mirabal580435e2017-03-01 16:17:10 +0100407
408 thread = oft.openflow_thread(ofc_uuid, of_conn, of_test=self.of_test_mode, db=self.db_of, db_lock=self.db_lock,
409 pmp_with_same_vlan=ofc_net_same_vlan, debug=self.config['log_level_of'])
410 #r, c = thread.OF_connector.obtain_port_correspondence()
411 #if r < 0:
412 # raise ovimException("Cannot get openflow information %s", c)
413 thread.start()
414 return thread
415
tierno57f7bda2017-02-09 12:01:55 +0100416 def stop_service(self):
417 threads = self.config.get('host_threads', {})
418 if 'of_thread' in self.config:
419 threads['of'] = (self.config['of_thread'])
mirabal580435e2017-03-01 16:17:10 +0100420 if 'ofcs_thread' in self.config:
421 ofcs_thread = self.config['ofcs_thread']
422 for ofc in ofcs_thread:
423 threads[ofc] = ofcs_thread[ofc]
424
tierno57f7bda2017-02-09 12:01:55 +0100425 if 'dhcp_thread' in self.config:
426 threads['dhcp'] = (self.config['dhcp_thread'])
427
428 for thread in threads.values():
429 thread.insert_task("exit")
430 for thread in threads.values():
431 thread.join()
tierno57f7bda2017-02-09 12:01:55 +0100432
mirabal9e194592017-02-17 11:03:25 +0100433 def get_networks(self, columns=None, db_filter={}, limit=None):
mirabal65ba8f82017-02-15 12:36:33 +0100434 """
435 Retreive networks available
mirabale9f6f1a2017-02-16 17:57:35 +0100436 :param columns: List with select query parameters
mirabal9e194592017-02-17 11:03:25 +0100437 :param db_filter: List with where query parameters
mirabale9f6f1a2017-02-16 17:57:35 +0100438 :param limit: Query limit result
mirabal65ba8f82017-02-15 12:36:33 +0100439 :return:
440 """
mirabal9e194592017-02-17 11:03:25 +0100441 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=db_filter, LIMIT=limit)
mirabal65ba8f82017-02-15 12:36:33 +0100442
443 if result < 0:
444 raise ovimException(str(content), -result)
445
446 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
447
448 return content
449
mirabal9e194592017-02-17 11:03:25 +0100450 def show_network(self, network_id, db_filter={}):
mirabal65ba8f82017-02-15 12:36:33 +0100451 """
mirabale9f6f1a2017-02-16 17:57:35 +0100452 Get network from DB by id
453 :param network_id: net Id
mirabal9e194592017-02-17 11:03:25 +0100454 :param db_filter: List with where query parameters
mirabal65ba8f82017-02-15 12:36:33 +0100455 :return:
456 """
457 # obtain data
mirabale9f6f1a2017-02-16 17:57:35 +0100458 if not network_id:
459 raise ovimException("Not network id was not found")
mirabal9e194592017-02-17 11:03:25 +0100460 db_filter['uuid'] = network_id
mirabal65ba8f82017-02-15 12:36:33 +0100461
mirabal9e194592017-02-17 11:03:25 +0100462 result, content = self.db.get_table(FROM='nets', WHERE=db_filter, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100463
464 if result < 0:
465 raise ovimException(str(content), -result)
466 elif result == 0:
mirabale9f6f1a2017-02-16 17:57:35 +0100467 raise ovimException("show_network network '%s' not found" % network_id, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100468 else:
469 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
mirabale9f6f1a2017-02-16 17:57:35 +0100470 # get ports from DB
mirabal65ba8f82017-02-15 12:36:33 +0100471 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
mirabale9f6f1a2017-02-16 17:57:35 +0100472 WHERE={'net_id': network_id}, LIMIT=100)
mirabal65ba8f82017-02-15 12:36:33 +0100473 if len(ports) > 0:
474 content[0]['ports'] = ports
mirabal65ba8f82017-02-15 12:36:33 +0100475
mirabale9f6f1a2017-02-16 17:57:35 +0100476 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
477 return content[0]
478
479 def new_network(self, network):
mirabal65ba8f82017-02-15 12:36:33 +0100480 """
mirabale9f6f1a2017-02-16 17:57:35 +0100481 Create a net in DB
mirabal65ba8f82017-02-15 12:36:33 +0100482 :return:
483 """
484 tenant_id = network.get('tenant_id')
485
486 if tenant_id:
487 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
488 if result <= 0:
489 raise ovimException("set_network error, no tenant founded", -result)
490
491 bridge_net = None
492 # check valid params
493 net_provider = network.get('provider')
494 net_type = network.get('type')
mirabal65ba8f82017-02-15 12:36:33 +0100495 net_vlan = network.get("vlan")
496 net_bind_net = network.get("bind_net")
497 net_bind_type = network.get("bind_type")
498 name = network["name"]
499
500 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
501 vlan_index = name.rfind(":")
502 if not net_bind_net and not net_bind_type and vlan_index > 1:
503 try:
504 vlan_tag = int(name[vlan_index + 1:])
505 if not vlan_tag and vlan_tag < 4096:
506 net_bind_net = name[:vlan_index]
507 net_bind_type = "vlan:" + name[vlan_index + 1:]
508 except:
509 pass
510
511 if net_bind_net:
512 # look for a valid net
513 if self._check_valid_uuid(net_bind_net):
514 net_bind_key = "uuid"
515 else:
516 net_bind_key = "name"
517 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
518 if result < 0:
519 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
520 elif result == 0:
521 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
522 elif result > 1:
523 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
524 network["bind_net"] = content[0]["uuid"]
525
526 if net_bind_type:
527 if net_bind_type[0:5] != "vlan:":
528 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
529 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
530 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
531 HTTP_Bad_Request)
532 network["bind_type"] = net_bind_type
533
534 if net_provider:
535 if net_provider[:9] == "openflow:":
536 if net_type:
537 if net_type != "ptp" and net_type != "data":
538 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
539 HTTP_Bad_Request)
540 else:
541 net_type = 'data'
542 else:
543 if net_type:
544 if net_type != "bridge_man" and net_type != "bridge_data":
545 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
546 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
547 else:
548 net_type = 'bridge_man'
549
550 if not net_type:
551 net_type = 'bridge_man'
552
553 if net_provider:
554 if net_provider[:7] == 'bridge:':
555 # check it is one of the pre-provisioned bridges
556 bridge_net_name = net_provider[7:]
557 for brnet in self.config['bridge_nets']:
558 if brnet[0] == bridge_net_name: # free
559 if not brnet[3]:
560 raise ovimException("invalid 'provider:physical', "
561 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
562 bridge_net = brnet
563 net_vlan = brnet[1]
564 break
mirabale9f6f1a2017-02-16 17:57:35 +0100565 # if bridge_net==None:
566 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
567 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
568 # return
569
mirabal65ba8f82017-02-15 12:36:33 +0100570 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
571 # look for a free precreated nets
572 for brnet in self.config['bridge_nets']:
573 if not brnet[3]: # free
574 if not bridge_net:
575 if net_type == 'bridge_man': # look for the smaller speed
576 if brnet[2] < bridge_net[2]:
577 bridge_net = brnet
578 else: # look for the larger speed
579 if brnet[2] > bridge_net[2]:
580 bridge_net = brnet
581 else:
582 bridge_net = brnet
583 net_vlan = brnet[1]
584 if not bridge_net:
585 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
586 "will overcome this limit", HTTP_Bad_Request)
587 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100588 self.logger.debug("using net " + bridge_net)
mirabal65ba8f82017-02-15 12:36:33 +0100589 net_provider = "bridge:" + bridge_net[0]
590 net_vlan = bridge_net[1]
591 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
592 net_provider = 'OVS'
593 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
594 net_vlan = self.db.get_free_net_vlan()
595 if net_vlan < 0:
596 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
597 if net_provider == 'OVS':
598 net_provider = 'OVS' + ":" + str(net_vlan)
599
600 network['provider'] = net_provider
601 network['type'] = net_type
602 network['vlan'] = net_vlan
603 dhcp_integrity = True
604 if 'enable_dhcp' in network and network['enable_dhcp']:
605 dhcp_integrity = self._check_dhcp_data_integrity(network)
606
607 result, content = self.db.new_row('nets', network, True, True)
608
609 if result >= 0 and dhcp_integrity:
610 if bridge_net:
611 bridge_net[3] = content
612 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
613 if network["name"] in self.config["dhcp_server"].get("nets", ()):
614 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100615 self.logger.debug("dhcp_server: add new net", content)
mirabal65ba8f82017-02-15 12:36:33 +0100616 elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
617 self.config["dhcp_nets"].append(content)
mirabale9f6f1a2017-02-16 17:57:35 +0100618 self.logger.debug("dhcp_server: add new net", content, content)
mirabal65ba8f82017-02-15 12:36:33 +0100619 return content
620 else:
mirabal65ba8f82017-02-15 12:36:33 +0100621 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
mirabale9f6f1a2017-02-16 17:57:35 +0100622# TODO kei change update->edit
mirabal65ba8f82017-02-15 12:36:33 +0100623
mirabale9f6f1a2017-02-16 17:57:35 +0100624 def edit_network(self, network_id, network):
mirabal65ba8f82017-02-15 12:36:33 +0100625 """
mirabale9f6f1a2017-02-16 17:57:35 +0100626 Update entwork data byt id
mirabal65ba8f82017-02-15 12:36:33 +0100627 :return:
628 """
mirabale9f6f1a2017-02-16 17:57:35 +0100629 # Look for the previous data
630 where_ = {'uuid': network_id}
631 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
632 if result < 0:
633 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
634 elif result == 0:
635 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
636 # get ports
637 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
638 WHERE={'net_id': network_id}, LIMIT=100)
639 if result < 0:
640 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
641 if nbports > 0:
642 if 'type' in network and network['type'] != network_old[0]['type']:
643 raise ovimException("Can not change type of network while having ports attached",
644 HTTP_Method_Not_Allowed)
645 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
646 raise ovimException("Can not change vlan of network while having ports attached",
647 HTTP_Method_Not_Allowed)
mirabal65ba8f82017-02-15 12:36:33 +0100648
mirabale9f6f1a2017-02-16 17:57:35 +0100649 # check valid params
650 net_provider = network.get('provider', network_old[0]['provider'])
651 net_type = network.get('type', network_old[0]['type'])
652 net_bind_net = network.get("bind_net")
653 net_bind_type = network.get("bind_type")
654 if net_bind_net:
655 # look for a valid net
656 if self._check_valid_uuid(net_bind_net):
657 net_bind_key = "uuid"
658 else:
659 net_bind_key = "name"
660 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
661 if result < 0:
662 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
663 elif result == 0:
664 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
665 elif result > 1:
666 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
667 HTTP_Bad_Request)
668 network["bind_net"] = content[0]["uuid"]
669 if net_bind_type:
670 if net_bind_type[0:5] != "vlan:":
671 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
672 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
673 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
674 HTTP_Bad_Request)
675 if net_provider:
676 if net_provider[:9] == "openflow:":
677 if net_type != "ptp" and net_type != "data":
678 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
679 else:
680 if net_type != "bridge_man" and net_type != "bridge_data":
681 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
682 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
mirabal65ba8f82017-02-15 12:36:33 +0100683
mirabale9f6f1a2017-02-16 17:57:35 +0100684 # insert in data base
685 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
686 if result >= 0:
687 # if result > 0 and nbports>0 and 'admin_state_up' in network
688 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
689 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100690
691 try:
tiernoaa941462017-03-29 15:10:28 +0200692 if nbports:
693 self.net_update_ofc_thread(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100694 except ovimException as e:
695 raise ovimException("Error while launching openflow rules in network '{}' {}"
696 .format(network_id, str(e)), HTTP_Internal_Server_Error)
697 except Exception as e:
698 raise ovimException("Error while launching openflow rules in network '{}' {}"
699 .format(network_id, str(e)), HTTP_Internal_Server_Error)
700
mirabale9f6f1a2017-02-16 17:57:35 +0100701 if self.config.get("dhcp_server"):
702 if network_id in self.config["dhcp_nets"]:
703 self.config["dhcp_nets"].remove(network_id)
mirabal7bbf50e2017-03-13 15:15:18 +0100704 if network.get("name", network_old[0]["name"]) in self.config["dhcp_server"].get("nets", ()):
mirabale9f6f1a2017-02-16 17:57:35 +0100705 self.config["dhcp_nets"].append(network_id)
706 else:
mirabal7bbf50e2017-03-13 15:15:18 +0100707 net_bind = network.get("bind_type", network_old[0]["bind_type"])
708 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 +0100709 "bridge_ifaces", ()):
710 self.config["dhcp_nets"].append(network_id)
711 return network_id
mirabal65ba8f82017-02-15 12:36:33 +0100712 else:
mirabale9f6f1a2017-02-16 17:57:35 +0100713 raise ovimException(content, -result)
mirabal65ba8f82017-02-15 12:36:33 +0100714
mirabale9f6f1a2017-02-16 17:57:35 +0100715 def delete_network(self, network_id):
mirabal9e194592017-02-17 11:03:25 +0100716 """
717 Delete network by network id
718 :param network_id: network id
719 :return:
720 """
mirabale9f6f1a2017-02-16 17:57:35 +0100721
722 # delete from the data base
723 result, content = self.db.delete_row('nets', network_id)
724
725 if result == 0:
726 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
727 elif result > 0:
728 for brnet in self.config['bridge_nets']:
729 if brnet[3] == network_id:
730 brnet[3] = None
731 break
732 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
733 self.config["dhcp_nets"].remove(network_id)
734 return content
735 else:
736 raise ovimException("Error deleting network %s" % network_id, HTTP_Internal_Server_Error)
mirabal65ba8f82017-02-15 12:36:33 +0100737
738 def get_openflow_rules(self, network_id=None):
739 """
740 Get openflow id from DB
741 :param network_id: Network id, if none all networks will be retrieved
742 :return: Return a list with Openflow rules per net
743 """
744 # ignore input data
745 if not network_id:
746 where_ = {}
747 else:
748 where_ = {"net_id": network_id}
mirabal65ba8f82017-02-15 12:36:33 +0100749 result, content = self.db.get_table(
mirabalf9a1a8d2017-03-15 12:42:27 +0100750 SELECT=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
mirabal65ba8f82017-02-15 12:36:33 +0100751 WHERE=where_, FROM='of_flows')
752
753 if result < 0:
754 raise ovimException(str(content), -result)
755 return content
756
mirabale9f6f1a2017-02-16 17:57:35 +0100757 def edit_openflow_rules(self, network_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100758
759 """
760 To make actions over the net. The action is to reinstall the openflow rules
761 network_id can be 'all'
762 :param network_id: Network id, if none all networks will be retrieved
763 :return : Number of nets updated
764 """
765
766 # ignore input data
767 if not network_id:
768 where_ = {}
769 else:
770 where_ = {"uuid": network_id}
771 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
772
773 if result < 0:
774 raise ovimException(str(content), -result)
775
776 for net in content:
777 if net["type"] != "ptp" and net["type"] != "data":
778 result -= 1
779 continue
mirabal7bbf50e2017-03-13 15:15:18 +0100780
781 try:
782 self.net_update_ofc_thread(net['uuid'])
783 except ovimException as e:
784 raise ovimException("Error updating network'{}' {}".format(net['uuid'], str(e)),
785 HTTP_Internal_Server_Error)
786 except Exception as e:
787 raise ovimException("Error updating network '{}' {}".format(net['uuid'], str(e)),
788 HTTP_Internal_Server_Error)
789
mirabal65ba8f82017-02-15 12:36:33 +0100790 return result
791
mirabalf9a1a8d2017-03-15 12:42:27 +0100792 def delete_openflow_rules(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100793 """
794 To make actions over the net. The action is to delete ALL openflow rules
795 :return: return operation result
796 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100797
798 if not ofc_id:
799 if 'Default' in self.config['ofcs_thread']:
800 r, c = self.config['ofcs_thread']['Default'].insert_task("clear-all")
801 else:
802 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
803
804 elif ofc_id in self.config['ofcs_thread']:
805 r, c = self.config['ofcs_thread'][ofc_id].insert_task("clear-all")
806
807 # ignore input data
808 if r < 0:
809 raise ovimException(str(c), -r)
810 else:
811 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
mirabal65ba8f82017-02-15 12:36:33 +0100812 return r
813
mirabalf9a1a8d2017-03-15 12:42:27 +0100814 def get_openflow_ports(self, ofc_id=None):
mirabal65ba8f82017-02-15 12:36:33 +0100815 """
816 Obtain switch ports names of openflow controller
817 :return: Return flow ports in DB
818 """
mirabalf9a1a8d2017-03-15 12:42:27 +0100819 if not ofc_id:
820 if 'Default' in self.config['ofcs_thread']:
821 conn = self.config['ofcs_thread']['Default'].OF_connector
822 else:
823 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found)
824
825 if ofc_id in self.config['ofcs_thread']:
826 conn = self.config['ofcs_thread'][ofc_id].OF_connector
827 else:
828 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id), HTTP_Not_Found)
829 return conn.pp2ofi
tierno57f7bda2017-02-09 12:01:55 +0100830
831 def get_ports(self, columns=None, filter={}, limit=None):
832 # result, content = my.db.get_ports(where_)
833 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
834 if result < 0:
835 self.logger.error("http_get_ports Error %d %s", result, content)
836 raise ovimException(str(content), -result)
837 else:
838 convert_boolean(content, ('admin_state_up',))
839 return content
840
tierno57f7bda2017-02-09 12:01:55 +0100841 def new_port(self, port_data):
842 port_data['type'] = 'external'
843 if port_data.get('net_id'):
844 # check that new net has the correct type
845 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
846 if result < 0:
847 raise ovimException(str(new_net), -result)
848 # insert in data base
849 result, uuid = self.db.new_row('ports', port_data, True, True)
850 if result > 0:
851 if 'net_id' in port_data:
mirabal7bbf50e2017-03-13 15:15:18 +0100852 try:
853 self.net_update_ofc_thread(port_data['net_id'])
854 except ovimException as e:
855 raise ovimException("Cannot insert a task for updating network '{}' {}"
856 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
857 except Exception as e:
858 raise ovimException("Cannot insert a task for updating network '{}' {}"
859 .format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
860
tierno57f7bda2017-02-09 12:01:55 +0100861 return uuid
862 else:
863 raise ovimException(str(uuid), -result)
864
mirabal37829452017-03-09 14:41:21 +0100865 def new_external_port(self, port_data):
866 """
867 Create new external port and check port mapping correspondence
868 :param port_data: port_data = {
869 'region': 'datacenter region',
870 'compute_node': 'compute node id',
871 'pci': 'pci port address',
872 'vlan': 'net vlan',
873 'net_id': 'net id',
874 'tenant_id': 'tenant id',
875 'mac': 'switch mac',
876 'name': 'port name'
877 'ip_address': 'ip address - optional'}
878 :return:
879 """
880
881 port_data['type'] = 'external'
882
883 if port_data.get('net_id'):
884 # check that new net has the correct type
885 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
886 if result < 0:
887 raise ovimException(str(new_net), -result)
888 # insert in data base
889 db_filter = {}
890
891 if port_data.get('region'):
892 db_filter['region'] = port_data['region']
893 if port_data.get('pci'):
894 db_filter['pci'] = port_data['pci']
895 if port_data.get('compute_node'):
896 db_filter['compute_node'] = port_data['compute_node']
897
898 columns = ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
899 port_mapping_data = self.get_of_port_mappings(columns, db_filter)
900
901 if not len(port_mapping_data):
tierno16007502017-03-27 16:48:32 +0200902 raise ovimException("No port mapping founded for '{}'".format(str(db_filter)),
mirabal37829452017-03-09 14:41:21 +0100903 HTTP_Not_Found)
904 elif len(port_mapping_data) > 1:
905 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
906 HTTP_Conflict)
907
908 port_data['ofc_id'] = port_mapping_data[0]['ofc_id']
909 port_data['switch_dpid'] = port_mapping_data[0]['switch_dpid']
910 port_data['switch_port'] = port_mapping_data[0]['switch_port']
911 port_data['switch_mac'] = port_mapping_data[0]['switch_mac']
912
913 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
montesmoreno275b1992017-03-28 15:45:02 +0200914 if 'region' in port_data:
915 del port_data['region']
916 if 'pci' in port_data:
917 del port_data['pci']
918 if 'compute_node' in port_data:
919 del port_data['compute_node']
mirabal37829452017-03-09 14:41:21 +0100920
921 result, uuid = self.db.new_row('ports', port_data, True, True)
922 if result > 0:
mirabal7bbf50e2017-03-13 15:15:18 +0100923 try:
924 self.net_update_ofc_thread(port_data['net_id'], port_data['ofc_id'])
925 except ovimException as e:
926 raise ovimException("Cannot insert a task for updating network '{}' {}".
927 format(port_data['net_id'], str(e)), HTTP_Internal_Server_Error)
928 except Exception as e:
929 raise ovimException("Cannot insert a task for updating network '{}' {}"
930 .format(port_data['net_id'], e), HTTP_Internal_Server_Error)
mirabal37829452017-03-09 14:41:21 +0100931 return uuid
932 else:
933 raise ovimException(str(uuid), -result)
934
tiernoaa941462017-03-29 15:10:28 +0200935 def net_update_ofc_thread(self, net_id, ofc_id=None, switch_dpid=None):
mirabal7bbf50e2017-03-13 15:15:18 +0100936 """
937 Insert a update net task by net id or ofc_id for each ofc thread
938 :param net_id: network id
939 :param ofc_id: openflow controller id
tiernoaa941462017-03-29 15:10:28 +0200940 :param switch_dpid: switch dpid
mirabal7bbf50e2017-03-13 15:15:18 +0100941 :return:
942 """
943 if not net_id:
944 raise ovimException("No net_id received", HTTP_Internal_Server_Error)
945
mirabal7bbf50e2017-03-13 15:15:18 +0100946 r = -1
947 c = 'No valid ofc_id or switch_dpid received'
948
949 if not ofc_id:
950 ports = self.get_ports(filter={"net_id": net_id})
951 for port in ports:
952 port_ofc_id = port.get('ofc_id', None)
953 if port_ofc_id:
954 ofc_id = port['ofc_id']
955 switch_dpid = port['switch_dpid']
956 break
tiernoaa941462017-03-29 15:10:28 +0200957 #TODO if not ofc_id: look at database table ofcs
958
mirabal7bbf50e2017-03-13 15:15:18 +0100959
960 # If no ofc_id found it, default ofc_id is used.
961 if not ofc_id and not switch_dpid:
962 ofc_id = "Default"
963
964 if ofc_id and ofc_id in self.config['ofcs_thread']:
965 r, c = self.config['ofcs_thread'][ofc_id].insert_task("update-net", net_id)
966 elif switch_dpid:
967
968 ofcs_dpid_list = self.config['ofcs_thread_dpid']
969 for ofc_t in ofcs_dpid_list:
970 if switch_dpid in ofc_t:
971 r, c = ofc_t[switch_dpid].insert_task("update-net", net_id)
972
973 if r < 0:
tierno82232582017-03-15 18:09:16 +0100974 message = "Cannot insert a task for updating network '{}', {}".format(net_id, c)
mirabal7bbf50e2017-03-13 15:15:18 +0100975 self.logger.error(message)
976 raise ovimException(message, HTTP_Internal_Server_Error)
977
tierno57f7bda2017-02-09 12:01:55 +0100978 def delete_port(self, port_id):
979 # Look for the previous port data
980 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
981 if result < 0:
982 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
983 # delete from the data base
984 result, content = self.db.delete_row('ports', port_id)
985 if result == 0:
986 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
987 elif result < 0:
988 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
989 # update network
990 network = ports[0].get('net_id', None)
991 if network:
992 # change of net.
mirabal7bbf50e2017-03-13 15:15:18 +0100993
994 try:
tiernoaa941462017-03-29 15:10:28 +0200995 self.net_update_ofc_thread(network, ofc_id=ports[0]["ofc_id"], switch_dpid=ports[0]["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +0100996 except ovimException as e:
997 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
998 HTTP_Internal_Server_Error)
999 except Exception as e:
1000 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network, str(e)),
1001 HTTP_Internal_Server_Error)
1002
tierno57f7bda2017-02-09 12:01:55 +01001003 return content
1004
tierno57f7bda2017-02-09 12:01:55 +01001005 def edit_port(self, port_id, port_data, admin=True):
1006 # Look for the previous port data
1007 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
1008 if result < 0:
1009 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
1010 elif result == 0:
1011 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
1012 port = content[0]
1013 nets = []
1014 host_id = None
1015 result = 1
1016 if 'net_id' in port_data:
1017 # change of net.
1018 old_net = port.get('net_id', None)
1019 new_net = port_data['net_id']
1020 if old_net != new_net:
1021
1022 if new_net:
1023 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
1024 if old_net:
1025 nets.append(old_net)
1026 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
1027 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
1028 elif port['type'] == 'external' and not admin:
1029 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
1030 if new_net:
1031 # check that new net has the correct type
1032 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
1033 if result < 0:
1034 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
1035 # change VLAN for SR-IOV ports
1036 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
1037 if new_net:
1038 port_data["vlan"] = None
1039 else:
1040 port_data["vlan"] = new_net_dict["vlan"]
1041 # get host where this VM is allocated
1042 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
1043 if result > 0:
1044 host_id = content[0]["host_id"]
1045
1046 # insert in data base
1047 if result >= 0:
1048 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
tiernoaa941462017-03-29 15:10:28 +02001049 port.update(port_data)
tierno57f7bda2017-02-09 12:01:55 +01001050
1051 # Insert task to complete actions
1052 if result > 0:
1053 for net_id in nets:
mirabal7bbf50e2017-03-13 15:15:18 +01001054 try:
tiernoaa941462017-03-29 15:10:28 +02001055 self.net_update_ofc_thread(net_id, port["ofc_id"], switch_dpid=port["switch_dpid"])
mirabal7bbf50e2017-03-13 15:15:18 +01001056 except ovimException as e:
1057 raise ovimException("Error updating network'{}' {}".format(net_id, str(e)),
1058 HTTP_Internal_Server_Error)
1059 except Exception as e:
1060 raise ovimException("Error updating network '{}' {}".format(net_id, str(e)),
1061 HTTP_Internal_Server_Error)
1062
tierno57f7bda2017-02-09 12:01:55 +01001063 if host_id:
1064 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
1065 if r < 0:
1066 self.logger.error("Error updating network '{}' {}".format(r,v))
1067 # TODO Do something if fails
1068 if result >= 0:
1069 return port_id
1070 else:
1071 raise ovimException("Error {}".format(content), http_code=-result)
mirabalb716ac52017-02-10 14:47:53 +01001072
mirabal9e194592017-02-17 11:03:25 +01001073 def new_of_controller(self, ofc_data):
1074 """
1075 Create a new openflow controller into DB
1076 :param ofc_data: Dict openflow controller data
1077 :return: openflow controller dpid
1078 """
1079
mirabal580435e2017-03-01 16:17:10 +01001080 result, ofc_uuid = self.db.new_row('ofcs', ofc_data, True, True)
mirabal9e194592017-02-17 11:03:25 +01001081 if result < 0:
mirabal580435e2017-03-01 16:17:10 +01001082 raise ovimException("New ofc Error %s" % ofc_uuid, HTTP_Internal_Server_Error)
1083
1084 ofc_data['uuid'] = ofc_uuid
1085 of_conn = self._load_of_module(ofc_data)
1086 self._create_ofc_task(ofc_uuid, ofc_data['dpid'], of_conn)
1087
1088 return ofc_uuid
mirabal9e194592017-02-17 11:03:25 +01001089
1090 def edit_of_controller(self, of_id, ofc_data):
1091 """
1092 Edit an openflow controller entry from DB
1093 :return:
1094 """
1095 if not ofc_data:
1096 raise ovimException("No data received during uptade OF contorller", http_code=HTTP_Internal_Server_Error)
1097
1098 old_of_controller = self.show_of_controller(of_id)
1099
1100 if old_of_controller:
1101 result, content = self.db.update_rows('ofcs', ofc_data, WHERE={'uuid': of_id}, log=False)
1102 if result >= 0:
1103 return ofc_data
1104 else:
1105 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1106 http_code=-result)
1107 else:
1108 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id),
1109 http_code=HTTP_Internal_Server_Error)
1110
1111 def delete_of_controller(self, of_id):
1112 """
1113 Delete an openflow controller from DB.
1114 :param of_id: openflow controller dpid
1115 :return:
1116 """
1117
mirabal580435e2017-03-01 16:17:10 +01001118 ofc = self.show_of_controller(of_id)
1119
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001120 result, content = self.db.delete_row("ofcs", of_id)
mirabal9e194592017-02-17 11:03:25 +01001121 if result < 0:
1122 raise ovimException("Cannot delete ofc from database: {}".format(content), http_code=-result)
1123 elif result == 0:
1124 raise ovimException("ofc {} not found ".format(content), http_code=HTTP_Not_Found)
mirabal580435e2017-03-01 16:17:10 +01001125
1126 ofc_thread = self.config['ofcs_thread'][of_id]
1127 del self.config['ofcs_thread'][of_id]
1128 for ofc_th in self.config['ofcs_thread_dpid']:
1129 if ofc['dpid'] in ofc_th:
1130 self.config['ofcs_thread_dpid'].remove(ofc_th)
1131
1132 ofc_thread.insert_task("exit")
1133 #ofc_thread.join()
1134
mirabal9e194592017-02-17 11:03:25 +01001135 return content
1136
1137 def show_of_controller(self, uuid):
1138 """
1139 Show an openflow controller by dpid from DB.
1140 :param db_filter: List with where query parameters
1141 :return:
1142 """
1143
1144 result, content = self.db.get_table(FROM='ofcs', WHERE={"uuid": uuid}, LIMIT=100)
1145
1146 if result == 0:
1147 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid),
1148 http_code=HTTP_Not_Found)
1149 elif result < 0:
1150 raise ovimException("Openflow controller with uuid '{}' error".format(uuid),
1151 http_code=HTTP_Internal_Server_Error)
Pablo Montes Moreno5b6f7492017-03-02 16:18:36 +01001152 return content[0]
mirabal9e194592017-02-17 11:03:25 +01001153
mirabalfbfb7972017-02-27 17:36:17 +01001154 def get_of_controllers(self, columns=None, db_filter={}, limit=None):
mirabal9e194592017-02-17 11:03:25 +01001155 """
1156 Show an openflow controllers from DB.
1157 :param columns: List with SELECT query parameters
1158 :param db_filter: List with where query parameters
mirabalfbfb7972017-02-27 17:36:17 +01001159 :param limit: result Limit
mirabal9e194592017-02-17 11:03:25 +01001160 :return:
1161 """
mirabalfbfb7972017-02-27 17:36:17 +01001162 result, content = self.db.get_table(SELECT=columns, FROM='ofcs', WHERE=db_filter, LIMIT=limit)
mirabal9e194592017-02-17 11:03:25 +01001163
1164 if result < 0:
1165 raise ovimException(str(content), -result)
1166
1167 return content
1168
mirabalfbfb7972017-02-27 17:36:17 +01001169 def get_tenants(self, columns=None, db_filter={}, limit=None):
1170 """
1171 Retrieve tenant list from DB
1172 :param columns: List with SELECT query parameters
1173 :param db_filter: List with where query parameters
1174 :param limit: result limit
1175 :return:
1176 """
1177 result, content = self.db.get_table(FROM='tenants', SELECT=columns, WHERE=db_filter, LIMIT=limit)
1178 if result < 0:
1179 raise ovimException('get_tenatns Error {}'.format(str(content)), -result)
1180 else:
1181 convert_boolean(content, ('enabled',))
1182 return content
1183
1184 def show_tenant_id(self, tenant_id):
1185 """
1186 Get tenant from DB by id
1187 :param tenant_id: tenant id
1188 :return:
1189 """
1190 result, content = self.db.get_table(FROM='tenants', SELECT=('uuid', 'name', 'description', 'enabled'),
1191 WHERE={"uuid": tenant_id})
1192 if result < 0:
1193 raise ovimException(str(content), -result)
1194 elif result == 0:
1195 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1196 else:
1197 convert_boolean(content, ('enabled',))
1198 return content[0]
1199
1200 def new_tentant(self, tenant):
1201 """
1202 Create a tenant and store in DB
1203 :param tenant: Dictionary with tenant data
1204 :return: the uuid of created tenant. Raise exception upon error
1205 """
1206
1207 # insert in data base
1208 result, tenant_uuid = self.db.new_tenant(tenant)
1209
1210 if result >= 0:
1211 return tenant_uuid
1212 else:
1213 raise ovimException(str(tenant_uuid), -result)
1214
1215 def delete_tentant(self, tenant_id):
1216 """
1217 Delete a tenant from the database.
1218 :param tenant_id: Tenant id
1219 :return: delete tenant id
1220 """
1221
1222 # check permissions
1223 r, tenants_flavors = self.db.get_table(FROM='tenants_flavors', SELECT=('flavor_id', 'tenant_id'),
1224 WHERE={'tenant_id': tenant_id})
1225 if r <= 0:
1226 tenants_flavors = ()
1227 r, tenants_images = self.db.get_table(FROM='tenants_images', SELECT=('image_id', 'tenant_id'),
1228 WHERE={'tenant_id': tenant_id})
1229 if r <= 0:
1230 tenants_images = ()
1231
1232 result, content = self.db.delete_row('tenants', tenant_id)
1233 if result == 0:
1234 raise ovimException("tenant '%s' not found" % tenant_id, HTTP_Not_Found)
1235 elif result > 0:
1236 for flavor in tenants_flavors:
1237 self.db.delete_row_by_key("flavors", "uuid", flavor['flavor_id'])
1238 for image in tenants_images:
1239 self.db.delete_row_by_key("images", "uuid", image['image_id'])
1240 return content
1241 else:
1242 raise ovimException("Error deleting tenant '%s' " % tenant_id, HTTP_Internal_Server_Error)
1243
1244 def edit_tenant(self, tenant_id, tenant_data):
1245 """
1246 Update a tenant data identified by tenant id
1247 :param tenant_id: tenant id
1248 :param tenant_data: Dictionary with tenant data
1249 :return:
1250 """
1251
1252 # Look for the previous data
1253 result, tenant_data_old = self.db.get_table(FROM='tenants', WHERE={'uuid': tenant_id})
1254 if result < 0:
1255 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id, tenant_data_old),
1256 HTTP_Internal_Server_Error)
1257 elif result == 0:
1258 raise ovimException("tenant with uuid='{}' not found".format(tenant_id), HTTP_Not_Found)
1259
1260 # insert in data base
1261 result, content = self.db.update_rows('tenants', tenant_data, WHERE={'uuid': tenant_id}, log=True)
1262 if result >= 0:
1263 return content
1264 else:
1265 raise ovimException(str(content), -result)
1266
mirabal6045a9d2017-03-06 11:36:55 +01001267 def set_of_port_mapping(self, of_maps, ofc_id=None, switch_dpid=None, region=None):
1268 """
1269 Create new port mapping entry
1270 :param of_maps: List with port mapping information
1271 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1272 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1273 :param ofc_id: ofc id
1274 :param switch_dpid: switch dpid
1275 :param region: datacenter region id
1276 :return:
1277 """
1278
1279 for map in of_maps:
1280 if ofc_id:
1281 map['ofc_id'] = ofc_id
1282 if switch_dpid:
1283 map['switch_dpid'] = switch_dpid
1284 if region:
1285 map['region'] = region
1286
1287 for of_map in of_maps:
1288 result, uuid = self.db.new_row('of_port_mappings', of_map, True)
1289 if result > 0:
1290 of_map["uuid"] = uuid
1291 else:
1292 raise ovimException(str(uuid), -result)
1293 return of_maps
1294
1295 def clear_of_port_mapping(self, db_filter={}):
1296 """
1297 Clear port mapping filtering using db_filter dict
1298 :param db_filter: Parameter to filter during remove process
1299 :return:
1300 """
1301 result, content = self.db.delete_row_by_dict(FROM='of_port_mappings', WHERE=db_filter)
1302 # delete_row_by_key
1303 if result >= 0:
1304 return content
1305 else:
1306 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter)),
1307 HTTP_Internal_Server_Error)
1308
1309 def get_of_port_mappings(self, column=None, db_filter=None, db_limit=None):
1310 """
1311 Retrive port mapping from DB
1312 :param column:
1313 :param db_filter:
1314 :return:
1315 """
1316 result, content = self.db.get_table(SELECT=column, WHERE=db_filter, FROM='of_port_mappings', LIMIT=db_limit)
1317
1318 if result < 0:
1319 self.logger.error("get_of_port_mappings Error %d %s", result, content)
1320 raise ovimException(str(content), -result)
1321 else:
1322 return content
1323
mirabalb716ac52017-02-10 14:47:53 +01001324 def get_dhcp_controller(self):
1325 """
1326 Create an host_thread object for manage openvim controller and not create a thread for itself
1327 :return: dhcp_host openvim controller object
1328 """
1329
1330 if 'openvim_controller' in self.config['host_threads']:
1331 return self.config['host_threads']['openvim_controller']
1332
1333 bridge_ifaces = []
1334 controller_ip = self.config['ovs_controller_ip']
1335 ovs_controller_user = self.config['ovs_controller_user']
1336
1337 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
1338 host_develop_mode = True if self.config['mode'] == 'development' else False
1339
1340 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
tierno686b3952017-03-10 13:57:24 +01001341 db=self.db_of,
1342 db_lock=self.db_lock, test=host_test_mode,
mirabalb716ac52017-02-10 14:47:53 +01001343 image_path=self.config['image_path'], version=self.config['version'],
1344 host_id='openvim_controller', develop_mode=host_develop_mode,
1345 develop_bridge_iface=bridge_ifaces)
1346
1347 self.config['host_threads']['openvim_controller'] = dhcp_host
1348 if not host_test_mode:
1349 dhcp_host.ssh_connect()
1350 return dhcp_host
1351
mirabal18f5de32017-02-13 12:41:49 +01001352 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
mirabalb716ac52017-02-10 14:47:53 +01001353 """
1354 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1355 :param vlan: vlan identifier
1356 :param first_ip: First dhcp range ip
1357 :param last_ip: Last dhcp range ip
1358 :param cidr: net cidr
mirabale9f6f1a2017-02-16 17:57:35 +01001359 :param gateway: net gateway
mirabalb716ac52017-02-10 14:47:53 +01001360 :return:
1361 """
1362 ip_tools = IPNetwork(cidr)
1363 dhcp_netmask = str(ip_tools.netmask)
1364 ip_range = [first_ip, last_ip]
1365
1366 dhcp_path = self.config['ovs_controller_file_path']
1367
1368 controller_host = self.get_dhcp_controller()
1369 controller_host.create_linux_bridge(vlan)
1370 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
mirabal18f5de32017-02-13 12:41:49 +01001371 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
mirabalb716ac52017-02-10 14:47:53 +01001372
mirabald87877c2017-03-31 15:15:52 +02001373if __name__ == "__main__":
1374
1375 parser = argparse.ArgumentParser()
tierno46ca3a92017-04-05 19:49:24 +02001376 parser.add_argument("-v","--version", help="show ovim library version", action="store_true")
1377 parser.add_argument("--database-version", help="show required database version", action="store_true")
mirabald87877c2017-03-31 15:15:52 +02001378 args = parser.parse_args()
1379 if args.version:
1380 print ('openvimd version {} {}'.format(ovim.get_version(), ovim.get_version_date()))
1381 print ('(c) Copyright Telefonica')
tierno46ca3a92017-04-05 19:49:24 +02001382 elif args.database_version:
1383 print ('required database version: {}'.format(ovim.get_database_version()))
mirabalb716ac52017-02-10 14:47:53 +01001384