Move openflow logic to ovim.py from httpserver.py
[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
96 def _create_database_connection(self):
97 db = vim_db.vim_db((self.config["network_vlan_range_start"], self.config["network_vlan_range_end"]),
98 self.config['log_level_db']);
99 if db.connect(self.config['db_host'], self.config['db_user'], self.config['db_passwd'],
100 self.config['db_name']) == -1:
101 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
102 # self.config['db_host'])
103 raise ovimException("Cannot connect to database {} at {}@{}".format(self.config['db_name'],
104 self.config['db_user'],
105 self.config['db_host']) )
106 return db
107
108
109 def start_service(self):
110 #if self.running_info:
111 # return #TODO service can be checked and rebuild broken threads
112 r = self.db.get_db_version()
113 if r[0]<0:
114 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
115 "'./database_utils/migrate_vim_db.sh'".format(self.config["database_version"]) )
116 elif r[1]!=self.config["database_version"]:
117 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
118 "'./database_utils/migrate_vim_db.sh'".format(r[1], self.config["database_version"]) )
119
120 # create database connection for openflow threads
121 db_of = self._create_database_connection()
122 self.config["db"] = db_of
123 db_lock = threading.Lock()
124 self.config["db_lock"] = db_lock
125
126 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge, speed in Gbit/s
127 self.config['dhcp_nets'] = []
128 self.config['bridge_nets'] = []
129 for bridge, vlan_speed in self.config["bridge_ifaces"].items():
130 # skip 'development_bridge'
131 if self.config['mode'] == 'development' and self.config['development_bridge'] == bridge:
132 continue
133 self.config['bridge_nets'].append([bridge, vlan_speed[0], vlan_speed[1], None])
134
135 # check if this bridge is already used (present at database) for a network)
136 used_bridge_nets = []
137 for brnet in self.config['bridge_nets']:
138 r, nets = db_of.get_table(SELECT=('uuid',), FROM='nets', WHERE={'provider': "bridge:" + brnet[0]})
139 if r > 0:
140 brnet[3] = nets[0]['uuid']
141 used_bridge_nets.append(brnet[0])
142 if self.config.get("dhcp_server"):
143 if brnet[0] in self.config["dhcp_server"]["bridge_ifaces"]:
144 self.config['dhcp_nets'].append(nets[0]['uuid'])
145 if len(used_bridge_nets) > 0:
146 self.logger.info("found used bridge nets: " + ",".join(used_bridge_nets))
147 # get nets used by dhcp
148 if self.config.get("dhcp_server"):
149 for net in self.config["dhcp_server"].get("nets", ()):
150 r, nets = db_of.get_table(SELECT=('uuid',), FROM='nets', WHERE={'name': net})
151 if r > 0:
152 self.config['dhcp_nets'].append(nets[0]['uuid'])
153
154 # get host list from data base before starting threads
155 r, hosts = db_of.get_table(SELECT=('name', 'ip_name', 'user', 'uuid'), FROM='hosts', WHERE={'status': 'ok'})
156 if r < 0:
157 raise ovimException("Cannot get hosts from database {}".format(hosts))
158 # create connector to the openflow controller
159 of_test_mode = False if self.config['mode'] == 'normal' or self.config['mode'] == "OF only" else True
160
161 if of_test_mode:
162 OF_conn = oft.of_test_connector({"of_debug": self.config['log_level_of']})
163 else:
164 # load other parameters starting by of_ from config dict in a temporal dict
165 temp_dict = {"of_ip": self.config['of_controller_ip'],
166 "of_port": self.config['of_controller_port'],
167 "of_dpid": self.config['of_controller_dpid'],
168 "of_debug": self.config['log_level_of']
169 }
170 for k, v in self.config.iteritems():
171 if type(k) is str and k[0:3] == "of_" and k[0:13] != "of_controller":
172 temp_dict[k] = v
173 if self.config['of_controller'] == 'opendaylight':
174 module = "ODL"
175 elif "of_controller_module" in self.config:
176 module = self.config["of_controller_module"]
177 else:
178 module = self.config['of_controller']
179 module_info = None
180 try:
181 module_info = imp.find_module(module)
182
183 OF_conn = imp.load_module("OF_conn", *module_info)
184 try:
185 OF_conn = OF_conn.OF_conn(temp_dict)
186 except Exception as e:
187 self.logger.error("Cannot open the Openflow controller '%s': %s", type(e).__name__, str(e))
188 if module_info and module_info[0]:
189 file.close(module_info[0])
190 exit(-1)
191 except (IOError, ImportError) as e:
192 if module_info and module_info[0]:
193 file.close(module_info[0])
194 self.logger.error(
195 "Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' field of configuration file.",
196 module, type(e).__name__, str(e))
197 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' field of configuration file.".fromat(
198 module, type(e).__name__, str(e)))
199
200
201 # create openflow thread
202 thread = oft.openflow_thread(OF_conn, of_test=of_test_mode, db=db_of, db_lock=db_lock,
203 pmp_with_same_vlan=self.config['of_controller_nets_with_same_vlan'],
204 debug=self.config['log_level_of'])
205 r, c = thread.OF_connector.obtain_port_correspondence()
206 if r < 0:
207 raise ovimException("Cannot get openflow information %s", c)
208 thread.start()
209 self.config['of_thread'] = thread
210
211 # create dhcp_server thread
212 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
213 dhcp_params = self.config.get("dhcp_server")
214 if dhcp_params:
215 thread = dt.dhcp_thread(dhcp_params=dhcp_params, test=host_test_mode, dhcp_nets=self.config["dhcp_nets"],
216 db=db_of, db_lock=db_lock, debug=self.config['log_level_of'])
217 thread.start()
218 self.config['dhcp_thread'] = thread
219
220 # Create one thread for each host
221 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
222 host_develop_mode = True if self.config['mode'] == 'development' else False
223 host_develop_bridge_iface = self.config.get('development_bridge', None)
224 self.config['host_threads'] = {}
225 for host in hosts:
226 host['image_path'] = '/opt/VNF/images/openvim'
227 thread = ht.host_thread(name=host['name'], user=host['user'], host=host['ip_name'], db=db_of, db_lock=db_lock,
228 test=host_test_mode, image_path=self.config['image_path'], version=self.config['version'],
229 host_id=host['uuid'], develop_mode=host_develop_mode,
230 develop_bridge_iface=host_develop_bridge_iface)
231 thread.start()
232 self.config['host_threads'][host['uuid']] = thread
233
234 # create ovs dhcp thread
235 result, content = self.db.get_table(FROM='nets')
236 if result < 0:
237 self.logger.error("http_get_ports Error %d %s", result, content)
238 raise ovimException(str(content), -result)
239
240 for net in content:
241 net_type = net['type']
242 if net_type == 'bridge_data' or net_type == 'bridge_man' \
243 and net["provider"][:4] =="OVS:" and net["enable_dhcp"] == "true":
244 self.launch_dhcp_server(net['vlan'],
245 net['dhcp_first_ip'],
246 net['dhcp_last_ip'],
247 net['cidr'],
248 net['gateway_ip'])
249
250 def stop_service(self):
251 threads = self.config.get('host_threads', {})
252 if 'of_thread' in self.config:
253 threads['of'] = (self.config['of_thread'])
254 if 'dhcp_thread' in self.config:
255 threads['dhcp'] = (self.config['dhcp_thread'])
256
257 for thread in threads.values():
258 thread.insert_task("exit")
259 for thread in threads.values():
260 thread.join()
261
262 def get_networks(self, select_=None, where_=None, limit_=100):
263 """
264 Retreive networks available
265 :param select_: List with select query parameters
266 :param where_: List with where query parameters
267 :param limit_: Query limit result
268 :return:
269 """
270 result, content = self.db.get_table(SELECT=select_, FROM='nets', WHERE=where_, LIMIT=limit_)
271
272 if result < 0:
273 raise ovimException(str(content), -result)
274
275 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
276
277 return content
278
279 def get_network_by_id(self, where_, limit_=100):
280 """
281
282 :param where_:
283 :param limit_:
284 :return:
285 """
286 # obtain data
287 if 'uuid' not in where_:
288 raise ovimException("Not network id was not found" )
289 result, content = self.db.get_table(FROM='nets', WHERE=where_, LIMIT=limit_)
290
291 network_id = where_['uuid']
292
293 if result < 0:
294 raise ovimException(str(content), -result)
295 elif result == 0:
296 raise ovimException("http_get_networks_id network '%s' not found" % network_id, -result)
297 else:
298 convert_boolean(content, ('shared', 'admin_state_up', 'enable_dhcp'))
299 # get ports
300 result, ports = self.db.get_table(FROM='ports', SELECT=('uuid as port_id',),
301 WHERE={'net_id': network_id}, LIMIT=100)
302 if len(ports) > 0:
303 content[0]['ports'] = ports
304 return content
305
306 def set_network(self, network):
307 """
308
309 :return:
310 """
311 tenant_id = network.get('tenant_id')
312
313 if tenant_id:
314 result, _ = self.db.get_table(FROM='tenants', SELECT=('uuid',), WHERE={'uuid': tenant_id, "enabled": True})
315 if result <= 0:
316 raise ovimException("set_network error, no tenant founded", -result)
317
318 bridge_net = None
319 # check valid params
320 net_provider = network.get('provider')
321 net_type = network.get('type')
322 net_enable_dhcp = network.get('enable_dhcp')
323 if net_enable_dhcp:
324 net_cidr = network.get('cidr')
325
326 net_vlan = network.get("vlan")
327 net_bind_net = network.get("bind_net")
328 net_bind_type = network.get("bind_type")
329 name = network["name"]
330
331 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
332 vlan_index = name.rfind(":")
333 if not net_bind_net and not net_bind_type and vlan_index > 1:
334 try:
335 vlan_tag = int(name[vlan_index + 1:])
336 if not vlan_tag and vlan_tag < 4096:
337 net_bind_net = name[:vlan_index]
338 net_bind_type = "vlan:" + name[vlan_index + 1:]
339 except:
340 pass
341
342 if net_bind_net:
343 # look for a valid net
344 if self._check_valid_uuid(net_bind_net):
345 net_bind_key = "uuid"
346 else:
347 net_bind_key = "name"
348 result, content = self.db.get_table(FROM='nets', WHERE={net_bind_key: net_bind_net})
349 if result < 0:
350 raise ovimException(' getting nets from db ' + content, HTTP_Internal_Server_Error)
351 elif result == 0:
352 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
353 elif result > 1:
354 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key, net_bind_net), HTTP_Bad_Request)
355 network["bind_net"] = content[0]["uuid"]
356
357 if net_bind_type:
358 if net_bind_type[0:5] != "vlan:":
359 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request)
360 if int(net_bind_type[5:]) > 4095 or int(net_bind_type[5:]) <= 0:
361 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
362 HTTP_Bad_Request)
363 network["bind_type"] = net_bind_type
364
365 if net_provider:
366 if net_provider[:9] == "openflow:":
367 if net_type:
368 if net_type != "ptp" and net_type != "data":
369 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
370 HTTP_Bad_Request)
371 else:
372 net_type = 'data'
373 else:
374 if net_type:
375 if net_type != "bridge_man" and net_type != "bridge_data":
376 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
377 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request)
378 else:
379 net_type = 'bridge_man'
380
381 if not net_type:
382 net_type = 'bridge_man'
383
384 if net_provider:
385 if net_provider[:7] == 'bridge:':
386 # check it is one of the pre-provisioned bridges
387 bridge_net_name = net_provider[7:]
388 for brnet in self.config['bridge_nets']:
389 if brnet[0] == bridge_net_name: # free
390 if not brnet[3]:
391 raise ovimException("invalid 'provider:physical', "
392 "bridge '%s' is already used" % bridge_net_name, HTTP_Conflict)
393 bridge_net = brnet
394 net_vlan = brnet[1]
395 break
396 # if bridge_net==None:
397 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
398 # return
399 elif self.config['network_type'] == 'bridge' and (net_type == 'bridge_data' or net_type == 'bridge_man'):
400 # look for a free precreated nets
401 for brnet in self.config['bridge_nets']:
402 if not brnet[3]: # free
403 if not bridge_net:
404 if net_type == 'bridge_man': # look for the smaller speed
405 if brnet[2] < bridge_net[2]:
406 bridge_net = brnet
407 else: # look for the larger speed
408 if brnet[2] > bridge_net[2]:
409 bridge_net = brnet
410 else:
411 bridge_net = brnet
412 net_vlan = brnet[1]
413 if not bridge_net:
414 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
415 "will overcome this limit", HTTP_Bad_Request)
416 else:
417 print "using net", bridge_net
418 net_provider = "bridge:" + bridge_net[0]
419 net_vlan = bridge_net[1]
420 elif net_type == 'bridge_data' or net_type == 'bridge_man' and self.config['network_type'] == 'ovs':
421 net_provider = 'OVS'
422 if not net_vlan and (net_type == "data" or net_type == "ptp" or net_provider == "OVS"):
423 net_vlan = self.db.get_free_net_vlan()
424 if net_vlan < 0:
425 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error)
426 if net_provider == 'OVS':
427 net_provider = 'OVS' + ":" + str(net_vlan)
428
429 network['provider'] = net_provider
430 network['type'] = net_type
431 network['vlan'] = net_vlan
432 dhcp_integrity = True
433 if 'enable_dhcp' in network and network['enable_dhcp']:
434 dhcp_integrity = self._check_dhcp_data_integrity(network)
435
436 result, content = self.db.new_row('nets', network, True, True)
437
438 if result >= 0 and dhcp_integrity:
439 if bridge_net:
440 bridge_net[3] = content
441 if self.config.get("dhcp_server") and self.config['network_type'] == 'bridge':
442 if network["name"] in self.config["dhcp_server"].get("nets", ()):
443 self.config["dhcp_nets"].append(content)
444 print "dhcp_server: add new net", content
445 elif not bridge_net and bridge_net[0] in self.config["dhcp_server"].get("bridge_ifaces", ()):
446 self.config["dhcp_nets"].append(content)
447 print "dhcp_server: add new net", content
448 return content
449 else:
450 print "http_post_networks error %d %s" % (result, content)
451 raise ovimException("Error posting network", HTTP_Internal_Server_Error)
452
453 @staticmethod
454 def _check_dhcp_data_integrity(network):
455 """
456 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
457 :param network: list with user nets paramters
458 :return:
459 """
460 control_iface = []
461
462 if "cidr" in network:
463 cidr = network["cidr"]
464 ip_tools = IPNetwork(cidr)
465 cidr_len = ip_tools.prefixlen
466 if cidr_len > 29:
467 return False
468
469 ips = IPNetwork(cidr)
470 if "dhcp_first_ip" not in network:
471 network["dhcp_first_ip"] = str(ips[2])
472 if "dhcp_last_ip" not in network:
473 network["dhcp_last_ip"] = str(ips[-2])
474 if "gateway_ip" not in network:
475 network["gateway_ip"] = str(ips[1])
476
477 return True
478 else:
479 return False
480
481 @staticmethod
482 def _check_valid_uuid( uuid):
483 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
484 try:
485 js_v(uuid, id_schema)
486 return True
487 except js_e.ValidationError:
488 return False
489
490 def get_openflow_rules(self, network_id=None):
491 """
492 Get openflow id from DB
493 :param network_id: Network id, if none all networks will be retrieved
494 :return: Return a list with Openflow rules per net
495 """
496 # ignore input data
497 if not network_id:
498 where_ = {}
499 else:
500 where_ = {"net_id": network_id}
501
502 result, content = self.db.get_table(
503 SELECT=("name", "net_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
504 WHERE=where_, FROM='of_flows')
505
506 if result < 0:
507 raise ovimException(str(content), -result)
508 return content
509
510 def update_openflow_rules(self, network_id=None):
511
512 """
513 To make actions over the net. The action is to reinstall the openflow rules
514 network_id can be 'all'
515 :param network_id: Network id, if none all networks will be retrieved
516 :return : Number of nets updated
517 """
518
519 # ignore input data
520 if not network_id:
521 where_ = {}
522 else:
523 where_ = {"uuid": network_id}
524 result, content = self.db.get_table(SELECT=("uuid", "type"), WHERE=where_, FROM='nets')
525
526 if result < 0:
527 raise ovimException(str(content), -result)
528
529 for net in content:
530 if net["type"] != "ptp" and net["type"] != "data":
531 result -= 1
532 continue
533 r, c = self.config['of_thread'].insert_task("update-net", net['uuid'])
534 if r < 0:
535 raise ovimException(str(c), -r)
536 return result
537
538 def clear_openflow_rules(self):
539 """
540 To make actions over the net. The action is to delete ALL openflow rules
541 :return: return operation result
542 """
543 # ignore input data
544 r, c = self.config['of_thread'].insert_task("clear-all")
545 if r < 0:
546 raise ovimException(str(c), -r)
547 return r
548
549 def get_openflow_ports(self):
550 """
551 Obtain switch ports names of openflow controller
552 :return: Return flow ports in DB
553 """
554 data = {'ports': self.config['of_thread'].OF_connector.pp2ofi}
555 return data
556
557 def get_ports(self, columns=None, filter={}, limit=None):
558 # result, content = my.db.get_ports(where_)
559 result, content = self.db.get_table(SELECT=columns, WHERE=filter, FROM='ports', LIMIT=limit)
560 if result < 0:
561 self.logger.error("http_get_ports Error %d %s", result, content)
562 raise ovimException(str(content), -result)
563 else:
564 convert_boolean(content, ('admin_state_up',))
565 return content
566
567 def new_port(self, port_data):
568 port_data['type'] = 'external'
569 if port_data.get('net_id'):
570 # check that new net has the correct type
571 result, new_net = self.db.check_target_net(port_data['net_id'], None, 'external')
572 if result < 0:
573 raise ovimException(str(new_net), -result)
574 # insert in data base
575 result, uuid = self.db.new_row('ports', port_data, True, True)
576 if result > 0:
577 if 'net_id' in port_data:
578 r, c = self.config['of_thread'].insert_task("update-net", port_data['net_id'])
579 if r < 0:
580 self.logger.error("Cannot insert a task for updating network '$s' %s", port_data['net_id'], c)
581 #TODO put network in error status
582 return uuid
583 else:
584 raise ovimException(str(uuid), -result)
585
586 def delete_port(self, port_id):
587 # Look for the previous port data
588 result, ports = self.db.get_table(WHERE={'uuid': port_id, "type": "external"}, FROM='ports')
589 if result < 0:
590 raise ovimException("Cannot get port info from database: {}".format(ports), http_code=-result)
591 # delete from the data base
592 result, content = self.db.delete_row('ports', port_id)
593 if result == 0:
594 raise ovimException("External port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
595 elif result < 0:
596 raise ovimException("Cannot delete port from database: {}".format(content), http_code=-result)
597 # update network
598 network = ports[0].get('net_id', None)
599 if network:
600 # change of net.
601 r, c = self.config['of_thread'].insert_task("update-net", network)
602 if r < 0:
603 self.logger.error("Cannot insert a task for updating network '$s' %s", network, c)
604 return content
605
606
607 def edit_port(self, port_id, port_data, admin=True):
608 # Look for the previous port data
609 result, content = self.db.get_table(FROM="ports", WHERE={'uuid': port_id})
610 if result < 0:
611 raise ovimException("Cannot get port info from database: {}".format(content), http_code=-result)
612 elif result == 0:
613 raise ovimException("Port '{}' not found".format(port_id), http_code=HTTP_Not_Found)
614 port = content[0]
615 nets = []
616 host_id = None
617 result = 1
618 if 'net_id' in port_data:
619 # change of net.
620 old_net = port.get('net_id', None)
621 new_net = port_data['net_id']
622 if old_net != new_net:
623
624 if new_net:
625 nets.append(new_net) # put first the new net, so that new openflow rules are created before removing the old ones
626 if old_net:
627 nets.append(old_net)
628 if port['type'] == 'instance:bridge' or port['type'] == 'instance:ovs':
629 raise ovimException("bridge interfaces cannot be attached to a different net", http_code=HTTP_Forbidden)
630 elif port['type'] == 'external' and not admin:
631 raise ovimException("Needed admin privileges",http_code=HTTP_Unauthorized)
632 if new_net:
633 # check that new net has the correct type
634 result, new_net_dict = self.db.check_target_net(new_net, None, port['type'])
635 if result < 0:
636 raise ovimException("Error {}".format(new_net_dict), http_code=HTTP_Conflict)
637 # change VLAN for SR-IOV ports
638 if result >= 0 and port["type"] == "instance:data" and port["model"] == "VF": # TODO consider also VFnotShared
639 if new_net:
640 port_data["vlan"] = None
641 else:
642 port_data["vlan"] = new_net_dict["vlan"]
643 # get host where this VM is allocated
644 result, content = self.db.get_table(FROM="instances", WHERE={"uuid": port["instance_id"]})
645 if result > 0:
646 host_id = content[0]["host_id"]
647
648 # insert in data base
649 if result >= 0:
650 result, content = self.db.update_rows('ports', port_data, WHERE={'uuid': port_id}, log=False)
651
652 # Insert task to complete actions
653 if result > 0:
654 for net_id in nets:
655 r, v = self.config['of_thread'].insert_task("update-net", net_id)
656 if r < 0:
657 self.logger.error("Error updating network '{}' {}".format(r,v))
658 # TODO Do something if fails
659 if host_id:
660 r, v = self.config['host_threads'][host_id].insert_task("edit-iface", port_id, old_net, new_net)
661 if r < 0:
662 self.logger.error("Error updating network '{}' {}".format(r,v))
663 # TODO Do something if fails
664 if result >= 0:
665 return port_id
666 else:
667 raise ovimException("Error {}".format(content), http_code=-result)
668
669 def get_dhcp_controller(self):
670 """
671 Create an host_thread object for manage openvim controller and not create a thread for itself
672 :return: dhcp_host openvim controller object
673 """
674
675 if 'openvim_controller' in self.config['host_threads']:
676 return self.config['host_threads']['openvim_controller']
677
678 bridge_ifaces = []
679 controller_ip = self.config['ovs_controller_ip']
680 ovs_controller_user = self.config['ovs_controller_user']
681
682 host_test_mode = True if self.config['mode'] == 'test' or self.config['mode'] == "OF only" else False
683 host_develop_mode = True if self.config['mode'] == 'development' else False
684
685 dhcp_host = ht.host_thread(name='openvim_controller', user=ovs_controller_user, host=controller_ip,
686 db=self.config['db'],
687 db_lock=self.config['db_lock'], test=host_test_mode,
688 image_path=self.config['image_path'], version=self.config['version'],
689 host_id='openvim_controller', develop_mode=host_develop_mode,
690 develop_bridge_iface=bridge_ifaces)
691
692 self.config['host_threads']['openvim_controller'] = dhcp_host
693 if not host_test_mode:
694 dhcp_host.ssh_connect()
695 return dhcp_host
696
697 def launch_dhcp_server(self, vlan, first_ip, last_ip, cidr, gateway):
698 """
699 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
700 :param vlan: vlan identifier
701 :param first_ip: First dhcp range ip
702 :param last_ip: Last dhcp range ip
703 :param cidr: net cidr
704 :return:
705 """
706 ip_tools = IPNetwork(cidr)
707 dhcp_netmask = str(ip_tools.netmask)
708 ip_range = [first_ip, last_ip]
709
710 dhcp_path = self.config['ovs_controller_file_path']
711
712 controller_host = self.get_dhcp_controller()
713 controller_host.create_linux_bridge(vlan)
714 controller_host.create_dhcp_interfaces(vlan, first_ip, dhcp_netmask)
715 controller_host.launch_dhcp_server(vlan, ip_range, dhcp_netmask, dhcp_path, gateway)
716
717