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']
354 temp_dict
['of_user'] = db_config
['user']
355 temp_dict
['of_password'] = db_config
['password']
357 temp_dict
['of_debug'] = self
.config
['log_level_of']
359 if temp_dict
['of_controller'] == 'opendaylight':
362 module
= temp_dict
['of_controller']
364 if module
not in ovim
.of_module
:
365 module_info
= imp
.find_module(module
)
366 of_conn_module
= imp
.load_module("OF_conn", *module_info
)
367 ovim
.of_module
[module
] = of_conn_module
369 of_conn_module
= ovim
.of_module
[module
]
372 return of_conn_module
.OF_conn(temp_dict
)
373 except Exception as e
:
374 self
.logger
.error("Cannot open the Openflow controller '%s': %s", type(e
).__name
__, str(e
))
375 if module_info
and module_info
[0]:
376 file.close(module_info
[0])
377 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e
).__name
__, str(e
)),
378 HTTP_Internal_Server_Error
)
379 except (IOError, ImportError) as e
:
380 if module_info
and module_info
[0]:
381 file.close(module_info
[0])
382 self
.logger
.error("Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' "
383 "field of configuration file.", module
, type(e
).__name
__, str(e
))
384 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' "
385 "field of configuration file.".format(module
, type(e
).__name
__, str(e
)),
386 HTTP_Internal_Server_Error
)
388 def _create_ofc_thread(self
, of_conn
, ofc_uuid
="Default"):
390 Create and launch a of thread
393 # create openflow thread
395 if 'of_controller_nets_with_same_vlan' in self
.config
:
396 ofc_net_same_vlan
= self
.config
['of_controller_nets_with_same_vlan']
398 ofc_net_same_vlan
= False
400 thread
= oft
.openflow_thread(ofc_uuid
, of_conn
, of_test
=self
.of_test_mode
, db
=self
.db_of
, db_lock
=self
.db_lock
,
401 pmp_with_same_vlan
=ofc_net_same_vlan
, debug
=self
.config
['log_level_of'])
402 #r, c = thread.OF_connector.obtain_port_correspondence()
404 # raise ovimException("Cannot get openflow information %s", c)
408 def stop_service(self
):
409 threads
= self
.config
.get('host_threads', {})
410 if 'of_thread' in self
.config
:
411 threads
['of'] = (self
.config
['of_thread'])
412 if 'ofcs_thread' in self
.config
:
413 ofcs_thread
= self
.config
['ofcs_thread']
414 for ofc
in ofcs_thread
:
415 threads
[ofc
] = ofcs_thread
[ofc
]
417 if 'dhcp_thread' in self
.config
:
418 threads
['dhcp'] = (self
.config
['dhcp_thread'])
420 for thread
in threads
.values():
421 thread
.insert_task("exit")
422 for thread
in threads
.values():
425 def get_networks(self
, columns
=None, db_filter
={}, limit
=None):
427 Retreive networks available
428 :param columns: List with select query parameters
429 :param db_filter: List with where query parameters
430 :param limit: Query limit result
433 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='nets', WHERE
=db_filter
, LIMIT
=limit
)
436 raise ovimException(str(content
), -result
)
438 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
442 def show_network(self
, network_id
, db_filter
={}):
444 Get network from DB by id
445 :param network_id: net Id
446 :param db_filter: List with where query parameters
451 raise ovimException("Not network id was not found")
452 db_filter
['uuid'] = network_id
454 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
=db_filter
, LIMIT
=100)
457 raise ovimException(str(content
), -result
)
459 raise ovimException("show_network network '%s' not found" % network_id
, -result
)
461 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
463 result
, ports
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
464 WHERE
={'net_id': network_id
}, LIMIT
=100)
466 content
[0]['ports'] = ports
468 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
471 def new_network(self
, network
):
476 tenant_id
= network
.get('tenant_id')
479 result
, _
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
, "enabled": True})
481 raise ovimException("set_network error, no tenant founded", -result
)
485 net_provider
= network
.get('provider')
486 net_type
= network
.get('type')
487 net_vlan
= network
.get("vlan")
488 net_bind_net
= network
.get("bind_net")
489 net_bind_type
= network
.get("bind_type")
490 name
= network
["name"]
492 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
493 vlan_index
= name
.rfind(":")
494 if not net_bind_net
and not net_bind_type
and vlan_index
> 1:
496 vlan_tag
= int(name
[vlan_index
+ 1:])
497 if not vlan_tag
and vlan_tag
< 4096:
498 net_bind_net
= name
[:vlan_index
]
499 net_bind_type
= "vlan:" + name
[vlan_index
+ 1:]
504 # look for a valid net
505 if self
._check
_valid
_uuid
(net_bind_net
):
506 net_bind_key
= "uuid"
508 net_bind_key
= "name"
509 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
511 raise ovimException(' getting nets from db ' + content
, HTTP_Internal_Server_Error
)
513 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
515 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
516 network
["bind_net"] = content
[0]["uuid"]
519 if net_bind_type
[0:5] != "vlan:":
520 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
521 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
522 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
524 network
["bind_type"] = net_bind_type
527 if net_provider
[:9] == "openflow:":
529 if net_type
!= "ptp" and net_type
!= "data":
530 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
536 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
537 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
538 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
540 net_type
= 'bridge_man'
543 net_type
= 'bridge_man'
546 if net_provider
[:7] == 'bridge:':
547 # check it is one of the pre-provisioned bridges
548 bridge_net_name
= net_provider
[7:]
549 for brnet
in self
.config
['bridge_nets']:
550 if brnet
[0] == bridge_net_name
: # free
552 raise ovimException("invalid 'provider:physical', "
553 "bridge '%s' is already used" % bridge_net_name
, HTTP_Conflict
)
557 # if bridge_net==None:
558 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
559 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
562 elif self
.config
['network_type'] == 'bridge' and (net_type
== 'bridge_data' or net_type
== 'bridge_man'):
563 # look for a free precreated nets
564 for brnet
in self
.config
['bridge_nets']:
565 if not brnet
[3]: # free
567 if net_type
== 'bridge_man': # look for the smaller speed
568 if brnet
[2] < bridge_net
[2]:
570 else: # look for the larger speed
571 if brnet
[2] > bridge_net
[2]:
577 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
578 "will overcome this limit", HTTP_Bad_Request
)
580 self
.logger
.debug("using net " + bridge_net
)
581 net_provider
= "bridge:" + bridge_net
[0]
582 net_vlan
= bridge_net
[1]
583 elif net_type
== 'bridge_data' or net_type
== 'bridge_man' and self
.config
['network_type'] == 'ovs':
585 if not net_vlan
and (net_type
== "data" or net_type
== "ptp" or net_provider
== "OVS"):
586 net_vlan
= self
.db
.get_free_net_vlan()
588 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error
)
589 if net_provider
== 'OVS':
590 net_provider
= 'OVS' + ":" + str(net_vlan
)
592 network
['provider'] = net_provider
593 network
['type'] = net_type
594 network
['vlan'] = net_vlan
595 dhcp_integrity
= True
596 if 'enable_dhcp' in network
and network
['enable_dhcp']:
597 dhcp_integrity
= self
._check
_dhcp
_data
_integrity
(network
)
599 result
, content
= self
.db
.new_row('nets', network
, True, True)
601 if result
>= 0 and dhcp_integrity
:
603 bridge_net
[3] = content
604 if self
.config
.get("dhcp_server") and self
.config
['network_type'] == 'bridge':
605 if network
["name"] in self
.config
["dhcp_server"].get("nets", ()):
606 self
.config
["dhcp_nets"].append(content
)
607 self
.logger
.debug("dhcp_server: add new net", content
)
608 elif not bridge_net
and bridge_net
[0] in self
.config
["dhcp_server"].get("bridge_ifaces", ()):
609 self
.config
["dhcp_nets"].append(content
)
610 self
.logger
.debug("dhcp_server: add new net", content
, content
)
613 raise ovimException("Error posting network", HTTP_Internal_Server_Error
)
614 # TODO kei change update->edit
616 def edit_network(self
, network_id
, network
):
618 Update entwork data byt id
621 # Look for the previous data
622 where_
= {'uuid': network_id
}
623 result
, network_old
= self
.db
.get_table(FROM
='nets', WHERE
=where_
)
625 raise ovimException("Error updating network %s" % network_old
, HTTP_Internal_Server_Error
)
627 raise ovimException('network %s not found' % network_id
, HTTP_Not_Found
)
629 nbports
, content
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
630 WHERE
={'net_id': network_id
}, LIMIT
=100)
632 raise ovimException("http_put_network_id error %d %s" % (result
, network_old
), HTTP_Internal_Server_Error
)
634 if 'type' in network
and network
['type'] != network_old
[0]['type']:
635 raise ovimException("Can not change type of network while having ports attached",
636 HTTP_Method_Not_Allowed
)
637 if 'vlan' in network
and network
['vlan'] != network_old
[0]['vlan']:
638 raise ovimException("Can not change vlan of network while having ports attached",
639 HTTP_Method_Not_Allowed
)
642 net_provider
= network
.get('provider', network_old
[0]['provider'])
643 net_type
= network
.get('type', network_old
[0]['type'])
644 net_bind_net
= network
.get("bind_net")
645 net_bind_type
= network
.get("bind_type")
647 # look for a valid net
648 if self
._check
_valid
_uuid
(net_bind_net
):
649 net_bind_key
= "uuid"
651 net_bind_key
= "name"
652 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
654 raise ovimException('Getting nets from db ' + content
, HTTP_Internal_Server_Error
)
656 raise ovimException("bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
658 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
),
660 network
["bind_net"] = content
[0]["uuid"]
662 if net_bind_type
[0:5] != "vlan:":
663 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
664 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
665 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
668 if net_provider
[:9] == "openflow:":
669 if net_type
!= "ptp" and net_type
!= "data":
670 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request
)
672 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
673 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
674 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
676 # insert in data base
677 result
, content
= self
.db
.update_rows('nets', network
, WHERE
={'uuid': network_id
}, log
=True)
679 # if result > 0 and nbports>0 and 'admin_state_up' in network
680 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
685 self
.net_update_ofc_thread(network_id
)
686 except ovimException
as e
:
687 raise ovimException("Error while launching openflow rules in network '{}' {}"
688 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
689 except Exception as e
:
690 raise ovimException("Error while launching openflow rules in network '{}' {}"
691 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
693 if self
.config
.get("dhcp_server"):
694 if network_id
in self
.config
["dhcp_nets"]:
695 self
.config
["dhcp_nets"].remove(network_id
)
696 if network
.get("name", network_old
[0]["name"]) in self
.config
["dhcp_server"].get("nets", ()):
697 self
.config
["dhcp_nets"].append(network_id
)
699 net_bind
= network
.get("bind_type", network_old
[0]["bind_type"])
700 if net_bind
and net_bind
and net_bind
[:7] == "bridge:" and net_bind
[7:] in self
.config
["dhcp_server"].get(
701 "bridge_ifaces", ()):
702 self
.config
["dhcp_nets"].append(network_id
)
705 raise ovimException(content
, -result
)
707 def delete_network(self
, network_id
):
709 Delete network by network id
710 :param network_id: network id
714 # delete from the data base
715 result
, content
= self
.db
.delete_row('nets', network_id
)
718 raise ovimException("Network %s not found " % network_id
, HTTP_Not_Found
)
720 for brnet
in self
.config
['bridge_nets']:
721 if brnet
[3] == network_id
:
724 if self
.config
.get("dhcp_server") and network_id
in self
.config
["dhcp_nets"]:
725 self
.config
["dhcp_nets"].remove(network_id
)
728 raise ovimException("Error deleting network %s" % network_id
, HTTP_Internal_Server_Error
)
730 def get_openflow_rules(self
, network_id
=None):
732 Get openflow id from DB
733 :param network_id: Network id, if none all networks will be retrieved
734 :return: Return a list with Openflow rules per net
740 where_
= {"net_id": network_id
}
741 result
, content
= self
.db
.get_table(
742 SELECT
=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
743 WHERE
=where_
, FROM
='of_flows')
746 raise ovimException(str(content
), -result
)
749 def edit_openflow_rules(self
, network_id
=None):
752 To make actions over the net. The action is to reinstall the openflow rules
753 network_id can be 'all'
754 :param network_id: Network id, if none all networks will be retrieved
755 :return : Number of nets updated
762 where_
= {"uuid": network_id
}
763 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
766 raise ovimException(str(content
), -result
)
769 if net
["type"] != "ptp" and net
["type"] != "data":
774 self
.net_update_ofc_thread(net
['uuid'])
775 except ovimException
as e
:
776 raise ovimException("Error updating network'{}' {}".format(net
['uuid'], str(e
)),
777 HTTP_Internal_Server_Error
)
778 except Exception as e
:
779 raise ovimException("Error updating network '{}' {}".format(net
['uuid'], str(e
)),
780 HTTP_Internal_Server_Error
)
784 def delete_openflow_rules(self
, ofc_id
=None):
786 To make actions over the net. The action is to delete ALL openflow rules
787 :return: return operation result
791 if 'Default' in self
.config
['ofcs_thread']:
792 r
, c
= self
.config
['ofcs_thread']['Default'].insert_task("clear-all")
794 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
796 elif ofc_id
in self
.config
['ofcs_thread']:
797 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("clear-all")
801 raise ovimException(str(c
), -r
)
803 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
806 def get_openflow_ports(self
, ofc_id
=None):
808 Obtain switch ports names of openflow controller
809 :return: Return flow ports in DB
812 if 'Default' in self
.config
['ofcs_thread']:
813 conn
= self
.config
['ofcs_thread']['Default'].OF_connector
815 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
817 if ofc_id
in self
.config
['ofcs_thread']:
818 conn
= self
.config
['ofcs_thread'][ofc_id
].OF_connector
820 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
823 def get_ports(self
, columns
=None, filter={}, limit
=None):
824 # result, content = my.db.get_ports(where_)
825 result
, content
= self
.db
.get_table(SELECT
=columns
, WHERE
=filter, FROM
='ports', LIMIT
=limit
)
827 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
828 raise ovimException(str(content
), -result
)
830 convert_boolean(content
, ('admin_state_up',))
833 def new_port(self
, port_data
):
834 port_data
['type'] = 'external'
835 if port_data
.get('net_id'):
836 # check that new net has the correct type
837 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
839 raise ovimException(str(new_net
), -result
)
840 # insert in data base
841 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
843 if 'net_id' in port_data
:
845 self
.net_update_ofc_thread(port_data
['net_id'])
846 except ovimException
as e
:
847 raise ovimException("Cannot insert a task for updating network '{}' {}"
848 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
849 except Exception as e
:
850 raise ovimException("Cannot insert a task for updating network '{}' {}"
851 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
855 raise ovimException(str(uuid
), -result
)
857 def new_external_port(self
, port_data
):
859 Create new external port and check port mapping correspondence
860 :param port_data: port_data = {
861 'region': 'datacenter region',
862 'compute_node': 'compute node id',
863 'pci': 'pci port address',
866 'tenant_id': 'tenant id',
869 'ip_address': 'ip address - optional'}
873 port_data
['type'] = 'external'
875 if port_data
.get('net_id'):
876 # check that new net has the correct type
877 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
879 raise ovimException(str(new_net
), -result
)
880 # insert in data base
883 if port_data
.get('region'):
884 db_filter
['region'] = port_data
['region']
885 if port_data
.get('pci'):
886 db_filter
['pci'] = port_data
['pci']
887 if port_data
.get('compute_node'):
888 db_filter
['compute_node'] = port_data
['compute_node']
890 columns
= ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
891 port_mapping_data
= self
.get_of_port_mappings(columns
, db_filter
)
893 if not len(port_mapping_data
):
894 raise ovimException("No port mapping founded for '{}'".format(str(db_filter
)),
896 elif len(port_mapping_data
) > 1:
897 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
900 port_data
['ofc_id'] = port_mapping_data
[0]['ofc_id']
901 port_data
['switch_dpid'] = port_mapping_data
[0]['switch_dpid']
902 port_data
['switch_port'] = port_mapping_data
[0]['switch_port']
903 port_data
['switch_mac'] = port_mapping_data
[0]['switch_mac']
905 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
906 if 'region' in port_data
:
907 del port_data
['region']
908 if 'pci' in port_data
:
910 if 'compute_node' in port_data
:
911 del port_data
['compute_node']
913 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
916 self
.net_update_ofc_thread(port_data
['net_id'], port_data
['ofc_id'])
917 except ovimException
as e
:
918 raise ovimException("Cannot insert a task for updating network '{}' {}".
919 format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
920 except Exception as e
:
921 raise ovimException("Cannot insert a task for updating network '{}' {}"
922 .format(port_data
['net_id'], e
), HTTP_Internal_Server_Error
)
925 raise ovimException(str(uuid
), -result
)
927 def net_update_ofc_thread(self
, net_id
, ofc_id
=None, switch_dpid
=None):
929 Insert a update net task by net id or ofc_id for each ofc thread
930 :param net_id: network id
931 :param ofc_id: openflow controller id
932 :param switch_dpid: switch dpid
936 raise ovimException("No net_id received", HTTP_Internal_Server_Error
)
939 c
= 'No valid ofc_id or switch_dpid received'
942 ports
= self
.get_ports(filter={"net_id": net_id
})
944 port_ofc_id
= port
.get('ofc_id', None)
946 ofc_id
= port
['ofc_id']
947 switch_dpid
= port
['switch_dpid']
949 #TODO if not ofc_id: look at database table ofcs
952 # If no ofc_id found it, default ofc_id is used.
953 if not ofc_id
and not switch_dpid
:
956 if ofc_id
and ofc_id
in self
.config
['ofcs_thread']:
957 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("update-net", net_id
)
960 ofcs_dpid_list
= self
.config
['ofcs_thread_dpid']
961 for ofc_t
in ofcs_dpid_list
:
962 if switch_dpid
in ofc_t
:
963 r
, c
= ofc_t
[switch_dpid
].insert_task("update-net", net_id
)
966 message
= "Cannot insert a task for updating network '{}', {}".format(net_id
, c
)
967 self
.logger
.error(message
)
968 raise ovimException(message
, HTTP_Internal_Server_Error
)
970 def delete_port(self
, port_id
):
971 # Look for the previous port data
972 result
, ports
= self
.db
.get_table(WHERE
={'uuid': port_id
, "type": "external"}, FROM
='ports')
974 raise ovimException("Cannot get port info from database: {}".format(ports
), http_code
=-result
)
975 # delete from the data base
976 result
, content
= self
.db
.delete_row('ports', port_id
)
978 raise ovimException("External port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
980 raise ovimException("Cannot delete port from database: {}".format(content
), http_code
=-result
)
982 network
= ports
[0].get('net_id', None)
987 self
.net_update_ofc_thread(network
, ofc_id
=ports
[0]["ofc_id"], switch_dpid
=ports
[0]["switch_dpid"])
988 except ovimException
as e
:
989 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
990 HTTP_Internal_Server_Error
)
991 except Exception as e
:
992 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
993 HTTP_Internal_Server_Error
)
997 def edit_port(self
, port_id
, port_data
, admin
=True):
998 # Look for the previous port data
999 result
, content
= self
.db
.get_table(FROM
="ports", WHERE
={'uuid': port_id
})
1001 raise ovimException("Cannot get port info from database: {}".format(content
), http_code
=-result
)
1003 raise ovimException("Port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
1008 if 'net_id' in port_data
:
1010 old_net
= port
.get('net_id', None)
1011 new_net
= port_data
['net_id']
1012 if old_net
!= new_net
:
1015 nets
.append(new_net
) # put first the new net, so that new openflow rules are created before removing the old ones
1017 nets
.append(old_net
)
1018 if port
['type'] == 'instance:bridge' or port
['type'] == 'instance:ovs':
1019 raise ovimException("bridge interfaces cannot be attached to a different net", http_code
=HTTP_Forbidden
)
1020 elif port
['type'] == 'external' and not admin
:
1021 raise ovimException("Needed admin privileges",http_code
=HTTP_Unauthorized
)
1023 # check that new net has the correct type
1024 result
, new_net_dict
= self
.db
.check_target_net(new_net
, None, port
['type'])
1026 raise ovimException("Error {}".format(new_net_dict
), http_code
=HTTP_Conflict
)
1027 # change VLAN for SR-IOV ports
1028 if result
>= 0 and port
["type"] == "instance:data" and port
["model"] == "VF": # TODO consider also VFnotShared
1030 port_data
["vlan"] = None
1032 port_data
["vlan"] = new_net_dict
["vlan"]
1033 # get host where this VM is allocated
1034 result
, content
= self
.db
.get_table(FROM
="instances", WHERE
={"uuid": port
["instance_id"]})
1036 host_id
= content
[0]["host_id"]
1038 # insert in data base
1040 result
, content
= self
.db
.update_rows('ports', port_data
, WHERE
={'uuid': port_id
}, log
=False)
1041 port
.update(port_data
)
1043 # Insert task to complete actions
1047 self
.net_update_ofc_thread(net_id
, port
["ofc_id"], switch_dpid
=port
["switch_dpid"])
1048 except ovimException
as e
:
1049 raise ovimException("Error updating network'{}' {}".format(net_id
, str(e
)),
1050 HTTP_Internal_Server_Error
)
1051 except Exception as e
:
1052 raise ovimException("Error updating network '{}' {}".format(net_id
, str(e
)),
1053 HTTP_Internal_Server_Error
)
1056 r
, v
= self
.config
['host_threads'][host_id
].insert_task("edit-iface", port_id
, old_net
, new_net
)
1058 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
1059 # TODO Do something if fails
1063 raise ovimException("Error {}".format(content
), http_code
=-result
)
1065 def new_of_controller(self
, ofc_data
):
1067 Create a new openflow controller into DB
1068 :param ofc_data: Dict openflow controller data
1069 :return: openflow controller dpid
1072 result
, ofc_uuid
= self
.db
.new_row('ofcs', ofc_data
, True, True)
1074 raise ovimException("New ofc Error %s" % ofc_uuid
, HTTP_Internal_Server_Error
)
1076 ofc_data
['uuid'] = ofc_uuid
1077 of_conn
= self
._load
_of
_module
(ofc_data
)
1078 self
._create
_ofc
_task
(ofc_uuid
, ofc_data
['dpid'], of_conn
)
1082 def edit_of_controller(self
, of_id
, ofc_data
):
1084 Edit an openflow controller entry from DB
1088 raise ovimException("No data received during uptade OF contorller", http_code
=HTTP_Internal_Server_Error
)
1090 old_of_controller
= self
.show_of_controller(of_id
)
1092 if old_of_controller
:
1093 result
, content
= self
.db
.update_rows('ofcs', ofc_data
, WHERE
={'uuid': of_id
}, log
=False)
1097 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1100 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1101 http_code
=HTTP_Internal_Server_Error
)
1103 def delete_of_controller(self
, of_id
):
1105 Delete an openflow controller from DB.
1106 :param of_id: openflow controller dpid
1110 ofc
= self
.show_of_controller(of_id
)
1112 result
, content
= self
.db
.delete_row("ofcs", of_id
)
1114 raise ovimException("Cannot delete ofc from database: {}".format(content
), http_code
=-result
)
1116 raise ovimException("ofc {} not found ".format(content
), http_code
=HTTP_Not_Found
)
1118 ofc_thread
= self
.config
['ofcs_thread'][of_id
]
1119 del self
.config
['ofcs_thread'][of_id
]
1120 for ofc_th
in self
.config
['ofcs_thread_dpid']:
1121 if ofc
['dpid'] in ofc_th
:
1122 self
.config
['ofcs_thread_dpid'].remove(ofc_th
)
1124 ofc_thread
.insert_task("exit")
1129 def show_of_controller(self
, uuid
):
1131 Show an openflow controller by dpid from DB.
1132 :param db_filter: List with where query parameters
1136 result
, content
= self
.db
.get_table(FROM
='ofcs', WHERE
={"uuid": uuid
}, LIMIT
=100)
1139 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid
),
1140 http_code
=HTTP_Not_Found
)
1142 raise ovimException("Openflow controller with uuid '{}' error".format(uuid
),
1143 http_code
=HTTP_Internal_Server_Error
)
1146 def get_of_controllers(self
, columns
=None, db_filter
={}, limit
=None):
1148 Show an openflow controllers from DB.
1149 :param columns: List with SELECT query parameters
1150 :param db_filter: List with where query parameters
1151 :param limit: result Limit
1154 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='ofcs', WHERE
=db_filter
, LIMIT
=limit
)
1157 raise ovimException(str(content
), -result
)
1161 def get_tenants(self
, columns
=None, db_filter
={}, limit
=None):
1163 Retrieve tenant list from DB
1164 :param columns: List with SELECT query parameters
1165 :param db_filter: List with where query parameters
1166 :param limit: result limit
1169 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=columns
, WHERE
=db_filter
, LIMIT
=limit
)
1171 raise ovimException('get_tenatns Error {}'.format(str(content
)), -result
)
1173 convert_boolean(content
, ('enabled',))
1176 def show_tenant_id(self
, tenant_id
):
1178 Get tenant from DB by id
1179 :param tenant_id: tenant id
1182 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid', 'name', 'description', 'enabled'),
1183 WHERE
={"uuid": tenant_id
})
1185 raise ovimException(str(content
), -result
)
1187 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1189 convert_boolean(content
, ('enabled',))
1192 def new_tentant(self
, tenant
):
1194 Create a tenant and store in DB
1195 :param tenant: Dictionary with tenant data
1196 :return: the uuid of created tenant. Raise exception upon error
1199 # insert in data base
1200 result
, tenant_uuid
= self
.db
.new_tenant(tenant
)
1205 raise ovimException(str(tenant_uuid
), -result
)
1207 def delete_tentant(self
, tenant_id
):
1209 Delete a tenant from the database.
1210 :param tenant_id: Tenant id
1211 :return: delete tenant id
1215 r
, tenants_flavors
= self
.db
.get_table(FROM
='tenants_flavors', SELECT
=('flavor_id', 'tenant_id'),
1216 WHERE
={'tenant_id': tenant_id
})
1218 tenants_flavors
= ()
1219 r
, tenants_images
= self
.db
.get_table(FROM
='tenants_images', SELECT
=('image_id', 'tenant_id'),
1220 WHERE
={'tenant_id': tenant_id
})
1224 result
, content
= self
.db
.delete_row('tenants', tenant_id
)
1226 raise ovimException("tenant '%s' not found" % tenant_id
, HTTP_Not_Found
)
1228 for flavor
in tenants_flavors
:
1229 self
.db
.delete_row_by_key("flavors", "uuid", flavor
['flavor_id'])
1230 for image
in tenants_images
:
1231 self
.db
.delete_row_by_key("images", "uuid", image
['image_id'])
1234 raise ovimException("Error deleting tenant '%s' " % tenant_id
, HTTP_Internal_Server_Error
)
1236 def edit_tenant(self
, tenant_id
, tenant_data
):
1238 Update a tenant data identified by tenant id
1239 :param tenant_id: tenant id
1240 :param tenant_data: Dictionary with tenant data
1244 # Look for the previous data
1245 result
, tenant_data_old
= self
.db
.get_table(FROM
='tenants', WHERE
={'uuid': tenant_id
})
1247 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id
, tenant_data_old
),
1248 HTTP_Internal_Server_Error
)
1250 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1252 # insert in data base
1253 result
, content
= self
.db
.update_rows('tenants', tenant_data
, WHERE
={'uuid': tenant_id
}, log
=True)
1257 raise ovimException(str(content
), -result
)
1259 def set_of_port_mapping(self
, of_maps
, ofc_id
=None, switch_dpid
=None, region
=None):
1261 Create new port mapping entry
1262 :param of_maps: List with port mapping information
1263 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1264 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1265 :param ofc_id: ofc id
1266 :param switch_dpid: switch dpid
1267 :param region: datacenter region id
1273 map['ofc_id'] = ofc_id
1275 map['switch_dpid'] = switch_dpid
1277 map['region'] = region
1279 for of_map
in of_maps
:
1280 result
, uuid
= self
.db
.new_row('of_port_mappings', of_map
, True)
1282 of_map
["uuid"] = uuid
1284 raise ovimException(str(uuid
), -result
)
1287 def clear_of_port_mapping(self
, db_filter
={}):
1289 Clear port mapping filtering using db_filter dict
1290 :param db_filter: Parameter to filter during remove process
1293 result
, content
= self
.db
.delete_row_by_dict(FROM
='of_port_mappings', WHERE
=db_filter
)
1298 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter
)),
1299 HTTP_Internal_Server_Error
)
1301 def get_of_port_mappings(self
, column
=None, db_filter
=None, db_limit
=None):
1303 Retrive port mapping from DB
1308 result
, content
= self
.db
.get_table(SELECT
=column
, WHERE
=db_filter
, FROM
='of_port_mappings', LIMIT
=db_limit
)
1311 self
.logger
.error("get_of_port_mappings Error %d %s", result
, content
)
1312 raise ovimException(str(content
), -result
)
1316 def get_dhcp_controller(self
):
1318 Create an host_thread object for manage openvim controller and not create a thread for itself
1319 :return: dhcp_host openvim controller object
1322 if 'openvim_controller' in self
.config
['host_threads']:
1323 return self
.config
['host_threads']['openvim_controller']
1326 controller_ip
= self
.config
['ovs_controller_ip']
1327 ovs_controller_user
= self
.config
['ovs_controller_user']
1329 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
1330 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
1332 dhcp_host
= ht
.host_thread(name
='openvim_controller', user
=ovs_controller_user
, host
=controller_ip
,
1334 db_lock
=self
.db_lock
, test
=host_test_mode
,
1335 image_path
=self
.config
['image_path'], version
=self
.config
['version'],
1336 host_id
='openvim_controller', develop_mode
=host_develop_mode
,
1337 develop_bridge_iface
=bridge_ifaces
)
1339 self
.config
['host_threads']['openvim_controller'] = dhcp_host
1340 if not host_test_mode
:
1341 dhcp_host
.ssh_connect()
1344 def launch_dhcp_server(self
, vlan
, first_ip
, last_ip
, cidr
, gateway
):
1346 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1347 :param vlan: vlan identifier
1348 :param first_ip: First dhcp range ip
1349 :param last_ip: Last dhcp range ip
1350 :param cidr: net cidr
1351 :param gateway: net gateway
1354 ip_tools
= IPNetwork(cidr
)
1355 dhcp_netmask
= str(ip_tools
.netmask
)
1356 ip_range
= [first_ip
, last_ip
]
1358 dhcp_path
= self
.config
['ovs_controller_file_path']
1360 controller_host
= self
.get_dhcp_controller()
1361 controller_host
.create_linux_bridge(vlan
)
1362 controller_host
.create_dhcp_interfaces(vlan
, first_ip
, dhcp_netmask
)
1363 controller_host
.launch_dhcp_server(vlan
, ip_range
, dhcp_netmask
, dhcp_path
, gateway
)