1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openvim
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 This is the thread for the http server North API.
26 Two thread will be launched, with normal and administrative permissions.
29 __author__
= "Alfonso Tierno, Leonardo Mirabal"
30 __date__
= "$06-Feb-2017 12:07:15$"
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
43 HTTP_Bad_Request
= 400
44 HTTP_Unauthorized
= 401
47 HTTP_Method_Not_Allowed
= 405
48 HTTP_Not_Acceptable
= 406
49 HTTP_Request_Timeout
= 408
51 HTTP_Service_Unavailable
= 503
52 HTTP_Internal_Server_Error
= 500
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
59 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
60 'items': tuple of keys to convert
64 if type(data
) is dict:
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
)
69 if type(data
[k
]) is str:
70 if data
[k
] == "false":
72 elif data
[k
] == "true":
74 if type(data
) is tuple or type(data
) is list:
76 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
77 convert_boolean(k
, items
)
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
)
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"])
93 self
.db
= self
._create
_database
_connection
()
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']) )
108 def _check_dhcp_data_integrity(network
):
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
114 if "cidr" in network
:
115 cidr
= network
["cidr"]
116 ip_tools
= IPNetwork(cidr
)
117 cidr_len
= ip_tools
.prefixlen
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])
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}$"}
137 js_v(uuid
, id_schema
)
139 except js_e
.ValidationError
:
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()
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"]) )
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
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
:
166 self
.config
['bridge_nets'].append([bridge
, vlan_speed
[0], vlan_speed
[1], None])
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]})
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
})
185 self
.config
['dhcp_nets'].append(nets
[0]['uuid'])
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'})
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
195 OF_conn
= oft
.of_test_connector({"of_debug": self
.config
['log_level_of']})
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']
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":
206 if self
.config
['of_controller'] == 'opendaylight':
208 elif "of_controller_module" in self
.config
:
209 module
= self
.config
["of_controller_module"]
211 module
= self
.config
['of_controller']
214 module_info
= imp
.find_module(module
)
216 OF_conn
= imp
.load_module("OF_conn", *module_info
)
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])
224 except (IOError, ImportError) as e
:
225 if module_info
and module_info
[0]:
226 file.close(module_info
[0])
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
)))
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()
240 raise ovimException("Cannot get openflow information %s", c
)
242 self
.config
['of_thread'] = thread
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")
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'])
251 self
.config
['dhcp_thread'] = thread
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'] = {}
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
)
265 self
.config
['host_threads'][host
['uuid']] = thread
267 # create ovs dhcp thread
268 result
, content
= self
.db
.get_table(FROM
='nets')
270 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
271 raise ovimException(str(content
), -result
)
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'],
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'])
290 for thread
in threads
.values():
291 thread
.insert_task("exit")
292 for thread
in threads
.values():
295 def get_networks(self
, columns
=None, db_filter
={}, limit
=None):
297 Retreive networks available
298 :param columns: List with select query parameters
299 :param db_filter: List with where query parameters
300 :param limit: Query limit result
303 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='nets', WHERE
=db_filter
, LIMIT
=limit
)
306 raise ovimException(str(content
), -result
)
308 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
312 def show_network(self
, network_id
, db_filter
={}):
314 Get network from DB by id
315 :param network_id: net Id
316 :param db_filter: List with where query parameters
321 raise ovimException("Not network id was not found")
322 db_filter
['uuid'] = network_id
324 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
=db_filter
, LIMIT
=100)
327 raise ovimException(str(content
), -result
)
329 raise ovimException("show_network network '%s' not found" % network_id
, -result
)
331 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
333 result
, ports
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
334 WHERE
={'net_id': network_id
}, LIMIT
=100)
336 content
[0]['ports'] = ports
338 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
341 def new_network(self
, network
):
346 tenant_id
= network
.get('tenant_id')
349 result
, _
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
, "enabled": True})
351 raise ovimException("set_network error, no tenant founded", -result
)
355 net_provider
= network
.get('provider')
356 net_type
= network
.get('type')
357 net_vlan
= network
.get("vlan")
358 net_bind_net
= network
.get("bind_net")
359 net_bind_type
= network
.get("bind_type")
360 name
= network
["name"]
362 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
363 vlan_index
= name
.rfind(":")
364 if not net_bind_net
and not net_bind_type
and vlan_index
> 1:
366 vlan_tag
= int(name
[vlan_index
+ 1:])
367 if not vlan_tag
and vlan_tag
< 4096:
368 net_bind_net
= name
[:vlan_index
]
369 net_bind_type
= "vlan:" + name
[vlan_index
+ 1:]
374 # look for a valid net
375 if self
._check
_valid
_uuid
(net_bind_net
):
376 net_bind_key
= "uuid"
378 net_bind_key
= "name"
379 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
381 raise ovimException(' getting nets from db ' + content
, HTTP_Internal_Server_Error
)
383 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
385 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
386 network
["bind_net"] = content
[0]["uuid"]
389 if net_bind_type
[0:5] != "vlan:":
390 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
391 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
392 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
394 network
["bind_type"] = net_bind_type
397 if net_provider
[:9] == "openflow:":
399 if net_type
!= "ptp" and net_type
!= "data":
400 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
406 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
407 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
408 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
410 net_type
= 'bridge_man'
413 net_type
= 'bridge_man'
416 if net_provider
[:7] == 'bridge:':
417 # check it is one of the pre-provisioned bridges
418 bridge_net_name
= net_provider
[7:]
419 for brnet
in self
.config
['bridge_nets']:
420 if brnet
[0] == bridge_net_name
: # free
422 raise ovimException("invalid 'provider:physical', "
423 "bridge '%s' is already used" % bridge_net_name
, HTTP_Conflict
)
427 # if bridge_net==None:
428 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
429 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
432 elif self
.config
['network_type'] == 'bridge' and (net_type
== 'bridge_data' or net_type
== 'bridge_man'):
433 # look for a free precreated nets
434 for brnet
in self
.config
['bridge_nets']:
435 if not brnet
[3]: # free
437 if net_type
== 'bridge_man': # look for the smaller speed
438 if brnet
[2] < bridge_net
[2]:
440 else: # look for the larger speed
441 if brnet
[2] > bridge_net
[2]:
447 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
448 "will overcome this limit", HTTP_Bad_Request
)
450 self
.logger
.debug("using net " + bridge_net
)
451 net_provider
= "bridge:" + bridge_net
[0]
452 net_vlan
= bridge_net
[1]
453 elif net_type
== 'bridge_data' or net_type
== 'bridge_man' and self
.config
['network_type'] == 'ovs':
455 if not net_vlan
and (net_type
== "data" or net_type
== "ptp" or net_provider
== "OVS"):
456 net_vlan
= self
.db
.get_free_net_vlan()
458 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error
)
459 if net_provider
== 'OVS':
460 net_provider
= 'OVS' + ":" + str(net_vlan
)
462 network
['provider'] = net_provider
463 network
['type'] = net_type
464 network
['vlan'] = net_vlan
465 dhcp_integrity
= True
466 if 'enable_dhcp' in network
and network
['enable_dhcp']:
467 dhcp_integrity
= self
._check
_dhcp
_data
_integrity
(network
)
469 result
, content
= self
.db
.new_row('nets', network
, True, True)
471 if result
>= 0 and dhcp_integrity
:
473 bridge_net
[3] = content
474 if self
.config
.get("dhcp_server") and self
.config
['network_type'] == 'bridge':
475 if network
["name"] in self
.config
["dhcp_server"].get("nets", ()):
476 self
.config
["dhcp_nets"].append(content
)
477 self
.logger
.debug("dhcp_server: add new net", content
)
478 elif not bridge_net
and bridge_net
[0] in self
.config
["dhcp_server"].get("bridge_ifaces", ()):
479 self
.config
["dhcp_nets"].append(content
)
480 self
.logger
.debug("dhcp_server: add new net", content
, content
)
483 raise ovimException("Error posting network", HTTP_Internal_Server_Error
)
484 # TODO kei change update->edit
486 def edit_network(self
, network_id
, network
):
488 Update entwork data byt id
491 # Look for the previous data
492 where_
= {'uuid': network_id
}
493 result
, network_old
= self
.db
.get_table(FROM
='nets', WHERE
=where_
)
495 raise ovimException("Error updating network %s" % network_old
, HTTP_Internal_Server_Error
)
497 raise ovimException('network %s not found' % network_id
, HTTP_Not_Found
)
499 nbports
, content
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
500 WHERE
={'net_id': network_id
}, LIMIT
=100)
502 raise ovimException("http_put_network_id error %d %s" % (result
, network_old
), HTTP_Internal_Server_Error
)
504 if 'type' in network
and network
['type'] != network_old
[0]['type']:
505 raise ovimException("Can not change type of network while having ports attached",
506 HTTP_Method_Not_Allowed
)
507 if 'vlan' in network
and network
['vlan'] != network_old
[0]['vlan']:
508 raise ovimException("Can not change vlan of network while having ports attached",
509 HTTP_Method_Not_Allowed
)
512 net_provider
= network
.get('provider', network_old
[0]['provider'])
513 net_type
= network
.get('type', network_old
[0]['type'])
514 net_bind_net
= network
.get("bind_net")
515 net_bind_type
= network
.get("bind_type")
517 # look for a valid net
518 if self
._check
_valid
_uuid
(net_bind_net
):
519 net_bind_key
= "uuid"
521 net_bind_key
= "name"
522 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
524 raise ovimException('Getting nets from db ' + content
, HTTP_Internal_Server_Error
)
526 raise ovimException("bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
528 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
),
530 network
["bind_net"] = content
[0]["uuid"]
532 if net_bind_type
[0:5] != "vlan:":
533 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
534 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
535 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
538 if net_provider
[:9] == "openflow:":
539 if net_type
!= "ptp" and net_type
!= "data":
540 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request
)
542 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
543 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
544 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
546 # insert in data base
547 result
, content
= self
.db
.update_rows('nets', network
, WHERE
={'uuid': network_id
}, log
=True)
549 # if result > 0 and nbports>0 and 'admin_state_up' in network
550 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
552 r
, c
= self
.config
['of_thread'].insert_task("update-net", network_id
)
554 raise ovimException("Error while launching openflow rules %s" % c
, HTTP_Internal_Server_Error
)
555 if self
.config
.get("dhcp_server"):
556 if network_id
in self
.config
["dhcp_nets"]:
557 self
.config
["dhcp_nets"].remove(network_id
)
558 if network
.get("name", network_old
["name"]) in self
.config
["dhcp_server"].get("nets", ()):
559 self
.config
["dhcp_nets"].append(network_id
)
561 net_bind
= network
.get("bind", network_old
["bind"])
562 if net_bind
and net_bind
[:7] == "bridge:" and net_bind
[7:] in self
.config
["dhcp_server"].get(
563 "bridge_ifaces", ()):
564 self
.config
["dhcp_nets"].append(network_id
)
567 raise ovimException(content
, -result
)
569 def delete_network(self
, network_id
):
571 Delete network by network id
572 :param network_id: network id
576 # delete from the data base
577 result
, content
= self
.db
.delete_row('nets', network_id
)
580 raise ovimException("Network %s not found " % network_id
, HTTP_Not_Found
)
582 for brnet
in self
.config
['bridge_nets']:
583 if brnet
[3] == network_id
:
586 if self
.config
.get("dhcp_server") and network_id
in self
.config
["dhcp_nets"]:
587 self
.config
["dhcp_nets"].remove(network_id
)
590 raise ovimException("Error deleting network %s" % network_id
, HTTP_Internal_Server_Error
)
592 def get_openflow_rules(self
, network_id
=None):
594 Get openflow id from DB
595 :param network_id: Network id, if none all networks will be retrieved
596 :return: Return a list with Openflow rules per net
602 where_
= {"net_id": network_id
}
604 result
, content
= self
.db
.get_table(
605 SELECT
=("name", "net_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
606 WHERE
=where_
, FROM
='of_flows')
609 raise ovimException(str(content
), -result
)
612 def edit_openflow_rules(self
, network_id
=None):
615 To make actions over the net. The action is to reinstall the openflow rules
616 network_id can be 'all'
617 :param network_id: Network id, if none all networks will be retrieved
618 :return : Number of nets updated
625 where_
= {"uuid": network_id
}
626 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
629 raise ovimException(str(content
), -result
)
632 if net
["type"] != "ptp" and net
["type"] != "data":
635 r
, c
= self
.config
['of_thread'].insert_task("update-net", net
['uuid'])
637 raise ovimException(str(c
), -r
)
640 def delete_openflow_rules(self
):
642 To make actions over the net. The action is to delete ALL openflow rules
643 :return: return operation result
646 r
, c
= self
.config
['of_thread'].insert_task("clear-all")
648 raise ovimException(str(c
), -r
)
651 def get_openflow_ports(self
):
653 Obtain switch ports names of openflow controller
654 :return: Return flow ports in DB
656 data
= {'ports': self
.config
['of_thread'].OF_connector
.pp2ofi
}
659 def get_ports(self
, columns
=None, filter={}, limit
=None):
660 # result, content = my.db.get_ports(where_)
661 result
, content
= self
.db
.get_table(SELECT
=columns
, WHERE
=filter, FROM
='ports', LIMIT
=limit
)
663 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
664 raise ovimException(str(content
), -result
)
666 convert_boolean(content
, ('admin_state_up',))
669 def new_port(self
, port_data
):
670 port_data
['type'] = 'external'
671 if port_data
.get('net_id'):
672 # check that new net has the correct type
673 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
675 raise ovimException(str(new_net
), -result
)
676 # insert in data base
677 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
679 if 'net_id' in port_data
:
680 r
, c
= self
.config
['of_thread'].insert_task("update-net", port_data
['net_id'])
682 self
.logger
.error("Cannot insert a task for updating network '$s' %s", port_data
['net_id'], c
)
683 #TODO put network in error status
686 raise ovimException(str(uuid
), -result
)
688 def delete_port(self
, port_id
):
689 # Look for the previous port data
690 result
, ports
= self
.db
.get_table(WHERE
={'uuid': port_id
, "type": "external"}, FROM
='ports')
692 raise ovimException("Cannot get port info from database: {}".format(ports
), http_code
=-result
)
693 # delete from the data base
694 result
, content
= self
.db
.delete_row('ports', port_id
)
696 raise ovimException("External port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
698 raise ovimException("Cannot delete port from database: {}".format(content
), http_code
=-result
)
700 network
= ports
[0].get('net_id', None)
703 r
, c
= self
.config
['of_thread'].insert_task("update-net", network
)
705 self
.logger
.error("Cannot insert a task for updating network '$s' %s", network
, c
)
708 def edit_port(self
, port_id
, port_data
, admin
=True):
709 # Look for the previous port data
710 result
, content
= self
.db
.get_table(FROM
="ports", WHERE
={'uuid': port_id
})
712 raise ovimException("Cannot get port info from database: {}".format(content
), http_code
=-result
)
714 raise ovimException("Port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
719 if 'net_id' in port_data
:
721 old_net
= port
.get('net_id', None)
722 new_net
= port_data
['net_id']
723 if old_net
!= new_net
:
726 nets
.append(new_net
) # put first the new net, so that new openflow rules are created before removing the old ones
729 if port
['type'] == 'instance:bridge' or port
['type'] == 'instance:ovs':
730 raise ovimException("bridge interfaces cannot be attached to a different net", http_code
=HTTP_Forbidden
)
731 elif port
['type'] == 'external' and not admin
:
732 raise ovimException("Needed admin privileges",http_code
=HTTP_Unauthorized
)
734 # check that new net has the correct type
735 result
, new_net_dict
= self
.db
.check_target_net(new_net
, None, port
['type'])
737 raise ovimException("Error {}".format(new_net_dict
), http_code
=HTTP_Conflict
)
738 # change VLAN for SR-IOV ports
739 if result
>= 0 and port
["type"] == "instance:data" and port
["model"] == "VF": # TODO consider also VFnotShared
741 port_data
["vlan"] = None
743 port_data
["vlan"] = new_net_dict
["vlan"]
744 # get host where this VM is allocated
745 result
, content
= self
.db
.get_table(FROM
="instances", WHERE
={"uuid": port
["instance_id"]})
747 host_id
= content
[0]["host_id"]
749 # insert in data base
751 result
, content
= self
.db
.update_rows('ports', port_data
, WHERE
={'uuid': port_id
}, log
=False)
753 # Insert task to complete actions
756 r
, v
= self
.config
['of_thread'].insert_task("update-net", net_id
)
758 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
759 # TODO Do something if fails
761 r
, v
= self
.config
['host_threads'][host_id
].insert_task("edit-iface", port_id
, old_net
, new_net
)
763 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
764 # TODO Do something if fails
768 raise ovimException("Error {}".format(content
), http_code
=-result
)
770 def new_of_controller(self
, ofc_data
):
772 Create a new openflow controller into DB
773 :param ofc_data: Dict openflow controller data
774 :return: openflow controller dpid
777 result
, content
= self
.db
.new_row('ofcs', ofc_data
, True, True)
779 raise ovimException("New ofc Error %s" % content
, HTTP_Internal_Server_Error
)
782 def edit_of_controller(self
, of_id
, ofc_data
):
784 Edit an openflow controller entry from DB
788 raise ovimException("No data received during uptade OF contorller", http_code
=HTTP_Internal_Server_Error
)
790 old_of_controller
= self
.show_of_controller(of_id
)
792 if old_of_controller
:
793 result
, content
= self
.db
.update_rows('ofcs', ofc_data
, WHERE
={'uuid': of_id
}, log
=False)
797 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
800 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
801 http_code
=HTTP_Internal_Server_Error
)
803 def delete_of_controller(self
, of_id
):
805 Delete an openflow controller from DB.
806 :param of_id: openflow controller dpid
810 result
, content
= self
.db
.delete_row_by_key("ofcs", "uuid", of_id
)
812 raise ovimException("Cannot delete ofc from database: {}".format(content
), http_code
=-result
)
814 raise ovimException("ofc {} not found ".format(content
), http_code
=HTTP_Not_Found
)
817 def show_of_controller(self
, uuid
):
819 Show an openflow controller by dpid from DB.
820 :param db_filter: List with where query parameters
824 result
, content
= self
.db
.get_table(FROM
='ofcs', WHERE
={"uuid": uuid
}, LIMIT
=100)
827 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid
),
828 http_code
=HTTP_Not_Found
)
830 raise ovimException("Openflow controller with uuid '{}' error".format(uuid
),
831 http_code
=HTTP_Internal_Server_Error
)
834 def get_of_controllers(self
, columns
=None, db_filter
={}, limit
=None):
836 Show an openflow controllers from DB.
837 :param columns: List with SELECT query parameters
838 :param db_filter: List with where query parameters
839 :param limit: result Limit
842 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='ofcs', WHERE
=db_filter
, LIMIT
=limit
)
845 raise ovimException(str(content
), -result
)
849 def get_tenants(self
, columns
=None, db_filter
={}, limit
=None):
851 Retrieve tenant list from DB
852 :param columns: List with SELECT query parameters
853 :param db_filter: List with where query parameters
854 :param limit: result limit
857 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=columns
, WHERE
=db_filter
, LIMIT
=limit
)
859 raise ovimException('get_tenatns Error {}'.format(str(content
)), -result
)
861 convert_boolean(content
, ('enabled',))
864 def show_tenant_id(self
, tenant_id
):
866 Get tenant from DB by id
867 :param tenant_id: tenant id
870 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid', 'name', 'description', 'enabled'),
871 WHERE
={"uuid": tenant_id
})
873 raise ovimException(str(content
), -result
)
875 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
877 convert_boolean(content
, ('enabled',))
880 def new_tentant(self
, tenant
):
882 Create a tenant and store in DB
883 :param tenant: Dictionary with tenant data
884 :return: the uuid of created tenant. Raise exception upon error
887 # insert in data base
888 result
, tenant_uuid
= self
.db
.new_tenant(tenant
)
893 raise ovimException(str(tenant_uuid
), -result
)
895 def delete_tentant(self
, tenant_id
):
897 Delete a tenant from the database.
898 :param tenant_id: Tenant id
899 :return: delete tenant id
903 r
, tenants_flavors
= self
.db
.get_table(FROM
='tenants_flavors', SELECT
=('flavor_id', 'tenant_id'),
904 WHERE
={'tenant_id': tenant_id
})
907 r
, tenants_images
= self
.db
.get_table(FROM
='tenants_images', SELECT
=('image_id', 'tenant_id'),
908 WHERE
={'tenant_id': tenant_id
})
912 result
, content
= self
.db
.delete_row('tenants', tenant_id
)
914 raise ovimException("tenant '%s' not found" % tenant_id
, HTTP_Not_Found
)
916 for flavor
in tenants_flavors
:
917 self
.db
.delete_row_by_key("flavors", "uuid", flavor
['flavor_id'])
918 for image
in tenants_images
:
919 self
.db
.delete_row_by_key("images", "uuid", image
['image_id'])
922 raise ovimException("Error deleting tenant '%s' " % tenant_id
, HTTP_Internal_Server_Error
)
924 def edit_tenant(self
, tenant_id
, tenant_data
):
926 Update a tenant data identified by tenant id
927 :param tenant_id: tenant id
928 :param tenant_data: Dictionary with tenant data
932 # Look for the previous data
933 result
, tenant_data_old
= self
.db
.get_table(FROM
='tenants', WHERE
={'uuid': tenant_id
})
935 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id
, tenant_data_old
),
936 HTTP_Internal_Server_Error
)
938 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
940 # insert in data base
941 result
, content
= self
.db
.update_rows('tenants', tenant_data
, WHERE
={'uuid': tenant_id
}, log
=True)
945 raise ovimException(str(content
), -result
)
947 def get_dhcp_controller(self
):
949 Create an host_thread object for manage openvim controller and not create a thread for itself
950 :return: dhcp_host openvim controller object
953 if 'openvim_controller' in self
.config
['host_threads']:
954 return self
.config
['host_threads']['openvim_controller']
957 controller_ip
= self
.config
['ovs_controller_ip']
958 ovs_controller_user
= self
.config
['ovs_controller_user']
960 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
961 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
963 dhcp_host
= ht
.host_thread(name
='openvim_controller', user
=ovs_controller_user
, host
=controller_ip
,
964 db
=self
.config
['db'],
965 db_lock
=self
.config
['db_lock'], test
=host_test_mode
,
966 image_path
=self
.config
['image_path'], version
=self
.config
['version'],
967 host_id
='openvim_controller', develop_mode
=host_develop_mode
,
968 develop_bridge_iface
=bridge_ifaces
)
970 self
.config
['host_threads']['openvim_controller'] = dhcp_host
971 if not host_test_mode
:
972 dhcp_host
.ssh_connect()
975 def launch_dhcp_server(self
, vlan
, first_ip
, last_ip
, cidr
, gateway
):
977 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
978 :param vlan: vlan identifier
979 :param first_ip: First dhcp range ip
980 :param last_ip: Last dhcp range ip
981 :param cidr: net cidr
982 :param gateway: net gateway
985 ip_tools
= IPNetwork(cidr
)
986 dhcp_netmask
= str(ip_tools
.netmask
)
987 ip_range
= [first_ip
, last_ip
]
989 dhcp_path
= self
.config
['ovs_controller_file_path']
991 controller_host
= self
.get_dhcp_controller()
992 controller_host
.create_linux_bridge(vlan
)
993 controller_host
.create_dhcp_interfaces(vlan
, first_ip
, dhcp_netmask
)
994 controller_host
.launch_dhcp_server(vlan
, ip_range
, dhcp_netmask
, dhcp_path
, gateway
)