get_of_rules corrected
[osm/openvim.git] / ovim.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openvim
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 '''
25 This is the thread for the http server North API.
26 Two thread will be launched, with normal and administrative permissions.
27 '''
28
29 __author__ = "Alfonso Tierno, Leonardo Mirabal"
30 __date__ = "$06-Feb-2017 12:07:15$"
31
32 import threading
33 import vim_db
34 import logging
35 import threading
36 import imp
37 import host_thread as ht
38 import dhcp_thread as dt
39 import openflow_thread as oft
40 from netaddr import IPNetwork
41 from jsonschema import validate as js_v, exceptions as js_e
42
43 HTTP_Bad_Request = 400
44 HTTP_Unauthorized = 401
45 HTTP_Not_Found = 404
46 HTTP_Forbidden = 403
47 HTTP_Method_Not_Allowed = 405
48 HTTP_Not_Acceptable = 406
49 HTTP_Request_Timeout = 408
50 HTTP_Conflict = 409
51 HTTP_Service_Unavailable = 503
52 HTTP_Internal_Server_Error= 500
53
54
55 def convert_boolean(data, items):
56 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
57 It assumes that bandwidth is well formed
58 Attributes:
59 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
60 'items': tuple of keys to convert
61 Return:
62 None
63 '''
64 if type(data) is dict:
65 for k in data.keys():
66 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
67 convert_boolean(data[k], items)
68 if k in items:
69 if type(data[k]) is str:
70 if data[k] == "false":
71 data[k] = False
72 elif data[k] == "true":
73 data[k] = True
74 if type(data) is tuple or type(data) is list:
75 for k in data:
76 if type(k) is dict or type(k) is tuple or type(k) is list:
77 convert_boolean(k, items)
78
79
80
81 class ovimException(Exception):
82 def __init__(self, message, http_code=HTTP_Bad_Request):
83 self.http_code = http_code
84 Exception.__init__(self, message)
85
86
87 class ovim():
88 running_info = {} #TODO OVIM move the info of running threads from config_dic to this static variable
89 def __init__(self, configuration):
90 self.config = configuration
91 self.logger = logging.getLogger(configuration["logger_name"])
92 self.db = None
93 self.db = self._create_database_connection()
94
95 def _create_database_connection(self):
96 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
97 self.config['log_level_db']);
98 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
99 self.config['db_name']) == -1:
100 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
101 # self.config['db_host'])
102 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
103 self.config['db_user'],
104 self.config['db_host']) )
105 return db
106
107 @staticmethod
108 def _check_dhcp_data_integrity(network):
109 """
110 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
111 :param network: list with user nets paramters
112 :return:
113 """
114 if "cidr" in network:
115 cidr = network["cidr"]
116 ip_tools = IPNetwork(cidr)
117 cidr_len = ip_tools.prefixlen
118 if cidr_len > 29:
119 return False
120
121 ips = IPNetwork(cidr)
122 if "dhcp_first_ip" not in network:
123 network["dhcp_first_ip"] = str(ips[2])
124 if "dhcp_last_ip" not in network:
125 network["dhcp_last_ip"] = str(ips[-2])
126 if "gateway_ip" not in network:
127 network["gateway_ip"] = str(ips[1])
128
129 return True
130 else:
131 return False
132
133 @staticmethod
134 def _check_valid_uuid(uuid):
135 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
136 try:
137 js_v(uuid, id_schema)
138 return True
139 except js_e.ValidationError:
140 return False
141
142 def start_service(self):
143 #if self.running_info:
144 # return #TODO service can be checked and rebuild broken threads
145 r = self.db.get_db_version()
146 if r[0]<0:
147 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
148 "'./database_utils/migrate_vim_db.sh'".format(self.config["database_version"]) )
149 elif r[1]!=self.config["database_version"]:
150 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
151 "'./database_utils/migrate_vim_db.sh'".format(r[1], self.config["database_version"]) )
152
153 # create database connection for openflow threads
154 db_of = self._create_database_connection()
155 self.config["db"] = db_of
156 db_lock = threading.Lock()
157 self.config["db_lock"] = db_lock
158
159 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge, speed in Gbit/s
160 self.config['dhcp_nets'] = []
161 self.config['bridge_nets'] = []
162 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
163 # skip 'development_bridge'
164 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
165 continue
166 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
167
168 # check if this bridge is already used (present at database) for a network)
169 used_bridge_nets = []
170 for brnet in self.config['bridge_nets']:
171 r, nets = db_of.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
172 if r > 0:
173 brnet[3] = nets[0]['uuid']
174 used_bridge_nets.append(brnet[0])
175 if self.config.get("dhcp_server"):
176 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
177 self.config['dhcp_nets'].append(nets[0]['uuid'])
178 if len(used_bridge_nets) > 0:
179 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
180 # get nets used by dhcp
181 if self.config.get("dhcp_server"):
182 for net in self.config["dhcp_server"].get("nets", ()):
183 r, nets = db_of.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
184 if r > 0:
185 self.config['dhcp_nets'].append(nets[0]['uuid'])
186
187 # get host list from data base before starting threads
188 r, hosts = db_of.get_table(SELECT=('name', 'ip_name', 'user', 'uuid'), FROM='hosts', WHERE={'status': 'ok'})
189 if r < 0:
190 raise ovimException("Cannot get hosts from database {}".format(hosts))
191 # create connector to the openflow controller
192 of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
193
194 if of_test_mode:
195 OF_conn = oft.of_test_connector({"of_debug": self.config['log_level_of']})
196 else:
197 # load other parameters starting by of_ from config dict in a temporal dict
198 temp_dict = {"of_ip": self.config['of_controller_ip'],
199 "of_port": self.config['of_controller_port'],
200 "of_dpid": self.config['of_controller_dpid'],
201 "of_debug": self.config['log_level_of']
202 }
203 for k, v in self.config.iteritems():
204 if type(k) is str and k[0:3] == "of_" and k[0:13] != "of_controller":
205 temp_dict[k] = v
206 if self.config['of_controller'] == 'opendaylight':
207 module = "ODL"
208 elif "of_controller_module" in self.config:
209 module = self.config["of_controller_module"]
210 else:
211 module = self.config['of_controller']
212 module_info = None
213 try:
214 module_info = imp.find_module(module)
215
216 OF_conn = imp.load_module("OF_conn", *module_info)
217 try:
218 OF_conn = OF_conn.OF_conn(temp_dict)
219 except Exception as e:
220 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
221 if module_info and module_info[0]:
222 file.close(module_info[0])
223 exit(-1)
224 except (IOError, ImportError) as e:
225 if module_info and module_info[0]:
226 file.close(module_info[0])
227 self.logger.error(
228 "Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' field of configuration file.",
229 module, type(e).__name__, str(e))
230 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' field of configuration file.".fromat(
231 module, type(e).__name__, str(e)))
232
233
234 # create openflow thread
235 thread = oft.openflow_thread(OF_conn, of_test=of_test_mode, db=db_of, db_lock=db_lock,
236 pmp_with_same_vlan=self.config['of_controller_nets_with_same_vlan'],
237 debug=self.config['log_level_of'])
238 r, c = thread.OF_connector.obtain_port_correspondence()
239 if r < 0:
240 raise ovimException("Cannot get openflow information %s", c)
241 thread.start()
242 self.config['of_thread'] = thread
243
244 # create dhcp_server thread
245 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
246 dhcp_params = self.config.get("dhcp_server")
247 if dhcp_params:
248 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
249 db=db_of, db_lock=db_lock, debug=self.config['log_level_of'])
250 thread.start()
251 self.config['dhcp_thread'] = thread
252
253 # Create one thread for each host
254 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
255 host_develop_mode = True if self.config['mode'] == 'development' else False
256 host_develop_bridge_iface = self.config.get('development_bridge', None)
257 self.config['host_threads'] = {}
258 for host in hosts:
259 host['image_path'] = '/opt/VNF/images/openvim'
260 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=db_of, db_lock=db_lock,
261 test=host_test_mode, image_path=self.config['image_path'], version=self.config['version'],
262 host_id=host['uuid'], develop_mode=host_develop_mode,
263 develop_bridge_iface=host_develop_bridge_iface)
264 thread.start()
265 self.config['host_threads'][host['uuid']] = thread
266
267 # create ovs dhcp thread
268 result, content = self.db.get_table(FROM='nets')
269 if result < 0:
270 self.logger.error("http_get_ports Error %d %s", result, content)
271 raise ovimException(str(content), -result)
272
273 for net in content:
274 net_type = net['type']
275 if (net_type == 'bridge_data' or net_type == 'bridge_man') \
276 and net["provider"][:4] == 'OVS:' and net["enable_dhcp"] == "true":
277 self.launch_dhcp_server(net['vlan'],
278 net['dhcp_first_ip'],
279 net['dhcp_last_ip'],
280 net['cidr'],
281 net['gateway_ip'])
282
283 def stop_service(self):
284 threads = self.config.get('host_threads', {})
285 if 'of_thread' in self.config:
286 threads['of'] = (self.config['of_thread'])
287 if 'dhcp_thread' in self.config:
288 threads['dhcp'] = (self.config['dhcp_thread'])
289
290 for thread in threads.values():
291 thread.insert_task("exit")
292 for thread in threads.values():
293 thread.join()
294
295 def get_networks(self, columns=None, filter={}, limit=None):
296 """
297 Retreive networks available
298 :param columns: List with select query parameters
299 :param filter: List with where query parameters
300 :param limit: Query limit result
301 :return:
302 """
303 result, content = self.db.get_table(SELECT=columns, FROM='nets', WHERE=filter, LIMIT=limit)
304
305 if result < 0:
306 raise ovimException(str(content), -result)
307
308 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
309
310 return content
311
312 def show_network(self, network_id, filter={}):
313 """
314 Get network from DB by id
315 :param network_id: net Id
316 :param filter:
317 :param limit:
318 :return:
319 """
320 # obtain data
321 if not network_id:
322 raise ovimException("Not network id was not found")
323 filter['uuid'] = network_id
324
325 result, content = self.db.get_table(FROM='nets', WHERE=filter, LIMIT=100)
326
327 if result < 0:
328 raise ovimException(str(content), -result)
329 elif result == 0:
330 raise ovimException("show_network network '%s' not found" % network_id, -result)
331 else:
332 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
333 # get ports from DB
334 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
335 WHERE={'net_id': network_id}, LIMIT=100)
336 if len(ports) > 0:
337 content[0]['ports'] = ports
338
339 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
340 return content[0]
341
342 def new_network(self, network):
343 """
344 Create a net in DB
345 :return:
346 """
347 tenant_id = network.get('tenant_id')
348
349 if tenant_id:
350 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
351 if result <= 0:
352 raise ovimException("set_network error, no tenant founded", -result)
353
354 bridge_net = None
355 # check valid params
356 net_provider = network.get('provider')
357 net_type = network.get('type')
358 net_vlan = network.get("vlan")
359 net_bind_net = network.get("bind_net")
360 net_bind_type = network.get("bind_type")
361 name = network["name"]
362
363 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
364 vlan_index = name.rfind(":")
365 if not net_bind_net and not net_bind_type and vlan_index > 1:
366 try:
367 vlan_tag = int(name[vlan_index + 1:])
368 if not vlan_tag and vlan_tag < 4096:
369 net_bind_net = name[:vlan_index]
370 net_bind_type = "vlan:" + name[vlan_index + 1:]
371 except:
372 pass
373
374 if net_bind_net:
375 # look for a valid net
376 if self._check_valid_uuid(net_bind_net):
377 net_bind_key = "uuid"
378 else:
379 net_bind_key = "name"
380 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
381 if result < 0:
382 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
383 elif result == 0:
384 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
385 elif result > 1:
386 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
387 network["bind_net"] = content[0]["uuid"]
388
389 if net_bind_type:
390 if net_bind_type[0:5] != "vlan:":
391 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
392 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
393 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
394 HTTP_Bad_Request)
395 network["bind_type"] = net_bind_type
396
397 if net_provider:
398 if net_provider[:9] == "openflow:":
399 if net_type:
400 if net_type != "ptp" and net_type != "data":
401 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
402 HTTP_Bad_Request)
403 else:
404 net_type = 'data'
405 else:
406 if net_type:
407 if net_type != "bridge_man" and net_type != "bridge_data":
408 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
409 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
410 else:
411 net_type = 'bridge_man'
412
413 if not net_type:
414 net_type = 'bridge_man'
415
416 if net_provider:
417 if net_provider[:7] == 'bridge:':
418 # check it is one of the pre-provisioned bridges
419 bridge_net_name = net_provider[7:]
420 for brnet in self.config['bridge_nets']:
421 if brnet[0] == bridge_net_name: # free
422 if not brnet[3]:
423 raise ovimException("invalid 'provider:physical', "
424 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
425 bridge_net = brnet
426 net_vlan = brnet[1]
427 break
428 # if bridge_net==None:
429 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
430 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
431 # return
432
433 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
434 # look for a free precreated nets
435 for brnet in self.config['bridge_nets']:
436 if not brnet[3]: # free
437 if not bridge_net:
438 if net_type == 'bridge_man': # look for the smaller speed
439 if brnet[2] < bridge_net[2]:
440 bridge_net = brnet
441 else: # look for the larger speed
442 if brnet[2] > bridge_net[2]:
443 bridge_net = brnet
444 else:
445 bridge_net = brnet
446 net_vlan = brnet[1]
447 if not bridge_net:
448 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
449 "will overcome this limit", HTTP_Bad_Request)
450 else:
451 self.logger.debug("using net " + bridge_net)
452 net_provider = "bridge:" + bridge_net[0]
453 net_vlan = bridge_net[1]
454 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
455 net_provider = 'OVS'
456 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
457 net_vlan = self.db.get_free_net_vlan()
458 if net_vlan < 0:
459 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
460 if net_provider == 'OVS':
461 net_provider = 'OVS' + ":" + str(net_vlan)
462
463 network['provider'] = net_provider
464 network['type'] = net_type
465 network['vlan'] = net_vlan
466 dhcp_integrity = True
467 if 'enable_dhcp' in network and network['enable_dhcp']:
468 dhcp_integrity = self._check_dhcp_data_integrity(network)
469
470 result, content = self.db.new_row('nets', network, True, True)
471
472 if result >= 0 and dhcp_integrity:
473 if bridge_net:
474 bridge_net[3] = content
475 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
476 if network["name"] in self.config["dhcp_server"].get("nets", ()):
477 self.config["dhcp_nets"].append(content)
478 self.logger.debug("dhcp_server: add new net", content)
479 elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
480 self.config["dhcp_nets"].append(content)
481 self.logger.debug("dhcp_server: add new net", content, content)
482 return content
483 else:
484 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
485 # TODO kei change update->edit
486
487 def edit_network(self, network_id, network):
488 """
489 Update entwork data byt id
490 :return:
491 """
492 # Look for the previous data
493 where_ = {'uuid': network_id}
494 result, network_old = self.db.get_table(FROM='nets', WHERE=where_)
495 if result < 0:
496 raise ovimException("Error updating network %s" % network_old, HTTP_Internal_Server_Error)
497 elif result == 0:
498 raise ovimException('network %s not found' % network_id, HTTP_Not_Found)
499 # get ports
500 nbports, content = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
501 WHERE={'net_id': network_id}, LIMIT=100)
502 if result < 0:
503 raise ovimException("http_put_network_id error %d %s" % (result, network_old), HTTP_Internal_Server_Error)
504 if nbports > 0:
505 if 'type' in network and network['type'] != network_old[0]['type']:
506 raise ovimException("Can not change type of network while having ports attached",
507 HTTP_Method_Not_Allowed)
508 if 'vlan' in network and network['vlan'] != network_old[0]['vlan']:
509 raise ovimException("Can not change vlan of network while having ports attached",
510 HTTP_Method_Not_Allowed)
511
512 # check valid params
513 net_provider = network.get('provider', network_old[0]['provider'])
514 net_type = network.get('type', network_old[0]['type'])
515 net_bind_net = network.get("bind_net")
516 net_bind_type = network.get("bind_type")
517 if net_bind_net:
518 # look for a valid net
519 if self._check_valid_uuid(net_bind_net):
520 net_bind_key = "uuid"
521 else:
522 net_bind_key = "name"
523 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
524 if result < 0:
525 raise ovimException('Getting nets from db ' + content, HTTP_Internal_Server_Error)
526 elif result == 0:
527 raise ovimException("bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
528 elif result > 1:
529 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net),
530 HTTP_Bad_Request)
531 network["bind_net"] = content[0]["uuid"]
532 if net_bind_type:
533 if net_bind_type[0:5] != "vlan:":
534 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
535 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
536 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
537 HTTP_Bad_Request)
538 if net_provider:
539 if net_provider[:9] == "openflow:":
540 if net_type != "ptp" and net_type != "data":
541 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request)
542 else:
543 if net_type != "bridge_man" and net_type != "bridge_data":
544 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
545 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
546
547 # insert in data base
548 result, content = self.db.update_rows('nets', network, WHERE={'uuid': network_id}, log=True)
549 if result >= 0:
550 # if result > 0 and nbports>0 and 'admin_state_up' in network
551 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
552 if result > 0:
553 r, c = self.config['of_thread'].insert_task("update-net", network_id)
554 if r < 0:
555 raise ovimException("Error while launching openflow rules %s" % c, HTTP_Internal_Server_Error)
556 if self.config.get("dhcp_server"):
557 if network_id in self.config["dhcp_nets"]:
558 self.config["dhcp_nets"].remove(network_id)
559 if network.get("name", network_old["name"]) in self.config["dhcp_server"].get("nets", ()):
560 self.config["dhcp_nets"].append(network_id)
561 else:
562 net_bind = network.get("bind", network_old["bind"])
563 if net_bind and net_bind[:7] == "bridge:" and net_bind[7:] in self.config["dhcp_server"].get(
564 "bridge_ifaces", ()):
565 self.config["dhcp_nets"].append(network_id)
566 return network_id
567 else:
568 raise ovimException(content, -result)
569
570 def delete_network(self, network_id):
571
572 # delete from the data base
573 result, content = self.db.delete_row('nets', network_id)
574
575 if result == 0:
576 raise ovimException("Network %s not found " % network_id, HTTP_Not_Found)
577 elif result > 0:
578 for brnet in self.config['bridge_nets']:
579 if brnet[3] == network_id:
580 brnet[3] = None
581 break
582 if self.config.get("dhcp_server") and network_id in self.config["dhcp_nets"]:
583 self.config["dhcp_nets"].remove(network_id)
584 return content
585 else:
586 raise ovimException("Error deleting network %s" % network_id, HTTP_Internal_Server_Error)
587
588 def get_openflow_rules(self, network_id=None):
589 """
590 Get openflow id from DB
591 :param network_id: Network id, if none all networks will be retrieved
592 :return: Return a list with Openflow rules per net
593 """
594 # ignore input data
595 if not network_id:
596 where_ = {}
597 else:
598 where_ = {"net_id": network_id}
599
600 result, content = self.db.get_table(
601 SELECT=("name", "net_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
602 WHERE=where_, FROM='of_flows')
603
604 if result < 0:
605 raise ovimException(str(content), -result)
606 return content
607
608 def edit_openflow_rules(self, network_id=None):
609
610 """
611 To make actions over the net. The action is to reinstall the openflow rules
612 network_id can be 'all'
613 :param network_id: Network id, if none all networks will be retrieved
614 :return : Number of nets updated
615 """
616
617 # ignore input data
618 if not network_id:
619 where_ = {}
620 else:
621 where_ = {"uuid": network_id}
622 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
623
624 if result < 0:
625 raise ovimException(str(content), -result)
626
627 for net in content:
628 if net["type"] != "ptp" and net["type"] != "data":
629 result -= 1
630 continue
631 r, c = self.config['of_thread'].insert_task("update-net", net['uuid'])
632 if r < 0:
633 raise ovimException(str(c), -r)
634 return result
635
636 def delete_openflow_rules(self):
637 """
638 To make actions over the net. The action is to delete ALL openflow rules
639 :return: return operation result
640 """
641 # ignore input data
642 r, c = self.config['of_thread'].insert_task("clear-all")
643 if r < 0:
644 raise ovimException(str(c), -r)
645 return r
646
647 def get_openflow_ports(self):
648 """
649 Obtain switch ports names of openflow controller
650 :return: Return flow ports in DB
651 """
652 data = {'ports': self.config['of_thread'].OF_connector.pp2ofi}
653 return data
654
655 def get_ports(self, columns=None, filter={}, limit=None):
656 # result, content = my.db.get_ports(where_)
657 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
658 if result < 0:
659 self.logger.error("http_get_ports Error %d %s", result, content)
660 raise ovimException(str(content), -result)
661 else:
662 convert_boolean(content, ('admin_state_up',))
663 return content
664
665 def new_port(self, port_data):
666 port_data['type'] = 'external'
667 if port_data.get('net_id'):
668 # check that new net has the correct type
669 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
670 if result < 0:
671 raise ovimException(str(new_net), -result)
672 # insert in data base
673 result, uuid = self.db.new_row('ports', port_data, True, True)
674 if result > 0:
675 if 'net_id' in port_data:
676 r, c = self.config['of_thread'].insert_task("update-net", port_data['net_id'])
677 if r < 0:
678 self.logger.error("Cannot insert a task for updating network '$s' %s", port_data['net_id'], c)
679 #TODO put network in error status
680 return uuid
681 else:
682 raise ovimException(str(uuid), -result)
683
684 def delete_port(self, port_id):
685 # Look for the previous port data
686 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
687 if result < 0:
688 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
689 # delete from the data base
690 result, content = self.db.delete_row('ports', port_id)
691 if result == 0:
692 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
693 elif result < 0:
694 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
695 # update network
696 network = ports[0].get('net_id', None)
697 if network:
698 # change of net.
699 r, c = self.config['of_thread'].insert_task("update-net", network)
700 if r < 0:
701 self.logger.error("Cannot insert a task for updating network '$s' %s", network, c)
702 return content
703
704 def edit_port(self, port_id, port_data, admin=True):
705 # Look for the previous port data
706 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
707 if result < 0:
708 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
709 elif result == 0:
710 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
711 port = content[0]
712 nets = []
713 host_id = None
714 result = 1
715 if 'net_id' in port_data:
716 # change of net.
717 old_net = port.get('net_id', None)
718 new_net = port_data['net_id']
719 if old_net != new_net:
720
721 if new_net:
722 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
723 if old_net:
724 nets.append(old_net)
725 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
726 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
727 elif port['type'] == 'external' and not admin:
728 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
729 if new_net:
730 # check that new net has the correct type
731 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
732 if result < 0:
733 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
734 # change VLAN for SR-IOV ports
735 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
736 if new_net:
737 port_data["vlan"] = None
738 else:
739 port_data["vlan"] = new_net_dict["vlan"]
740 # get host where this VM is allocated
741 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
742 if result > 0:
743 host_id = content[0]["host_id"]
744
745 # insert in data base
746 if result >= 0:
747 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
748
749 # Insert task to complete actions
750 if result > 0:
751 for net_id in nets:
752 r, v = self.config['of_thread'].insert_task("update-net", net_id)
753 if r < 0:
754 self.logger.error("Error updating network '{}' {}".format(r,v))
755 # TODO Do something if fails
756 if host_id:
757 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
758 if r < 0:
759 self.logger.error("Error updating network '{}' {}".format(r,v))
760 # TODO Do something if fails
761 if result >= 0:
762 return port_id
763 else:
764 raise ovimException("Error {}".format(content), http_code=-result)
765
766 def get_dhcp_controller(self):
767 """
768 Create an host_thread object for manage openvim controller and not create a thread for itself
769 :return: dhcp_host openvim controller object
770 """
771
772 if 'openvim_controller' in self.config['host_threads']:
773 return self.config['host_threads']['openvim_controller']
774
775 bridge_ifaces = []
776 controller_ip = self.config['ovs_controller_ip']
777 ovs_controller_user = self.config['ovs_controller_user']
778
779 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
780 host_develop_mode = True if self.config['mode'] == 'development' else False
781
782 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
783 db=self.config['db'],
784 db_lock=self.config['db_lock'], test=host_test_mode,
785 image_path=self.config['image_path'], version=self.config['version'],
786 host_id='openvim_controller', develop_mode=host_develop_mode,
787 develop_bridge_iface=bridge_ifaces)
788
789 self.config['host_threads']['openvim_controller'] = dhcp_host
790 if not host_test_mode:
791 dhcp_host.ssh_connect()
792 return dhcp_host
793
794 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
795 """
796 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
797 :param vlan: vlan identifier
798 :param first_ip: First dhcp range ip
799 :param last_ip: Last dhcp range ip
800 :param cidr: net cidr
801 :param gateway: net gateway
802 :return:
803 """
804 ip_tools = IPNetwork(cidr)
805 dhcp_netmask = str(ip_tools.netmask)
806 ip_range = [first_ip, last_ip]
807
808 dhcp_path = self.config['ovs_controller_file_path']
809
810 controller_host = self.get_dhcp_controller()
811 controller_host.create_linux_bridge(vlan)
812 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
813 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
814
815