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
47 HTTP_Bad_Request
= 400
48 HTTP_Unauthorized
= 401
51 HTTP_Method_Not_Allowed
= 405
52 HTTP_Not_Acceptable
= 406
53 HTTP_Request_Timeout
= 408
55 HTTP_Service_Unavailable
= 503
56 HTTP_Internal_Server_Error
= 500
59 def convert_boolean(data
, items
):
60 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
61 It assumes that bandwidth is well formed
63 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
64 'items': tuple of keys to convert
68 if type(data
) is dict:
70 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
71 convert_boolean(data
[k
], items
)
73 if type(data
[k
]) is str:
74 if data
[k
] == "false":
76 elif data
[k
] == "true":
78 if type(data
) is tuple or type(data
) is list:
80 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
81 convert_boolean(k
, items
)
85 class ovimException(Exception):
86 def __init__(self
, message
, http_code
=HTTP_Bad_Request
):
87 self
.http_code
= http_code
88 Exception.__init
__(self
, message
)
92 running_info
= {} #TODO OVIM move the info of running threads from config_dic to this static variable
95 def __init__(self
, configuration
):
96 self
.config
= configuration
97 self
.logger
= logging
.getLogger(configuration
["logger_name"])
99 self
.db
= self
._create
_database
_connection
()
102 self
.of_test_mode
= False
104 def _create_database_connection(self
):
105 db
= vim_db
.vim_db((self
.config
["network_vlan_range_start"], self
.config
["network_vlan_range_end"]),
106 self
.config
['log_level_db']);
107 if db
.connect(self
.config
['db_host'], self
.config
['db_user'], self
.config
['db_passwd'],
108 self
.config
['db_name']) == -1:
109 # self.logger.error("Cannot connect to database %s at %s@%s", self.config['db_name'], self.config['db_user'],
110 # self.config['db_host'])
111 raise ovimException("Cannot connect to database {} at {}@{}".format(self
.config
['db_name'],
112 self
.config
['db_user'],
113 self
.config
['db_host']) )
121 def get_version_date():
125 def get_database_version():
126 return database_version
129 def _check_dhcp_data_integrity(network
):
131 Check if all dhcp parameter for anet are valid, if not will be calculated from cidr value
132 :param network: list with user nets paramters
135 if "cidr" in network
:
136 cidr
= network
["cidr"]
137 ip_tools
= IPNetwork(cidr
)
138 cidr_len
= ip_tools
.prefixlen
142 ips
= IPNetwork(cidr
)
143 if "dhcp_first_ip" not in network
:
144 network
["dhcp_first_ip"] = str(ips
[2])
145 if "dhcp_last_ip" not in network
:
146 network
["dhcp_last_ip"] = str(ips
[-2])
147 if "gateway_ip" not in network
:
148 network
["gateway_ip"] = str(ips
[1])
155 def _check_valid_uuid(uuid
):
156 id_schema
= {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
158 js_v(uuid
, id_schema
)
160 except js_e
.ValidationError
:
163 def start_service(self
):
168 global database_version
169 # if self.running_info:
170 # return #TODO service can be checked and rebuild broken threads
171 r
= self
.db
.get_db_version()
173 raise ovimException("DATABASE is not a VIM one or it is a '0.0' version. Try to upgrade to version '{}' with "\
174 "'./database_utils/migrate_vim_db.sh'".format(database_version
) )
175 elif r
[1] != database_version
:
176 raise ovimException("DATABASE wrong version '{}'. Try to upgrade/downgrade to version '{}' with "\
177 "'./database_utils/migrate_vim_db.sh'".format(r
[1], database_version
) )
179 # create database connection for openflow threads
180 self
.db_of
= self
._create
_database
_connection
()
181 self
.config
["db"] = self
.db_of
182 self
.db_lock
= threading
.Lock()
183 self
.config
["db_lock"] = self
.db_lock
185 self
.of_test_mode
= False if self
.config
['mode'] == 'normal' or self
.config
['mode'] == "OF only" else True
186 # precreate interfaces; [bridge:<host_bridge_name>, VLAN used at Host, uuid of network camping in this bridge,
189 self
.config
['dhcp_nets'] = []
190 self
.config
['bridge_nets'] = []
191 for bridge
, vlan_speed
in self
.config
["bridge_ifaces"].items():
192 # skip 'development_bridge'
193 if self
.config
['mode'] == 'development' and self
.config
['development_bridge'] == bridge
:
195 self
.config
['bridge_nets'].append([bridge
, vlan_speed
[0], vlan_speed
[1], None])
197 # check if this bridge is already used (present at database) for a network)
198 used_bridge_nets
= []
199 for brnet
in self
.config
['bridge_nets']:
200 r
, nets
= self
.db
.get_table(SELECT
=('uuid',), FROM
='nets', WHERE
={'provider': "bridge:" + brnet
[0]})
202 brnet
[3] = nets
[0]['uuid']
203 used_bridge_nets
.append(brnet
[0])
204 if self
.config
.get("dhcp_server"):
205 if brnet
[0] in self
.config
["dhcp_server"]["bridge_ifaces"]:
206 self
.config
['dhcp_nets'].append(nets
[0]['uuid'])
207 if len(used_bridge_nets
) > 0:
208 self
.logger
.info("found used bridge nets: " + ",".join(used_bridge_nets
))
209 # get nets used by dhcp
210 if self
.config
.get("dhcp_server"):
211 for net
in self
.config
["dhcp_server"].get("nets", ()):
212 r
, nets
= self
.db
.get_table(SELECT
=('uuid',), FROM
='nets', WHERE
={'name': net
})
214 self
.config
['dhcp_nets'].append(nets
[0]['uuid'])
217 self
._start
_ofc
_default
_task
()
219 # OFC per tenant in DB
220 self
._start
_of
_db
_tasks
()
222 # create dhcp_server thread
223 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
224 dhcp_params
= self
.config
.get("dhcp_server")
226 thread
= dt
.dhcp_thread(dhcp_params
=dhcp_params
, test
=host_test_mode
, dhcp_nets
=self
.config
["dhcp_nets"],
227 db
=self
.db_of
, db_lock
=self
.db_lock
, debug
=self
.config
['log_level_of'])
229 self
.config
['dhcp_thread'] = thread
231 # Create one thread for each host
232 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
233 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
234 host_develop_bridge_iface
= self
.config
.get('development_bridge', None)
236 # get host list from data base before starting threads
237 r
, hosts
= self
.db
.get_table(SELECT
=('name', 'ip_name', 'user', 'uuid'), FROM
='hosts', WHERE
={'status': 'ok'})
239 raise ovimException("Cannot get hosts from database {}".format(hosts
))
241 self
.config
['host_threads'] = {}
243 host
['image_path'] = '/opt/VNF/images/openvim'
244 thread
= ht
.host_thread(name
=host
['name'], user
=host
['user'], host
=host
['ip_name'], db
=self
.db_of
,
245 db_lock
=self
.db_lock
, test
=host_test_mode
, image_path
=self
.config
['image_path'],
246 version
=self
.config
['version'], host_id
=host
['uuid'], develop_mode
=host_develop_mode
,
247 develop_bridge_iface
=host_develop_bridge_iface
)
249 self
.config
['host_threads'][host
['uuid']] = thread
251 # create ovs dhcp thread
252 result
, content
= self
.db
.get_table(FROM
='nets')
254 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
255 raise ovimException(str(content
), -result
)
258 net_type
= net
['type']
259 if (net_type
== 'bridge_data' or net_type
== 'bridge_man') \
260 and net
["provider"][:4] == 'OVS:' and net
["enable_dhcp"] == "true":
261 self
.launch_dhcp_server(net
['vlan'],
262 net
['dhcp_first_ip'],
267 def _start_of_db_tasks(self
):
269 Start ofc task for existing ofcs in database
274 ofcs
= self
.get_of_controllers()
277 of_conn
= self
._load
_of
_module
(ofc
)
278 # create ofc thread per of controller
279 self
._create
_ofc
_task
(ofc
['uuid'], ofc
['dpid'], of_conn
)
281 def _create_ofc_task(self
, ofc_uuid
, dpid
, of_conn
):
283 Create an ofc thread for handle each sdn controllers
284 :param ofc_uuid: sdn controller uuid
285 :param dpid: sdn controller dpid
286 :param of_conn: OF_conn module
289 if 'ofcs_thread' not in self
.config
and 'ofcs_thread_dpid' not in self
.config
:
291 ofcs_thread_dpid
= []
293 ofcs_threads
= self
.config
['ofcs_thread']
294 ofcs_thread_dpid
= self
.config
['ofcs_thread_dpid']
296 if ofc_uuid
not in ofcs_threads
:
297 ofc_thread
= self
._create
_ofc
_thread
(of_conn
, ofc_uuid
)
298 if ofc_uuid
== "Default":
299 self
.config
['of_thread'] = ofc_thread
301 ofcs_threads
[ofc_uuid
] = ofc_thread
302 self
.config
['ofcs_thread'] = ofcs_threads
304 ofcs_thread_dpid
.append({dpid
: ofc_thread
})
305 self
.config
['ofcs_thread_dpid'] = ofcs_thread_dpid
307 def _start_ofc_default_task(self
):
309 Create default ofc thread
311 if 'of_controller' not in self
.config \
312 and 'of_controller_ip' not in self
.config \
313 and 'of_controller_port' not in self
.config \
314 and 'of_controller_dpid' not in self
.config
:
319 db_config
['ip'] = self
.config
.get('of_controller_ip')
320 db_config
['port'] = self
.config
.get('of_controller_port')
321 db_config
['dpid'] = self
.config
.get('of_controller_dpid')
322 db_config
['type'] = self
.config
.get('of_controller')
323 db_config
['user'] = self
.config
.get('of_user')
324 db_config
['password'] = self
.config
.get('of_password')
326 # create connector to the openflow controller
327 # load other parameters starting by of_ from config dict in a temporal dict
329 of_conn
= self
._load
_of
_module
(db_config
)
330 # create openflow thread
331 self
._create
_ofc
_task
("Default", db_config
['dpid'], of_conn
)
333 def _load_of_module(self
, db_config
):
335 import python module for each SDN controller supported
336 :param db_config: SDN dn information
340 raise ovimException("No module found it", HTTP_Internal_Server_Error
)
345 if self
.of_test_mode
:
346 return openflow_conn
.OfTestConnector({"name": db_config
['type'],
347 "dpid": db_config
['dpid'],
348 "of_debug": self
.config
['log_level_of']})
352 temp_dict
['of_ip'] = db_config
['ip']
353 temp_dict
['of_port'] = db_config
['port']
354 temp_dict
['of_dpid'] = db_config
['dpid']
355 temp_dict
['of_controller'] = db_config
['type']
356 temp_dict
['of_user'] = db_config
['user']
357 temp_dict
['of_password'] = db_config
['password']
359 temp_dict
['of_debug'] = self
.config
['log_level_of']
361 if temp_dict
['of_controller'] == 'opendaylight':
364 module
= temp_dict
['of_controller']
366 if module
not in ovim
.of_module
:
367 module_info
= imp
.find_module(module
)
368 of_conn_module
= imp
.load_module("OF_conn", *module_info
)
369 ovim
.of_module
[module
] = of_conn_module
371 of_conn_module
= ovim
.of_module
[module
]
374 return of_conn_module
.OF_conn(temp_dict
)
375 except Exception as e
:
376 self
.logger
.error("Cannot open the Openflow controller '%s': %s", type(e
).__name
__, str(e
))
377 if module_info
and module_info
[0]:
378 file.close(module_info
[0])
379 raise ovimException("Cannot open the Openflow controller '{}': '{}'".format(type(e
).__name
__, str(e
)),
380 HTTP_Internal_Server_Error
)
381 except (IOError, ImportError) as e
:
382 if module_info
and module_info
[0]:
383 file.close(module_info
[0])
384 self
.logger
.error("Cannot open openflow controller module '%s'; %s: %s; revise 'of_controller' "
385 "field of configuration file.", module
, type(e
).__name
__, str(e
))
386 raise ovimException("Cannot open openflow controller module '{}'; {}: {}; revise 'of_controller' "
387 "field of configuration file.".format(module
, type(e
).__name
__, str(e
)),
388 HTTP_Internal_Server_Error
)
390 def _create_ofc_thread(self
, of_conn
, ofc_uuid
="Default"):
392 Create and launch a of thread
395 # create openflow thread
397 #if 'of_controller_nets_with_same_vlan' in self.config:
398 # ofc_net_same_vlan = self.config['of_controller_nets_with_same_vlan']
400 # ofc_net_same_vlan = False
401 ofc_net_same_vlan
= False
403 thread
= oft
.openflow_thread(ofc_uuid
, of_conn
, of_test
=self
.of_test_mode
, db
=self
.db_of
, db_lock
=self
.db_lock
,
404 pmp_with_same_vlan
=ofc_net_same_vlan
, debug
=self
.config
['log_level_of'])
405 #r, c = thread.OF_connector.obtain_port_correspondence()
407 # raise ovimException("Cannot get openflow information %s", c)
411 def stop_service(self
):
412 threads
= self
.config
.get('host_threads', {})
413 if 'of_thread' in self
.config
:
414 threads
['of'] = (self
.config
['of_thread'])
415 if 'ofcs_thread' in self
.config
:
416 ofcs_thread
= self
.config
['ofcs_thread']
417 for ofc
in ofcs_thread
:
418 threads
[ofc
] = ofcs_thread
[ofc
]
420 if 'dhcp_thread' in self
.config
:
421 threads
['dhcp'] = (self
.config
['dhcp_thread'])
423 for thread
in threads
.values():
424 thread
.insert_task("exit")
425 for thread
in threads
.values():
428 def get_networks(self
, columns
=None, db_filter
={}, limit
=None):
430 Retreive networks available
431 :param columns: List with select query parameters
432 :param db_filter: List with where query parameters
433 :param limit: Query limit result
436 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='nets', WHERE
=db_filter
, LIMIT
=limit
)
439 raise ovimException(str(content
), -result
)
441 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
445 def show_network(self
, network_id
, db_filter
={}):
447 Get network from DB by id
448 :param network_id: net Id
449 :param db_filter: List with where query parameters
454 raise ovimException("Not network id was not found")
455 db_filter
['uuid'] = network_id
457 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
=db_filter
, LIMIT
=100)
460 raise ovimException(str(content
), -result
)
462 raise ovimException("show_network network '%s' not found" % network_id
, -result
)
464 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
466 result
, ports
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
467 WHERE
={'net_id': network_id
}, LIMIT
=100)
469 content
[0]['ports'] = ports
471 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
474 def new_network(self
, network
):
479 tenant_id
= network
.get('tenant_id')
482 result
, _
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
, "enabled": True})
484 raise ovimException("set_network error, no tenant founded", -result
)
488 net_provider
= network
.get('provider')
489 net_type
= network
.get('type')
490 net_vlan
= network
.get("vlan")
491 net_bind_net
= network
.get("bind_net")
492 net_bind_type
= network
.get("bind_type")
493 name
= network
["name"]
495 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
496 vlan_index
= name
.rfind(":")
497 if not net_bind_net
and not net_bind_type
and vlan_index
> 1:
499 vlan_tag
= int(name
[vlan_index
+ 1:])
500 if not vlan_tag
and vlan_tag
< 4096:
501 net_bind_net
= name
[:vlan_index
]
502 net_bind_type
= "vlan:" + name
[vlan_index
+ 1:]
507 # look for a valid net
508 if self
._check
_valid
_uuid
(net_bind_net
):
509 net_bind_key
= "uuid"
511 net_bind_key
= "name"
512 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
514 raise ovimException(' getting nets from db ' + content
, HTTP_Internal_Server_Error
)
516 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
518 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
519 network
["bind_net"] = content
[0]["uuid"]
522 if net_bind_type
[0:5] != "vlan:":
523 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
524 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
525 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
527 network
["bind_type"] = net_bind_type
530 if net_provider
[:9] == "openflow:":
532 if net_type
!= "ptp" and net_type
!= "data":
533 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
539 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
540 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
541 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
543 net_type
= 'bridge_man'
546 net_type
= 'bridge_man'
549 if net_provider
[:7] == 'bridge:':
550 # check it is one of the pre-provisioned bridges
551 bridge_net_name
= net_provider
[7:]
552 for brnet
in self
.config
['bridge_nets']:
553 if brnet
[0] == bridge_net_name
: # free
555 raise ovimException("invalid 'provider:physical', "
556 "bridge '%s' is already used" % bridge_net_name
, HTTP_Conflict
)
560 # if bridge_net==None:
561 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
562 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
565 elif self
.config
['network_type'] == 'bridge' and (net_type
== 'bridge_data' or net_type
== 'bridge_man'):
566 # look for a free precreated nets
567 for brnet
in self
.config
['bridge_nets']:
568 if not brnet
[3]: # free
570 if net_type
== 'bridge_man': # look for the smaller speed
571 if brnet
[2] < bridge_net
[2]:
573 else: # look for the larger speed
574 if brnet
[2] > bridge_net
[2]:
580 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
581 "will overcome this limit", HTTP_Bad_Request
)
583 self
.logger
.debug("using net " + bridge_net
)
584 net_provider
= "bridge:" + bridge_net
[0]
585 net_vlan
= bridge_net
[1]
586 elif net_type
== 'bridge_data' or net_type
== 'bridge_man' and self
.config
['network_type'] == 'ovs':
588 if not net_vlan
and (net_type
== "data" or net_type
== "ptp" or net_provider
== "OVS"):
589 net_vlan
= self
.db
.get_free_net_vlan()
591 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error
)
592 if net_provider
== 'OVS':
593 net_provider
= 'OVS' + ":" + str(net_vlan
)
595 network
['provider'] = net_provider
596 network
['type'] = net_type
597 network
['vlan'] = net_vlan
598 dhcp_integrity
= True
599 if 'enable_dhcp' in network
and network
['enable_dhcp']:
600 dhcp_integrity
= self
._check
_dhcp
_data
_integrity
(network
)
602 result
, content
= self
.db
.new_row('nets', network
, True, True)
604 if result
>= 0 and dhcp_integrity
:
606 bridge_net
[3] = content
607 if self
.config
.get("dhcp_server") and self
.config
['network_type'] == 'bridge':
608 if network
["name"] in self
.config
["dhcp_server"].get("nets", ()):
609 self
.config
["dhcp_nets"].append(content
)
610 self
.logger
.debug("dhcp_server: add new net", content
)
611 elif not bridge_net
and bridge_net
[0] in self
.config
["dhcp_server"].get("bridge_ifaces", ()):
612 self
.config
["dhcp_nets"].append(content
)
613 self
.logger
.debug("dhcp_server: add new net", content
, content
)
616 raise ovimException("Error posting network", HTTP_Internal_Server_Error
)
617 # TODO kei change update->edit
619 def edit_network(self
, network_id
, network
):
621 Update entwork data byt id
624 # Look for the previous data
625 where_
= {'uuid': network_id
}
626 result
, network_old
= self
.db
.get_table(FROM
='nets', WHERE
=where_
)
628 raise ovimException("Error updating network %s" % network_old
, HTTP_Internal_Server_Error
)
630 raise ovimException('network %s not found' % network_id
, HTTP_Not_Found
)
632 nbports
, content
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
633 WHERE
={'net_id': network_id
}, LIMIT
=100)
635 raise ovimException("http_put_network_id error %d %s" % (result
, network_old
), HTTP_Internal_Server_Error
)
637 if 'type' in network
and network
['type'] != network_old
[0]['type']:
638 raise ovimException("Can not change type of network while having ports attached",
639 HTTP_Method_Not_Allowed
)
640 if 'vlan' in network
and network
['vlan'] != network_old
[0]['vlan']:
641 raise ovimException("Can not change vlan of network while having ports attached",
642 HTTP_Method_Not_Allowed
)
645 net_provider
= network
.get('provider', network_old
[0]['provider'])
646 net_type
= network
.get('type', network_old
[0]['type'])
647 net_bind_net
= network
.get("bind_net")
648 net_bind_type
= network
.get("bind_type")
650 # look for a valid net
651 if self
._check
_valid
_uuid
(net_bind_net
):
652 net_bind_key
= "uuid"
654 net_bind_key
= "name"
655 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
657 raise ovimException('Getting nets from db ' + content
, HTTP_Internal_Server_Error
)
659 raise ovimException("bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
661 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
),
663 network
["bind_net"] = content
[0]["uuid"]
665 if net_bind_type
[0:5] != "vlan:":
666 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
667 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
668 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
671 if net_provider
[:9] == "openflow:":
672 if net_type
!= "ptp" and net_type
!= "data":
673 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request
)
675 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
676 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
677 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
679 # insert in data base
680 result
, content
= self
.db
.update_rows('nets', network
, WHERE
={'uuid': network_id
}, log
=True)
682 # if result > 0 and nbports>0 and 'admin_state_up' in network
683 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
688 self
.net_update_ofc_thread(network_id
)
689 except ovimException
as e
:
690 raise ovimException("Error while launching openflow rules in network '{}' {}"
691 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
692 except Exception as e
:
693 raise ovimException("Error while launching openflow rules in network '{}' {}"
694 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
696 if self
.config
.get("dhcp_server"):
697 if network_id
in self
.config
["dhcp_nets"]:
698 self
.config
["dhcp_nets"].remove(network_id
)
699 if network
.get("name", network_old
[0]["name"]) in self
.config
["dhcp_server"].get("nets", ()):
700 self
.config
["dhcp_nets"].append(network_id
)
702 net_bind
= network
.get("bind_type", network_old
[0]["bind_type"])
703 if net_bind
and net_bind
and net_bind
[:7] == "bridge:" and net_bind
[7:] in self
.config
["dhcp_server"].get(
704 "bridge_ifaces", ()):
705 self
.config
["dhcp_nets"].append(network_id
)
708 raise ovimException(content
, -result
)
710 def delete_network(self
, network_id
):
712 Delete network by network id
713 :param network_id: network id
717 # delete from the data base
718 result
, content
= self
.db
.delete_row('nets', network_id
)
721 raise ovimException("Network %s not found " % network_id
, HTTP_Not_Found
)
723 for brnet
in self
.config
['bridge_nets']:
724 if brnet
[3] == network_id
:
727 if self
.config
.get("dhcp_server") and network_id
in self
.config
["dhcp_nets"]:
728 self
.config
["dhcp_nets"].remove(network_id
)
731 raise ovimException("Error deleting network %s" % network_id
, HTTP_Internal_Server_Error
)
733 def get_openflow_rules(self
, network_id
=None):
735 Get openflow id from DB
736 :param network_id: Network id, if none all networks will be retrieved
737 :return: Return a list with Openflow rules per net
743 where_
= {"net_id": network_id
}
744 result
, content
= self
.db
.get_table(
745 SELECT
=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
746 WHERE
=where_
, FROM
='of_flows')
749 raise ovimException(str(content
), -result
)
752 def edit_openflow_rules(self
, network_id
=None):
755 To make actions over the net. The action is to reinstall the openflow rules
756 network_id can be 'all'
757 :param network_id: Network id, if none all networks will be retrieved
758 :return : Number of nets updated
765 where_
= {"uuid": network_id
}
766 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
769 raise ovimException(str(content
), -result
)
772 if net
["type"] != "ptp" and net
["type"] != "data":
777 self
.net_update_ofc_thread(net
['uuid'])
778 except ovimException
as e
:
779 raise ovimException("Error updating network'{}' {}".format(net
['uuid'], str(e
)),
780 HTTP_Internal_Server_Error
)
781 except Exception as e
:
782 raise ovimException("Error updating network '{}' {}".format(net
['uuid'], str(e
)),
783 HTTP_Internal_Server_Error
)
787 def delete_openflow_rules(self
, ofc_id
=None):
789 To make actions over the net. The action is to delete ALL openflow rules
790 :return: return operation result
794 if 'Default' in self
.config
['ofcs_thread']:
795 r
, c
= self
.config
['ofcs_thread']['Default'].insert_task("clear-all")
797 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
799 elif ofc_id
in self
.config
['ofcs_thread']:
800 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("clear-all")
804 raise ovimException(str(c
), -r
)
806 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
809 def get_openflow_ports(self
, ofc_id
=None):
811 Obtain switch ports names of openflow controller
812 :return: Return flow ports in DB
815 if 'Default' in self
.config
['ofcs_thread']:
816 conn
= self
.config
['ofcs_thread']['Default'].OF_connector
818 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
820 if ofc_id
in self
.config
['ofcs_thread']:
821 conn
= self
.config
['ofcs_thread'][ofc_id
].OF_connector
823 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
826 def get_ports(self
, columns
=None, filter={}, limit
=None):
827 # result, content = my.db.get_ports(where_)
828 result
, content
= self
.db
.get_table(SELECT
=columns
, WHERE
=filter, FROM
='ports', LIMIT
=limit
)
830 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
831 raise ovimException(str(content
), -result
)
833 convert_boolean(content
, ('admin_state_up',))
836 def new_port(self
, port_data
):
837 port_data
['type'] = 'external'
838 if port_data
.get('net_id'):
839 # check that new net has the correct type
840 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
842 raise ovimException(str(new_net
), -result
)
843 # insert in data base
844 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
846 if 'net_id' in port_data
:
848 self
.net_update_ofc_thread(port_data
['net_id'])
849 except ovimException
as e
:
850 raise ovimException("Cannot insert a task for updating network '{}' {}"
851 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
852 except Exception as e
:
853 raise ovimException("Cannot insert a task for updating network '{}' {}"
854 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
858 raise ovimException(str(uuid
), -result
)
860 def new_external_port(self
, port_data
):
862 Create new external port and check port mapping correspondence
863 :param port_data: port_data = {
864 'region': 'datacenter region',
865 'compute_node': 'compute node id',
866 'pci': 'pci port address',
869 'tenant_id': 'tenant id',
872 'ip_address': 'ip address - optional'}
876 port_data
['type'] = 'external'
878 if port_data
.get('net_id'):
879 # check that new net has the correct type
880 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
882 raise ovimException(str(new_net
), -result
)
883 # insert in data base
886 if port_data
.get('region'):
887 db_filter
['region'] = port_data
['region']
888 if port_data
.get('pci'):
889 db_filter
['pci'] = port_data
['pci']
890 if port_data
.get('compute_node'):
891 db_filter
['compute_node'] = port_data
['compute_node']
893 columns
= ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
894 port_mapping_data
= self
.get_of_port_mappings(columns
, db_filter
)
896 if not len(port_mapping_data
):
897 raise ovimException("No port mapping founded for '{}'".format(str(db_filter
)),
899 elif len(port_mapping_data
) > 1:
900 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
903 port_data
['ofc_id'] = port_mapping_data
[0]['ofc_id']
904 port_data
['switch_dpid'] = port_mapping_data
[0]['switch_dpid']
905 port_data
['switch_port'] = port_mapping_data
[0]['switch_port']
906 port_data
['switch_mac'] = port_mapping_data
[0]['switch_mac']
908 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
909 if 'region' in port_data
:
910 del port_data
['region']
911 if 'pci' in port_data
:
913 if 'compute_node' in port_data
:
914 del port_data
['compute_node']
916 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
919 self
.net_update_ofc_thread(port_data
['net_id'], port_data
['ofc_id'])
920 except ovimException
as e
:
921 raise ovimException("Cannot insert a task for updating network '{}' {}".
922 format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
923 except Exception as e
:
924 raise ovimException("Cannot insert a task for updating network '{}' {}"
925 .format(port_data
['net_id'], e
), HTTP_Internal_Server_Error
)
928 raise ovimException(str(uuid
), -result
)
930 def net_update_ofc_thread(self
, net_id
, ofc_id
=None, switch_dpid
=None):
932 Insert a update net task by net id or ofc_id for each ofc thread
933 :param net_id: network id
934 :param ofc_id: openflow controller id
935 :param switch_dpid: switch dpid
939 raise ovimException("No net_id received", HTTP_Internal_Server_Error
)
942 c
= 'No valid ofc_id or switch_dpid received'
945 ports
= self
.get_ports(filter={"net_id": net_id
})
947 port_ofc_id
= port
.get('ofc_id', None)
949 ofc_id
= port
['ofc_id']
950 switch_dpid
= port
['switch_dpid']
952 #TODO if not ofc_id: look at database table ofcs
955 # If no ofc_id found it, default ofc_id is used.
956 if not ofc_id
and not switch_dpid
:
959 if ofc_id
and ofc_id
in self
.config
['ofcs_thread']:
960 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("update-net", net_id
)
963 ofcs_dpid_list
= self
.config
['ofcs_thread_dpid']
964 for ofc_t
in ofcs_dpid_list
:
965 if switch_dpid
in ofc_t
:
966 r
, c
= ofc_t
[switch_dpid
].insert_task("update-net", net_id
)
969 message
= "Cannot insert a task for updating network '{}', {}".format(net_id
, c
)
970 self
.logger
.error(message
)
971 raise ovimException(message
, HTTP_Internal_Server_Error
)
973 def delete_port(self
, port_id
):
974 # Look for the previous port data
975 result
, ports
= self
.db
.get_table(WHERE
={'uuid': port_id
, "type": "external"}, FROM
='ports')
977 raise ovimException("Cannot get port info from database: {}".format(ports
), http_code
=-result
)
978 # delete from the data base
979 result
, content
= self
.db
.delete_row('ports', port_id
)
981 raise ovimException("External port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
983 raise ovimException("Cannot delete port from database: {}".format(content
), http_code
=-result
)
985 network
= ports
[0].get('net_id', None)
990 self
.net_update_ofc_thread(network
, ofc_id
=ports
[0]["ofc_id"], switch_dpid
=ports
[0]["switch_dpid"])
991 except ovimException
as e
:
992 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
993 HTTP_Internal_Server_Error
)
994 except Exception as e
:
995 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
996 HTTP_Internal_Server_Error
)
1000 def edit_port(self
, port_id
, port_data
, admin
=True):
1001 # Look for the previous port data
1002 result
, content
= self
.db
.get_table(FROM
="ports", WHERE
={'uuid': port_id
})
1004 raise ovimException("Cannot get port info from database: {}".format(content
), http_code
=-result
)
1006 raise ovimException("Port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
1011 if 'net_id' in port_data
:
1013 old_net
= port
.get('net_id', None)
1014 new_net
= port_data
['net_id']
1015 if old_net
!= new_net
:
1018 nets
.append(new_net
) # put first the new net, so that new openflow rules are created before removing the old ones
1020 nets
.append(old_net
)
1021 if port
['type'] == 'instance:bridge' or port
['type'] == 'instance:ovs':
1022 raise ovimException("bridge interfaces cannot be attached to a different net", http_code
=HTTP_Forbidden
)
1023 elif port
['type'] == 'external' and not admin
:
1024 raise ovimException("Needed admin privileges",http_code
=HTTP_Unauthorized
)
1026 # check that new net has the correct type
1027 result
, new_net_dict
= self
.db
.check_target_net(new_net
, None, port
['type'])
1029 raise ovimException("Error {}".format(new_net_dict
), http_code
=HTTP_Conflict
)
1030 # change VLAN for SR-IOV ports
1031 if result
>= 0 and port
["type"] == "instance:data" and port
["model"] == "VF": # TODO consider also VFnotShared
1033 port_data
["vlan"] = None
1035 port_data
["vlan"] = new_net_dict
["vlan"]
1036 # get host where this VM is allocated
1037 result
, content
= self
.db
.get_table(FROM
="instances", WHERE
={"uuid": port
["instance_id"]})
1039 host_id
= content
[0]["host_id"]
1041 # insert in data base
1043 result
, content
= self
.db
.update_rows('ports', port_data
, WHERE
={'uuid': port_id
}, log
=False)
1044 port
.update(port_data
)
1046 # Insert task to complete actions
1050 self
.net_update_ofc_thread(net_id
, port
["ofc_id"], switch_dpid
=port
["switch_dpid"])
1051 except ovimException
as e
:
1052 raise ovimException("Error updating network'{}' {}".format(net_id
, str(e
)),
1053 HTTP_Internal_Server_Error
)
1054 except Exception as e
:
1055 raise ovimException("Error updating network '{}' {}".format(net_id
, str(e
)),
1056 HTTP_Internal_Server_Error
)
1059 r
, v
= self
.config
['host_threads'][host_id
].insert_task("edit-iface", port_id
, old_net
, new_net
)
1061 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
1062 # TODO Do something if fails
1066 raise ovimException("Error {}".format(content
), http_code
=-result
)
1068 def new_of_controller(self
, ofc_data
):
1070 Create a new openflow controller into DB
1071 :param ofc_data: Dict openflow controller data
1072 :return: openflow controller dpid
1075 result
, ofc_uuid
= self
.db
.new_row('ofcs', ofc_data
, True, True)
1077 raise ovimException("New ofc Error %s" % ofc_uuid
, HTTP_Internal_Server_Error
)
1079 ofc_data
['uuid'] = ofc_uuid
1080 of_conn
= self
._load
_of
_module
(ofc_data
)
1081 self
._create
_ofc
_task
(ofc_uuid
, ofc_data
['dpid'], of_conn
)
1085 def edit_of_controller(self
, of_id
, ofc_data
):
1087 Edit an openflow controller entry from DB
1091 raise ovimException("No data received during uptade OF contorller", http_code
=HTTP_Internal_Server_Error
)
1093 old_of_controller
= self
.show_of_controller(of_id
)
1095 if old_of_controller
:
1096 result
, content
= self
.db
.update_rows('ofcs', ofc_data
, WHERE
={'uuid': of_id
}, log
=False)
1100 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1103 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1104 http_code
=HTTP_Internal_Server_Error
)
1106 def delete_of_controller(self
, of_id
):
1108 Delete an openflow controller from DB.
1109 :param of_id: openflow controller dpid
1113 ofc
= self
.show_of_controller(of_id
)
1115 result
, content
= self
.db
.delete_row("ofcs", of_id
)
1117 raise ovimException("Cannot delete ofc from database: {}".format(content
), http_code
=-result
)
1119 raise ovimException("ofc {} not found ".format(content
), http_code
=HTTP_Not_Found
)
1121 ofc_thread
= self
.config
['ofcs_thread'][of_id
]
1122 del self
.config
['ofcs_thread'][of_id
]
1123 for ofc_th
in self
.config
['ofcs_thread_dpid']:
1124 if ofc
['dpid'] in ofc_th
:
1125 self
.config
['ofcs_thread_dpid'].remove(ofc_th
)
1127 ofc_thread
.insert_task("exit")
1132 def show_of_controller(self
, uuid
):
1134 Show an openflow controller by dpid from DB.
1135 :param db_filter: List with where query parameters
1139 result
, content
= self
.db
.get_table(FROM
='ofcs', WHERE
={"uuid": uuid
}, LIMIT
=100)
1142 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid
),
1143 http_code
=HTTP_Not_Found
)
1145 raise ovimException("Openflow controller with uuid '{}' error".format(uuid
),
1146 http_code
=HTTP_Internal_Server_Error
)
1149 def get_of_controllers(self
, columns
=None, db_filter
={}, limit
=None):
1151 Show an openflow controllers from DB.
1152 :param columns: List with SELECT query parameters
1153 :param db_filter: List with where query parameters
1154 :param limit: result Limit
1157 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='ofcs', WHERE
=db_filter
, LIMIT
=limit
)
1160 raise ovimException(str(content
), -result
)
1164 def get_tenants(self
, columns
=None, db_filter
={}, limit
=None):
1166 Retrieve tenant list from DB
1167 :param columns: List with SELECT query parameters
1168 :param db_filter: List with where query parameters
1169 :param limit: result limit
1172 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=columns
, WHERE
=db_filter
, LIMIT
=limit
)
1174 raise ovimException('get_tenatns Error {}'.format(str(content
)), -result
)
1176 convert_boolean(content
, ('enabled',))
1179 def show_tenant_id(self
, tenant_id
):
1181 Get tenant from DB by id
1182 :param tenant_id: tenant id
1185 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid', 'name', 'description', 'enabled'),
1186 WHERE
={"uuid": tenant_id
})
1188 raise ovimException(str(content
), -result
)
1190 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1192 convert_boolean(content
, ('enabled',))
1195 def new_tentant(self
, tenant
):
1197 Create a tenant and store in DB
1198 :param tenant: Dictionary with tenant data
1199 :return: the uuid of created tenant. Raise exception upon error
1202 # insert in data base
1203 result
, tenant_uuid
= self
.db
.new_tenant(tenant
)
1208 raise ovimException(str(tenant_uuid
), -result
)
1210 def delete_tentant(self
, tenant_id
):
1212 Delete a tenant from the database.
1213 :param tenant_id: Tenant id
1214 :return: delete tenant id
1218 r
, tenants_flavors
= self
.db
.get_table(FROM
='tenants_flavors', SELECT
=('flavor_id', 'tenant_id'),
1219 WHERE
={'tenant_id': tenant_id
})
1221 tenants_flavors
= ()
1222 r
, tenants_images
= self
.db
.get_table(FROM
='tenants_images', SELECT
=('image_id', 'tenant_id'),
1223 WHERE
={'tenant_id': tenant_id
})
1227 result
, content
= self
.db
.delete_row('tenants', tenant_id
)
1229 raise ovimException("tenant '%s' not found" % tenant_id
, HTTP_Not_Found
)
1231 for flavor
in tenants_flavors
:
1232 self
.db
.delete_row_by_key("flavors", "uuid", flavor
['flavor_id'])
1233 for image
in tenants_images
:
1234 self
.db
.delete_row_by_key("images", "uuid", image
['image_id'])
1237 raise ovimException("Error deleting tenant '%s' " % tenant_id
, HTTP_Internal_Server_Error
)
1239 def edit_tenant(self
, tenant_id
, tenant_data
):
1241 Update a tenant data identified by tenant id
1242 :param tenant_id: tenant id
1243 :param tenant_data: Dictionary with tenant data
1247 # Look for the previous data
1248 result
, tenant_data_old
= self
.db
.get_table(FROM
='tenants', WHERE
={'uuid': tenant_id
})
1250 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id
, tenant_data_old
),
1251 HTTP_Internal_Server_Error
)
1253 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1255 # insert in data base
1256 result
, content
= self
.db
.update_rows('tenants', tenant_data
, WHERE
={'uuid': tenant_id
}, log
=True)
1260 raise ovimException(str(content
), -result
)
1262 def set_of_port_mapping(self
, of_maps
, ofc_id
=None, switch_dpid
=None, region
=None):
1264 Create new port mapping entry
1265 :param of_maps: List with port mapping information
1266 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1267 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1268 :param ofc_id: ofc id
1269 :param switch_dpid: switch dpid
1270 :param region: datacenter region id
1276 map['ofc_id'] = ofc_id
1278 map['switch_dpid'] = switch_dpid
1280 map['region'] = region
1282 for of_map
in of_maps
:
1283 result
, uuid
= self
.db
.new_row('of_port_mappings', of_map
, True)
1285 of_map
["uuid"] = uuid
1287 raise ovimException(str(uuid
), -result
)
1290 def clear_of_port_mapping(self
, db_filter
={}):
1292 Clear port mapping filtering using db_filter dict
1293 :param db_filter: Parameter to filter during remove process
1296 result
, content
= self
.db
.delete_row_by_dict(FROM
='of_port_mappings', WHERE
=db_filter
)
1301 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter
)),
1302 HTTP_Internal_Server_Error
)
1304 def get_of_port_mappings(self
, column
=None, db_filter
=None, db_limit
=None):
1306 Retrive port mapping from DB
1311 result
, content
= self
.db
.get_table(SELECT
=column
, WHERE
=db_filter
, FROM
='of_port_mappings', LIMIT
=db_limit
)
1314 self
.logger
.error("get_of_port_mappings Error %d %s", result
, content
)
1315 raise ovimException(str(content
), -result
)
1319 def get_dhcp_controller(self
):
1321 Create an host_thread object for manage openvim controller and not create a thread for itself
1322 :return: dhcp_host openvim controller object
1325 if 'openvim_controller' in self
.config
['host_threads']:
1326 return self
.config
['host_threads']['openvim_controller']
1329 controller_ip
= self
.config
['ovs_controller_ip']
1330 ovs_controller_user
= self
.config
['ovs_controller_user']
1332 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
1333 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
1335 dhcp_host
= ht
.host_thread(name
='openvim_controller', user
=ovs_controller_user
, host
=controller_ip
,
1337 db_lock
=self
.db_lock
, test
=host_test_mode
,
1338 image_path
=self
.config
['image_path'], version
=self
.config
['version'],
1339 host_id
='openvim_controller', develop_mode
=host_develop_mode
,
1340 develop_bridge_iface
=bridge_ifaces
)
1342 self
.config
['host_threads']['openvim_controller'] = dhcp_host
1343 if not host_test_mode
:
1344 dhcp_host
.ssh_connect()
1347 def launch_dhcp_server(self
, vlan
, first_ip
, last_ip
, cidr
, gateway
):
1349 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1350 :param vlan: vlan identifier
1351 :param first_ip: First dhcp range ip
1352 :param last_ip: Last dhcp range ip
1353 :param cidr: net cidr
1354 :param gateway: net gateway
1357 ip_tools
= IPNetwork(cidr
)
1358 dhcp_netmask
= str(ip_tools
.netmask
)
1359 ip_range
= [first_ip
, last_ip
]
1361 dhcp_path
= self
.config
['ovs_controller_file_path']
1363 controller_host
= self
.get_dhcp_controller()
1364 controller_host
.create_linux_bridge(vlan
)
1365 controller_host
.create_dhcp_interfaces(vlan
, first_ip
, dhcp_netmask
)
1366 controller_host
.launch_dhcp_server(vlan
, ip_range
, dhcp_netmask
, dhcp_path
, gateway
)