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$"
31 __version__
= "0.5.8-r524"
32 version_date
= "March 2017"
33 database_version
= "0.15" #expected database schema version
40 import host_thread
as ht
41 import dhcp_thread
as dt
42 import openflow_thread
as oft
43 from netaddr
import IPNetwork
44 from jsonschema
import validate
as js_v
, exceptions
as js_e
46 HTTP_Bad_Request
= 400
47 HTTP_Unauthorized
= 401
50 HTTP_Method_Not_Allowed
= 405
51 HTTP_Not_Acceptable
= 406
52 HTTP_Request_Timeout
= 408
54 HTTP_Service_Unavailable
= 503
55 HTTP_Internal_Server_Error
= 500
58 def convert_boolean(data
, items
):
59 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
60 It assumes that bandwidth is well formed
62 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
63 'items': tuple of keys to convert
67 if type(data
) is dict:
69 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
70 convert_boolean(data
[k
], items
)
72 if type(data
[k
]) is str:
73 if data
[k
] == "false":
75 elif data
[k
] == "true":
77 if type(data
) is tuple or type(data
) is list:
79 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
80 convert_boolean(k
, items
)
84 class ovimException(Exception):
85 def __init__(self
, message
, http_code
=HTTP_Bad_Request
):
86 self
.http_code
= http_code
87 Exception.__init
__(self
, message
)
91 running_info
= {} #TODO OVIM move the info of running threads from config_dic to this static variable
94 def __init__(self
, configuration
):
95 self
.config
= configuration
96 self
.logger
= logging
.getLogger(configuration
["logger_name"])
98 self
.db
= self
._create
_database
_connection
()
101 self
.of_test_mode
= False
103 def _create_database_connection(self
):
104 db
= vim_db
.vim_db((self
.config
["network_vlan_range_start"], self
.config
["network_vlan_range_end"]),
105 self
.config
['log_level_db']);
106 if db
.connect(self
.config
['db_host'], self
.config
['db_user'], self
.config
['db_passwd'],
107 self
.config
['db_name']) == -1:
108 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
109 # self.config['db_host'])
110 raise ovimException("Cannot connect to database {} at {}@{}".format(self
.config
['db_name'],
111 self
.config
['db_user'],
112 self
.config
['db_host']) )
120 def get_version_date():
124 def get_database_version():
125 return database_version
128 def _check_dhcp_data_integrity(network
):
130 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
131 :param network: list with user nets paramters
134 if "cidr" in network
:
135 cidr
= network
["cidr"]
136 ip_tools
= IPNetwork(cidr
)
137 cidr_len
= ip_tools
.prefixlen
141 ips
= IPNetwork(cidr
)
142 if "dhcp_first_ip" not in network
:
143 network
["dhcp_first_ip"] = str(ips
[2])
144 if "dhcp_last_ip" not in network
:
145 network
["dhcp_last_ip"] = str(ips
[-2])
146 if "gateway_ip" not in network
:
147 network
["gateway_ip"] = str(ips
[1])
154 def _check_valid_uuid(uuid
):
155 id_schema
= {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
157 js_v(uuid
, id_schema
)
159 except js_e
.ValidationError
:
162 def start_service(self
):
167 global database_version
168 # if self.running_info:
169 # return #TODO service can be checked and rebuild broken threads
170 r
= self
.db
.get_db_version()
172 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
173 "'./database_utils/migrate_vim_db.sh'".format(database_version
) )
174 elif r
[1] != database_version
:
175 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
176 "'./database_utils/migrate_vim_db.sh'".format(r
[1], database_version
) )
178 # create database connection for openflow threads
179 self
.db_of
= self
._create
_database
_connection
()
180 self
.config
["db"] = self
.db_of
181 self
.db_lock
= threading
.Lock()
182 self
.config
["db_lock"] = self
.db_lock
184 self
.of_test_mode
= False if self
.config
['mode'] == 'normal' or self
.config
['mode'] == "OF only" else True
185 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
188 self
.config
['dhcp_nets'] = []
189 self
.config
['bridge_nets'] = []
190 for bridge
, vlan_speed
in self
.config
["bridge_ifaces"].items():
191 # skip 'development_bridge'
192 if self
.config
['mode'] == 'development' and self
.config
['development_bridge'] == bridge
:
194 self
.config
['bridge_nets'].append([bridge
, vlan_speed
[0], vlan_speed
[1], None])
196 # check if this bridge is already used (present at database) for a network)
197 used_bridge_nets
= []
198 for brnet
in self
.config
['bridge_nets']:
199 r
, nets
= self
.db
.get_table(SELECT
=('uuid',), FROM
='nets', WHERE
={'provider': "bridge:" + brnet
[0]})
201 brnet
[3] = nets
[0]['uuid']
202 used_bridge_nets
.append(brnet
[0])
203 if self
.config
.get("dhcp_server"):
204 if brnet
[0] in self
.config
["dhcp_server"]["bridge_ifaces"]:
205 self
.config
['dhcp_nets'].append(nets
[0]['uuid'])
206 if len(used_bridge_nets
) > 0:
207 self
.logger
.info("found used bridge nets: " + ",".join(used_bridge_nets
))
208 # get nets used by dhcp
209 if self
.config
.get("dhcp_server"):
210 for net
in self
.config
["dhcp_server"].get("nets", ()):
211 r
, nets
= self
.db
.get_table(SELECT
=('uuid',), FROM
='nets', WHERE
={'name': net
})
213 self
.config
['dhcp_nets'].append(nets
[0]['uuid'])
216 self
._start
_ofc
_default
_task
()
218 # OFC per tenant in DB
219 self
._start
_of
_db
_tasks
()
221 # create dhcp_server thread
222 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
223 dhcp_params
= self
.config
.get("dhcp_server")
225 thread
= dt
.dhcp_thread(dhcp_params
=dhcp_params
, test
=host_test_mode
, dhcp_nets
=self
.config
["dhcp_nets"],
226 db
=self
.db_of
, db_lock
=self
.db_lock
, debug
=self
.config
['log_level_of'])
228 self
.config
['dhcp_thread'] = thread
230 # Create one thread for each host
231 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
232 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
233 host_develop_bridge_iface
= self
.config
.get('development_bridge', None)
235 # get host list from data base before starting threads
236 r
, hosts
= self
.db
.get_table(SELECT
=('name', 'ip_name', 'user', 'uuid'), FROM
='hosts', WHERE
={'status': 'ok'})
238 raise ovimException("Cannot get hosts from database {}".format(hosts
))
240 self
.config
['host_threads'] = {}
242 host
['image_path'] = '/opt/VNF/images/openvim'
243 thread
= ht
.host_thread(name
=host
['name'], user
=host
['user'], host
=host
['ip_name'], db
=self
.db_of
,
244 db_lock
=self
.db_lock
, test
=host_test_mode
, image_path
=self
.config
['image_path'],
245 version
=self
.config
['version'], host_id
=host
['uuid'], develop_mode
=host_develop_mode
,
246 develop_bridge_iface
=host_develop_bridge_iface
)
248 self
.config
['host_threads'][host
['uuid']] = thread
250 # create ovs dhcp thread
251 result
, content
= self
.db
.get_table(FROM
='nets')
253 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
254 raise ovimException(str(content
), -result
)
257 net_type
= net
['type']
258 if (net_type
== 'bridge_data' or net_type
== 'bridge_man') \
259 and net
["provider"][:4] == 'OVS:' and net
["enable_dhcp"] == "true":
260 self
.launch_dhcp_server(net
['vlan'],
261 net
['dhcp_first_ip'],
266 def _start_of_db_tasks(self
):
268 Start ofc task for existing ofcs in database
273 ofcs
= self
.get_of_controllers()
276 of_conn
= self
._load
_of
_module
(ofc
)
277 # create ofc thread per of controller
278 self
._create
_ofc
_task
(ofc
['uuid'], ofc
['dpid'], of_conn
)
280 def _create_ofc_task(self
, ofc_uuid
, dpid
, of_conn
):
282 Create an ofc thread for handle each sdn controllers
283 :param ofc_uuid: sdn controller uuid
284 :param dpid: sdn controller dpid
285 :param of_conn: OF_conn module
288 if 'ofcs_thread' not in self
.config
and 'ofcs_thread_dpid' not in self
.config
:
290 ofcs_thread_dpid
= []
292 ofcs_threads
= self
.config
['ofcs_thread']
293 ofcs_thread_dpid
= self
.config
['ofcs_thread_dpid']
295 if ofc_uuid
not in ofcs_threads
:
296 ofc_thread
= self
._create
_ofc
_thread
(of_conn
, ofc_uuid
)
297 if ofc_uuid
== "Default":
298 self
.config
['of_thread'] = ofc_thread
300 ofcs_threads
[ofc_uuid
] = ofc_thread
301 self
.config
['ofcs_thread'] = ofcs_threads
303 ofcs_thread_dpid
.append({dpid
: ofc_thread
})
304 self
.config
['ofcs_thread_dpid'] = ofcs_thread_dpid
306 def _start_ofc_default_task(self
):
308 Create default ofc thread
310 if 'of_controller' not in self
.config \
311 and 'of_controller_ip' not in self
.config \
312 and 'of_controller_port' not in self
.config \
313 and 'of_controller_dpid' not in self
.config
:
318 db_config
['ip'] = self
.config
.get('of_controller_ip')
319 db_config
['port'] = self
.config
.get('of_controller_port')
320 db_config
['dpid'] = self
.config
.get('of_controller_dpid')
321 db_config
['type'] = self
.config
.get('of_controller')
322 db_config
['user'] = self
.config
.get('of_user')
323 db_config
['password'] = self
.config
.get('of_password')
325 # create connector to the openflow controller
326 # load other parameters starting by of_ from config dict in a temporal dict
328 of_conn
= self
._load
_of
_module
(db_config
)
329 # create openflow thread
330 self
._create
_ofc
_task
("Default", db_config
['dpid'], of_conn
)
332 def _load_of_module(self
, db_config
):
334 import python module for each SDN controller supported
335 :param db_config: SDN dn information
339 raise ovimException("No module found it", HTTP_Internal_Server_Error
)
344 if self
.of_test_mode
:
345 return oft
.of_test_connector({"name": db_config
['type'], "dpid": db_config
['dpid'],
346 "of_debug": self
.config
['log_level_of']})
350 temp_dict
['of_ip'] = db_config
['ip']
351 temp_dict
['of_port'] = db_config
['port']
352 temp_dict
['of_dpid'] = db_config
['dpid']
353 temp_dict
['of_controller'] = db_config
['type']
355 temp_dict
['of_debug'] = self
.config
['log_level_of']
357 if temp_dict
['of_controller'] == 'opendaylight':
360 module
= temp_dict
['of_controller']
362 if module
not in ovim
.of_module
:
363 module_info
= imp
.find_module(module
)
364 of_conn_module
= imp
.load_module("OF_conn", *module_info
)
365 ovim
.of_module
[module
] = of_conn_module
367 of_conn_module
= ovim
.of_module
[module
]
370 return of_conn_module
.OF_conn(temp_dict
)
371 except Exception as e
:
372 self
.logger
.error("Cannot open the Openflow controller '%s': %s", type(e
).__name
__, str(e
))
373 if module_info
and module_info
[0]:
374 file.close(module_info
[0])
375 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e
).__name
__, str(e
)),
376 HTTP_Internal_Server_Error
)
377 except (IOError, ImportError) as e
:
378 if module_info
and module_info
[0]:
379 file.close(module_info
[0])
380 self
.logger
.error("Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' "
381 "field of configuration file.", module
, type(e
).__name
__, str(e
))
382 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' "
383 "field of configuration file.".format(module
, type(e
).__name
__, str(e
)),
384 HTTP_Internal_Server_Error
)
386 def _create_ofc_thread(self
, of_conn
, ofc_uuid
="Default"):
388 Create and launch a of thread
391 # create openflow thread
393 if 'of_controller_nets_with_same_vlan' in self
.config
:
394 ofc_net_same_vlan
= self
.config
['of_controller_nets_with_same_vlan']
396 ofc_net_same_vlan
= False
398 thread
= oft
.openflow_thread(ofc_uuid
, of_conn
, of_test
=self
.of_test_mode
, db
=self
.db_of
, db_lock
=self
.db_lock
,
399 pmp_with_same_vlan
=ofc_net_same_vlan
, debug
=self
.config
['log_level_of'])
400 #r, c = thread.OF_connector.obtain_port_correspondence()
402 # raise ovimException("Cannot get openflow information %s", c)
406 def stop_service(self
):
407 threads
= self
.config
.get('host_threads', {})
408 if 'of_thread' in self
.config
:
409 threads
['of'] = (self
.config
['of_thread'])
410 if 'ofcs_thread' in self
.config
:
411 ofcs_thread
= self
.config
['ofcs_thread']
412 for ofc
in ofcs_thread
:
413 threads
[ofc
] = ofcs_thread
[ofc
]
415 if 'dhcp_thread' in self
.config
:
416 threads
['dhcp'] = (self
.config
['dhcp_thread'])
418 for thread
in threads
.values():
419 thread
.insert_task("exit")
420 for thread
in threads
.values():
423 def get_networks(self
, columns
=None, db_filter
={}, limit
=None):
425 Retreive networks available
426 :param columns: List with select query parameters
427 :param db_filter: List with where query parameters
428 :param limit: Query limit result
431 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='nets', WHERE
=db_filter
, LIMIT
=limit
)
434 raise ovimException(str(content
), -result
)
436 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
440 def show_network(self
, network_id
, db_filter
={}):
442 Get network from DB by id
443 :param network_id: net Id
444 :param db_filter: List with where query parameters
449 raise ovimException("Not network id was not found")
450 db_filter
['uuid'] = network_id
452 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
=db_filter
, LIMIT
=100)
455 raise ovimException(str(content
), -result
)
457 raise ovimException("show_network network '%s' not found" % network_id
, -result
)
459 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
461 result
, ports
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
462 WHERE
={'net_id': network_id
}, LIMIT
=100)
464 content
[0]['ports'] = ports
466 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
469 def new_network(self
, network
):
474 tenant_id
= network
.get('tenant_id')
477 result
, _
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
, "enabled": True})
479 raise ovimException("set_network error, no tenant founded", -result
)
483 net_provider
= network
.get('provider')
484 net_type
= network
.get('type')
485 net_vlan
= network
.get("vlan")
486 net_bind_net
= network
.get("bind_net")
487 net_bind_type
= network
.get("bind_type")
488 name
= network
["name"]
490 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
491 vlan_index
= name
.rfind(":")
492 if not net_bind_net
and not net_bind_type
and vlan_index
> 1:
494 vlan_tag
= int(name
[vlan_index
+ 1:])
495 if not vlan_tag
and vlan_tag
< 4096:
496 net_bind_net
= name
[:vlan_index
]
497 net_bind_type
= "vlan:" + name
[vlan_index
+ 1:]
502 # look for a valid net
503 if self
._check
_valid
_uuid
(net_bind_net
):
504 net_bind_key
= "uuid"
506 net_bind_key
= "name"
507 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
509 raise ovimException(' getting nets from db ' + content
, HTTP_Internal_Server_Error
)
511 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
513 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
514 network
["bind_net"] = content
[0]["uuid"]
517 if net_bind_type
[0:5] != "vlan:":
518 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
519 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
520 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
522 network
["bind_type"] = net_bind_type
525 if net_provider
[:9] == "openflow:":
527 if net_type
!= "ptp" and net_type
!= "data":
528 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
534 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
535 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
536 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
538 net_type
= 'bridge_man'
541 net_type
= 'bridge_man'
544 if net_provider
[:7] == 'bridge:':
545 # check it is one of the pre-provisioned bridges
546 bridge_net_name
= net_provider
[7:]
547 for brnet
in self
.config
['bridge_nets']:
548 if brnet
[0] == bridge_net_name
: # free
550 raise ovimException("invalid 'provider:physical', "
551 "bridge '%s' is already used" % bridge_net_name
, HTTP_Conflict
)
555 # if bridge_net==None:
556 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
557 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
560 elif self
.config
['network_type'] == 'bridge' and (net_type
== 'bridge_data' or net_type
== 'bridge_man'):
561 # look for a free precreated nets
562 for brnet
in self
.config
['bridge_nets']:
563 if not brnet
[3]: # free
565 if net_type
== 'bridge_man': # look for the smaller speed
566 if brnet
[2] < bridge_net
[2]:
568 else: # look for the larger speed
569 if brnet
[2] > bridge_net
[2]:
575 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
576 "will overcome this limit", HTTP_Bad_Request
)
578 self
.logger
.debug("using net " + bridge_net
)
579 net_provider
= "bridge:" + bridge_net
[0]
580 net_vlan
= bridge_net
[1]
581 elif net_type
== 'bridge_data' or net_type
== 'bridge_man' and self
.config
['network_type'] == 'ovs':
583 if not net_vlan
and (net_type
== "data" or net_type
== "ptp" or net_provider
== "OVS"):
584 net_vlan
= self
.db
.get_free_net_vlan()
586 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error
)
587 if net_provider
== 'OVS':
588 net_provider
= 'OVS' + ":" + str(net_vlan
)
590 network
['provider'] = net_provider
591 network
['type'] = net_type
592 network
['vlan'] = net_vlan
593 dhcp_integrity
= True
594 if 'enable_dhcp' in network
and network
['enable_dhcp']:
595 dhcp_integrity
= self
._check
_dhcp
_data
_integrity
(network
)
597 result
, content
= self
.db
.new_row('nets', network
, True, True)
599 if result
>= 0 and dhcp_integrity
:
601 bridge_net
[3] = content
602 if self
.config
.get("dhcp_server") and self
.config
['network_type'] == 'bridge':
603 if network
["name"] in self
.config
["dhcp_server"].get("nets", ()):
604 self
.config
["dhcp_nets"].append(content
)
605 self
.logger
.debug("dhcp_server: add new net", content
)
606 elif not bridge_net
and bridge_net
[0] in self
.config
["dhcp_server"].get("bridge_ifaces", ()):
607 self
.config
["dhcp_nets"].append(content
)
608 self
.logger
.debug("dhcp_server: add new net", content
, content
)
611 raise ovimException("Error posting network", HTTP_Internal_Server_Error
)
612 # TODO kei change update->edit
614 def edit_network(self
, network_id
, network
):
616 Update entwork data byt id
619 # Look for the previous data
620 where_
= {'uuid': network_id
}
621 result
, network_old
= self
.db
.get_table(FROM
='nets', WHERE
=where_
)
623 raise ovimException("Error updating network %s" % network_old
, HTTP_Internal_Server_Error
)
625 raise ovimException('network %s not found' % network_id
, HTTP_Not_Found
)
627 nbports
, content
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
628 WHERE
={'net_id': network_id
}, LIMIT
=100)
630 raise ovimException("http_put_network_id error %d %s" % (result
, network_old
), HTTP_Internal_Server_Error
)
632 if 'type' in network
and network
['type'] != network_old
[0]['type']:
633 raise ovimException("Can not change type of network while having ports attached",
634 HTTP_Method_Not_Allowed
)
635 if 'vlan' in network
and network
['vlan'] != network_old
[0]['vlan']:
636 raise ovimException("Can not change vlan of network while having ports attached",
637 HTTP_Method_Not_Allowed
)
640 net_provider
= network
.get('provider', network_old
[0]['provider'])
641 net_type
= network
.get('type', network_old
[0]['type'])
642 net_bind_net
= network
.get("bind_net")
643 net_bind_type
= network
.get("bind_type")
645 # look for a valid net
646 if self
._check
_valid
_uuid
(net_bind_net
):
647 net_bind_key
= "uuid"
649 net_bind_key
= "name"
650 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
652 raise ovimException('Getting nets from db ' + content
, HTTP_Internal_Server_Error
)
654 raise ovimException("bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
656 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
),
658 network
["bind_net"] = content
[0]["uuid"]
660 if net_bind_type
[0:5] != "vlan:":
661 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
662 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
663 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
666 if net_provider
[:9] == "openflow:":
667 if net_type
!= "ptp" and net_type
!= "data":
668 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request
)
670 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
671 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
672 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
674 # insert in data base
675 result
, content
= self
.db
.update_rows('nets', network
, WHERE
={'uuid': network_id
}, log
=True)
677 # if result > 0 and nbports>0 and 'admin_state_up' in network
678 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
682 self
.net_update_ofc_thread(network_id
)
683 except ovimException
as e
:
684 raise ovimException("Error while launching openflow rules in network '{}' {}"
685 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
686 except Exception as e
:
687 raise ovimException("Error while launching openflow rules in network '{}' {}"
688 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
690 if self
.config
.get("dhcp_server"):
691 if network_id
in self
.config
["dhcp_nets"]:
692 self
.config
["dhcp_nets"].remove(network_id
)
693 if network
.get("name", network_old
[0]["name"]) in self
.config
["dhcp_server"].get("nets", ()):
694 self
.config
["dhcp_nets"].append(network_id
)
696 net_bind
= network
.get("bind_type", network_old
[0]["bind_type"])
697 if net_bind
and net_bind
and net_bind
[:7] == "bridge:" and net_bind
[7:] in self
.config
["dhcp_server"].get(
698 "bridge_ifaces", ()):
699 self
.config
["dhcp_nets"].append(network_id
)
702 raise ovimException(content
, -result
)
704 def delete_network(self
, network_id
):
706 Delete network by network id
707 :param network_id: network id
711 # delete from the data base
712 result
, content
= self
.db
.delete_row('nets', network_id
)
715 raise ovimException("Network %s not found " % network_id
, HTTP_Not_Found
)
717 for brnet
in self
.config
['bridge_nets']:
718 if brnet
[3] == network_id
:
721 if self
.config
.get("dhcp_server") and network_id
in self
.config
["dhcp_nets"]:
722 self
.config
["dhcp_nets"].remove(network_id
)
725 raise ovimException("Error deleting network %s" % network_id
, HTTP_Internal_Server_Error
)
727 def get_openflow_rules(self
, network_id
=None):
729 Get openflow id from DB
730 :param network_id: Network id, if none all networks will be retrieved
731 :return: Return a list with Openflow rules per net
737 where_
= {"net_id": network_id
}
738 result
, content
= self
.db
.get_table(
739 SELECT
=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
740 WHERE
=where_
, FROM
='of_flows')
743 raise ovimException(str(content
), -result
)
746 def edit_openflow_rules(self
, network_id
=None):
749 To make actions over the net. The action is to reinstall the openflow rules
750 network_id can be 'all'
751 :param network_id: Network id, if none all networks will be retrieved
752 :return : Number of nets updated
759 where_
= {"uuid": network_id
}
760 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
763 raise ovimException(str(content
), -result
)
766 if net
["type"] != "ptp" and net
["type"] != "data":
771 self
.net_update_ofc_thread(net
['uuid'])
772 except ovimException
as e
:
773 raise ovimException("Error updating network'{}' {}".format(net
['uuid'], str(e
)),
774 HTTP_Internal_Server_Error
)
775 except Exception as e
:
776 raise ovimException("Error updating network '{}' {}".format(net
['uuid'], str(e
)),
777 HTTP_Internal_Server_Error
)
781 def delete_openflow_rules(self
, ofc_id
=None):
783 To make actions over the net. The action is to delete ALL openflow rules
784 :return: return operation result
788 if 'Default' in self
.config
['ofcs_thread']:
789 r
, c
= self
.config
['ofcs_thread']['Default'].insert_task("clear-all")
791 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
793 elif ofc_id
in self
.config
['ofcs_thread']:
794 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("clear-all")
798 raise ovimException(str(c
), -r
)
800 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
803 def get_openflow_ports(self
, ofc_id
=None):
805 Obtain switch ports names of openflow controller
806 :return: Return flow ports in DB
809 if 'Default' in self
.config
['ofcs_thread']:
810 conn
= self
.config
['ofcs_thread']['Default'].OF_connector
812 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
814 if ofc_id
in self
.config
['ofcs_thread']:
815 conn
= self
.config
['ofcs_thread'][ofc_id
].OF_connector
817 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
820 def get_ports(self
, columns
=None, filter={}, limit
=None):
821 # result, content = my.db.get_ports(where_)
822 result
, content
= self
.db
.get_table(SELECT
=columns
, WHERE
=filter, FROM
='ports', LIMIT
=limit
)
824 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
825 raise ovimException(str(content
), -result
)
827 convert_boolean(content
, ('admin_state_up',))
830 def new_port(self
, port_data
):
831 port_data
['type'] = 'external'
832 if port_data
.get('net_id'):
833 # check that new net has the correct type
834 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
836 raise ovimException(str(new_net
), -result
)
837 # insert in data base
838 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
840 if 'net_id' in port_data
:
842 self
.net_update_ofc_thread(port_data
['net_id'])
843 except ovimException
as e
:
844 raise ovimException("Cannot insert a task for updating network '{}' {}"
845 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
846 except Exception as e
:
847 raise ovimException("Cannot insert a task for updating network '{}' {}"
848 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
852 raise ovimException(str(uuid
), -result
)
854 def new_external_port(self
, port_data
):
856 Create new external port and check port mapping correspondence
857 :param port_data: port_data = {
858 'region': 'datacenter region',
859 'compute_node': 'compute node id',
860 'pci': 'pci port address',
863 'tenant_id': 'tenant id',
866 'ip_address': 'ip address - optional'}
870 port_data
['type'] = 'external'
872 if port_data
.get('net_id'):
873 # check that new net has the correct type
874 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
876 raise ovimException(str(new_net
), -result
)
877 # insert in data base
880 if port_data
.get('region'):
881 db_filter
['region'] = port_data
['region']
882 if port_data
.get('pci'):
883 db_filter
['pci'] = port_data
['pci']
884 if port_data
.get('compute_node'):
885 db_filter
['compute_node'] = port_data
['compute_node']
887 columns
= ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
888 port_mapping_data
= self
.get_of_port_mappings(columns
, db_filter
)
890 if not len(port_mapping_data
):
891 raise ovimException("No port mapping founded for '{}'".format(str(db_filter
)),
893 elif len(port_mapping_data
) > 1:
894 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
897 port_data
['ofc_id'] = port_mapping_data
[0]['ofc_id']
898 port_data
['switch_dpid'] = port_mapping_data
[0]['switch_dpid']
899 port_data
['switch_port'] = port_mapping_data
[0]['switch_port']
900 port_data
['switch_mac'] = port_mapping_data
[0]['switch_mac']
902 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
903 del port_data
['compute_node']
904 del port_data
['region']
907 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
910 self
.net_update_ofc_thread(port_data
['net_id'], port_data
['ofc_id'])
911 except ovimException
as e
:
912 raise ovimException("Cannot insert a task for updating network '{}' {}".
913 format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
914 except Exception as e
:
915 raise ovimException("Cannot insert a task for updating network '{}' {}"
916 .format(port_data
['net_id'], e
), HTTP_Internal_Server_Error
)
919 raise ovimException(str(uuid
), -result
)
921 def net_update_ofc_thread(self
, net_id
, ofc_id
=None):
923 Insert a update net task by net id or ofc_id for each ofc thread
924 :param net_id: network id
925 :param ofc_id: openflow controller id
929 raise ovimException("No net_id received", HTTP_Internal_Server_Error
)
933 c
= 'No valid ofc_id or switch_dpid received'
936 ports
= self
.get_ports(filter={"net_id": net_id
})
938 port_ofc_id
= port
.get('ofc_id', None)
940 ofc_id
= port
['ofc_id']
941 switch_dpid
= port
['switch_dpid']
944 # If no ofc_id found it, default ofc_id is used.
945 if not ofc_id
and not switch_dpid
:
948 if ofc_id
and ofc_id
in self
.config
['ofcs_thread']:
949 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("update-net", net_id
)
952 ofcs_dpid_list
= self
.config
['ofcs_thread_dpid']
953 for ofc_t
in ofcs_dpid_list
:
954 if switch_dpid
in ofc_t
:
955 r
, c
= ofc_t
[switch_dpid
].insert_task("update-net", net_id
)
958 message
= "Cannot insert a task for updating network '{}', {}".format(net_id
, c
)
959 self
.logger
.error(message
)
960 raise ovimException(message
, HTTP_Internal_Server_Error
)
962 def delete_port(self
, port_id
):
963 # Look for the previous port data
964 result
, ports
= self
.db
.get_table(WHERE
={'uuid': port_id
, "type": "external"}, FROM
='ports')
966 raise ovimException("Cannot get port info from database: {}".format(ports
), http_code
=-result
)
967 # delete from the data base
968 result
, content
= self
.db
.delete_row('ports', port_id
)
970 raise ovimException("External port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
972 raise ovimException("Cannot delete port from database: {}".format(content
), http_code
=-result
)
974 network
= ports
[0].get('net_id', None)
979 self
.net_update_ofc_thread(network
)
980 except ovimException
as e
:
981 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
982 HTTP_Internal_Server_Error
)
983 except Exception as e
:
984 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
985 HTTP_Internal_Server_Error
)
989 def edit_port(self
, port_id
, port_data
, admin
=True):
990 # Look for the previous port data
991 result
, content
= self
.db
.get_table(FROM
="ports", WHERE
={'uuid': port_id
})
993 raise ovimException("Cannot get port info from database: {}".format(content
), http_code
=-result
)
995 raise ovimException("Port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
1000 if 'net_id' in port_data
:
1002 old_net
= port
.get('net_id', None)
1003 new_net
= port_data
['net_id']
1004 if old_net
!= new_net
:
1007 nets
.append(new_net
) # put first the new net, so that new openflow rules are created before removing the old ones
1009 nets
.append(old_net
)
1010 if port
['type'] == 'instance:bridge' or port
['type'] == 'instance:ovs':
1011 raise ovimException("bridge interfaces cannot be attached to a different net", http_code
=HTTP_Forbidden
)
1012 elif port
['type'] == 'external' and not admin
:
1013 raise ovimException("Needed admin privileges",http_code
=HTTP_Unauthorized
)
1015 # check that new net has the correct type
1016 result
, new_net_dict
= self
.db
.check_target_net(new_net
, None, port
['type'])
1018 raise ovimException("Error {}".format(new_net_dict
), http_code
=HTTP_Conflict
)
1019 # change VLAN for SR-IOV ports
1020 if result
>= 0 and port
["type"] == "instance:data" and port
["model"] == "VF": # TODO consider also VFnotShared
1022 port_data
["vlan"] = None
1024 port_data
["vlan"] = new_net_dict
["vlan"]
1025 # get host where this VM is allocated
1026 result
, content
= self
.db
.get_table(FROM
="instances", WHERE
={"uuid": port
["instance_id"]})
1028 host_id
= content
[0]["host_id"]
1030 # insert in data base
1032 result
, content
= self
.db
.update_rows('ports', port_data
, WHERE
={'uuid': port_id
}, log
=False)
1034 # Insert task to complete actions
1038 self
.net_update_ofc_thread(net_id
)
1039 except ovimException
as e
:
1040 raise ovimException("Error updating network'{}' {}".format(net_id
, str(e
)),
1041 HTTP_Internal_Server_Error
)
1042 except Exception as e
:
1043 raise ovimException("Error updating network '{}' {}".format(net_id
, str(e
)),
1044 HTTP_Internal_Server_Error
)
1047 r
, v
= self
.config
['host_threads'][host_id
].insert_task("edit-iface", port_id
, old_net
, new_net
)
1049 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
1050 # TODO Do something if fails
1054 raise ovimException("Error {}".format(content
), http_code
=-result
)
1056 def new_of_controller(self
, ofc_data
):
1058 Create a new openflow controller into DB
1059 :param ofc_data: Dict openflow controller data
1060 :return: openflow controller dpid
1063 result
, ofc_uuid
= self
.db
.new_row('ofcs', ofc_data
, True, True)
1065 raise ovimException("New ofc Error %s" % ofc_uuid
, HTTP_Internal_Server_Error
)
1067 ofc_data
['uuid'] = ofc_uuid
1068 of_conn
= self
._load
_of
_module
(ofc_data
)
1069 self
._create
_ofc
_task
(ofc_uuid
, ofc_data
['dpid'], of_conn
)
1073 def edit_of_controller(self
, of_id
, ofc_data
):
1075 Edit an openflow controller entry from DB
1079 raise ovimException("No data received during uptade OF contorller", http_code
=HTTP_Internal_Server_Error
)
1081 old_of_controller
= self
.show_of_controller(of_id
)
1083 if old_of_controller
:
1084 result
, content
= self
.db
.update_rows('ofcs', ofc_data
, WHERE
={'uuid': of_id
}, log
=False)
1088 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1091 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1092 http_code
=HTTP_Internal_Server_Error
)
1094 def delete_of_controller(self
, of_id
):
1096 Delete an openflow controller from DB.
1097 :param of_id: openflow controller dpid
1101 ofc
= self
.show_of_controller(of_id
)
1103 result
, content
= self
.db
.delete_row("ofcs", of_id
)
1105 raise ovimException("Cannot delete ofc from database: {}".format(content
), http_code
=-result
)
1107 raise ovimException("ofc {} not found ".format(content
), http_code
=HTTP_Not_Found
)
1109 ofc_thread
= self
.config
['ofcs_thread'][of_id
]
1110 del self
.config
['ofcs_thread'][of_id
]
1111 for ofc_th
in self
.config
['ofcs_thread_dpid']:
1112 if ofc
['dpid'] in ofc_th
:
1113 self
.config
['ofcs_thread_dpid'].remove(ofc_th
)
1115 ofc_thread
.insert_task("exit")
1120 def show_of_controller(self
, uuid
):
1122 Show an openflow controller by dpid from DB.
1123 :param db_filter: List with where query parameters
1127 result
, content
= self
.db
.get_table(FROM
='ofcs', WHERE
={"uuid": uuid
}, LIMIT
=100)
1130 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid
),
1131 http_code
=HTTP_Not_Found
)
1133 raise ovimException("Openflow controller with uuid '{}' error".format(uuid
),
1134 http_code
=HTTP_Internal_Server_Error
)
1137 def get_of_controllers(self
, columns
=None, db_filter
={}, limit
=None):
1139 Show an openflow controllers from DB.
1140 :param columns: List with SELECT query parameters
1141 :param db_filter: List with where query parameters
1142 :param limit: result Limit
1145 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='ofcs', WHERE
=db_filter
, LIMIT
=limit
)
1148 raise ovimException(str(content
), -result
)
1152 def get_tenants(self
, columns
=None, db_filter
={}, limit
=None):
1154 Retrieve tenant list from DB
1155 :param columns: List with SELECT query parameters
1156 :param db_filter: List with where query parameters
1157 :param limit: result limit
1160 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=columns
, WHERE
=db_filter
, LIMIT
=limit
)
1162 raise ovimException('get_tenatns Error {}'.format(str(content
)), -result
)
1164 convert_boolean(content
, ('enabled',))
1167 def show_tenant_id(self
, tenant_id
):
1169 Get tenant from DB by id
1170 :param tenant_id: tenant id
1173 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid', 'name', 'description', 'enabled'),
1174 WHERE
={"uuid": tenant_id
})
1176 raise ovimException(str(content
), -result
)
1178 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1180 convert_boolean(content
, ('enabled',))
1183 def new_tentant(self
, tenant
):
1185 Create a tenant and store in DB
1186 :param tenant: Dictionary with tenant data
1187 :return: the uuid of created tenant. Raise exception upon error
1190 # insert in data base
1191 result
, tenant_uuid
= self
.db
.new_tenant(tenant
)
1196 raise ovimException(str(tenant_uuid
), -result
)
1198 def delete_tentant(self
, tenant_id
):
1200 Delete a tenant from the database.
1201 :param tenant_id: Tenant id
1202 :return: delete tenant id
1206 r
, tenants_flavors
= self
.db
.get_table(FROM
='tenants_flavors', SELECT
=('flavor_id', 'tenant_id'),
1207 WHERE
={'tenant_id': tenant_id
})
1209 tenants_flavors
= ()
1210 r
, tenants_images
= self
.db
.get_table(FROM
='tenants_images', SELECT
=('image_id', 'tenant_id'),
1211 WHERE
={'tenant_id': tenant_id
})
1215 result
, content
= self
.db
.delete_row('tenants', tenant_id
)
1217 raise ovimException("tenant '%s' not found" % tenant_id
, HTTP_Not_Found
)
1219 for flavor
in tenants_flavors
:
1220 self
.db
.delete_row_by_key("flavors", "uuid", flavor
['flavor_id'])
1221 for image
in tenants_images
:
1222 self
.db
.delete_row_by_key("images", "uuid", image
['image_id'])
1225 raise ovimException("Error deleting tenant '%s' " % tenant_id
, HTTP_Internal_Server_Error
)
1227 def edit_tenant(self
, tenant_id
, tenant_data
):
1229 Update a tenant data identified by tenant id
1230 :param tenant_id: tenant id
1231 :param tenant_data: Dictionary with tenant data
1235 # Look for the previous data
1236 result
, tenant_data_old
= self
.db
.get_table(FROM
='tenants', WHERE
={'uuid': tenant_id
})
1238 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id
, tenant_data_old
),
1239 HTTP_Internal_Server_Error
)
1241 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1243 # insert in data base
1244 result
, content
= self
.db
.update_rows('tenants', tenant_data
, WHERE
={'uuid': tenant_id
}, log
=True)
1248 raise ovimException(str(content
), -result
)
1250 def set_of_port_mapping(self
, of_maps
, ofc_id
=None, switch_dpid
=None, region
=None):
1252 Create new port mapping entry
1253 :param of_maps: List with port mapping information
1254 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1255 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1256 :param ofc_id: ofc id
1257 :param switch_dpid: switch dpid
1258 :param region: datacenter region id
1264 map['ofc_id'] = ofc_id
1266 map['switch_dpid'] = switch_dpid
1268 map['region'] = region
1270 for of_map
in of_maps
:
1271 result
, uuid
= self
.db
.new_row('of_port_mappings', of_map
, True)
1273 of_map
["uuid"] = uuid
1275 raise ovimException(str(uuid
), -result
)
1278 def clear_of_port_mapping(self
, db_filter
={}):
1280 Clear port mapping filtering using db_filter dict
1281 :param db_filter: Parameter to filter during remove process
1284 result
, content
= self
.db
.delete_row_by_dict(FROM
='of_port_mappings', WHERE
=db_filter
)
1289 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter
)),
1290 HTTP_Internal_Server_Error
)
1292 def get_of_port_mappings(self
, column
=None, db_filter
=None, db_limit
=None):
1294 Retrive port mapping from DB
1299 result
, content
= self
.db
.get_table(SELECT
=column
, WHERE
=db_filter
, FROM
='of_port_mappings', LIMIT
=db_limit
)
1302 self
.logger
.error("get_of_port_mappings Error %d %s", result
, content
)
1303 raise ovimException(str(content
), -result
)
1307 def get_dhcp_controller(self
):
1309 Create an host_thread object for manage openvim controller and not create a thread for itself
1310 :return: dhcp_host openvim controller object
1313 if 'openvim_controller' in self
.config
['host_threads']:
1314 return self
.config
['host_threads']['openvim_controller']
1317 controller_ip
= self
.config
['ovs_controller_ip']
1318 ovs_controller_user
= self
.config
['ovs_controller_user']
1320 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
1321 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
1323 dhcp_host
= ht
.host_thread(name
='openvim_controller', user
=ovs_controller_user
, host
=controller_ip
,
1325 db_lock
=self
.db_lock
, test
=host_test_mode
,
1326 image_path
=self
.config
['image_path'], version
=self
.config
['version'],
1327 host_id
='openvim_controller', develop_mode
=host_develop_mode
,
1328 develop_bridge_iface
=bridge_ifaces
)
1330 self
.config
['host_threads']['openvim_controller'] = dhcp_host
1331 if not host_test_mode
:
1332 dhcp_host
.ssh_connect()
1335 def launch_dhcp_server(self
, vlan
, first_ip
, last_ip
, cidr
, gateway
):
1337 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1338 :param vlan: vlan identifier
1339 :param first_ip: First dhcp range ip
1340 :param last_ip: Last dhcp range ip
1341 :param cidr: net cidr
1342 :param gateway: net gateway
1345 ip_tools
= IPNetwork(cidr
)
1346 dhcp_netmask
= str(ip_tools
.netmask
)
1347 ip_range
= [first_ip
, last_ip
]
1349 dhcp_path
= self
.config
['ovs_controller_file_path']
1351 controller_host
= self
.get_dhcp_controller()
1352 controller_host
.create_linux_bridge(vlan
)
1353 controller_host
.create_dhcp_interfaces(vlan
, first_ip
, dhcp_netmask
)
1354 controller_host
.launch_dhcp_server(vlan
, ip_range
, dhcp_netmask
, dhcp_path
, gateway
)