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
402 thread
= oft
.openflow_thread(ofc_uuid
, of_conn
, of_test
=self
.of_test_mode
, db
=self
.db_of
, db_lock
=self
.db_lock
,
403 pmp_with_same_vlan
=ofc_net_same_vlan
, debug
=self
.config
['log_level_of'])
404 #r, c = thread.OF_connector.obtain_port_correspondence()
406 # raise ovimException("Cannot get openflow information %s", c)
410 def stop_service(self
):
411 threads
= self
.config
.get('host_threads', {})
412 if 'of_thread' in self
.config
:
413 threads
['of'] = (self
.config
['of_thread'])
414 if 'ofcs_thread' in self
.config
:
415 ofcs_thread
= self
.config
['ofcs_thread']
416 for ofc
in ofcs_thread
:
417 threads
[ofc
] = ofcs_thread
[ofc
]
419 if 'dhcp_thread' in self
.config
:
420 threads
['dhcp'] = (self
.config
['dhcp_thread'])
422 for thread
in threads
.values():
423 thread
.insert_task("exit")
424 for thread
in threads
.values():
427 def get_networks(self
, columns
=None, db_filter
={}, limit
=None):
429 Retreive networks available
430 :param columns: List with select query parameters
431 :param db_filter: List with where query parameters
432 :param limit: Query limit result
435 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='nets', WHERE
=db_filter
, LIMIT
=limit
)
438 raise ovimException(str(content
), -result
)
440 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
444 def show_network(self
, network_id
, db_filter
={}):
446 Get network from DB by id
447 :param network_id: net Id
448 :param db_filter: List with where query parameters
453 raise ovimException("Not network id was not found")
454 db_filter
['uuid'] = network_id
456 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
=db_filter
, LIMIT
=100)
459 raise ovimException(str(content
), -result
)
461 raise ovimException("show_network network '%s' not found" % network_id
, -result
)
463 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
465 result
, ports
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
466 WHERE
={'net_id': network_id
}, LIMIT
=100)
468 content
[0]['ports'] = ports
470 convert_boolean(content
, ('shared', 'admin_state_up', 'enable_dhcp'))
473 def new_network(self
, network
):
478 tenant_id
= network
.get('tenant_id')
481 result
, _
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
, "enabled": True})
483 raise ovimException("set_network error, no tenant founded", -result
)
487 net_provider
= network
.get('provider')
488 net_type
= network
.get('type')
489 net_vlan
= network
.get("vlan")
490 net_bind_net
= network
.get("bind_net")
491 net_bind_type
= network
.get("bind_type")
492 name
= network
["name"]
494 # check if network name ends with :<vlan_tag> and network exist in order to make and automated bindning
495 vlan_index
= name
.rfind(":")
496 if not net_bind_net
and not net_bind_type
and vlan_index
> 1:
498 vlan_tag
= int(name
[vlan_index
+ 1:])
499 if not vlan_tag
and vlan_tag
< 4096:
500 net_bind_net
= name
[:vlan_index
]
501 net_bind_type
= "vlan:" + name
[vlan_index
+ 1:]
506 # look for a valid net
507 if self
._check
_valid
_uuid
(net_bind_net
):
508 net_bind_key
= "uuid"
510 net_bind_key
= "name"
511 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
513 raise ovimException(' getting nets from db ' + content
, HTTP_Internal_Server_Error
)
515 raise ovimException(" bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
517 raise ovimException(" more than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
518 network
["bind_net"] = content
[0]["uuid"]
521 if net_bind_type
[0:5] != "vlan:":
522 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
523 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
524 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
526 network
["bind_type"] = net_bind_type
529 if net_provider
[:9] == "openflow:":
531 if net_type
!= "ptp" and net_type
!= "data":
532 raise ovimException(" only 'ptp' or 'data' net types can be bound to 'openflow'",
538 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
539 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound "
540 "to 'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
542 net_type
= 'bridge_man'
545 net_type
= 'bridge_man'
548 if net_provider
[:7] == 'bridge:':
549 # check it is one of the pre-provisioned bridges
550 bridge_net_name
= net_provider
[7:]
551 for brnet
in self
.config
['bridge_nets']:
552 if brnet
[0] == bridge_net_name
: # free
554 raise ovimException("invalid 'provider:physical', "
555 "bridge '%s' is already used" % bridge_net_name
, HTTP_Conflict
)
559 # if bridge_net==None:
560 # bottle.abort(HTTP_Bad_Request, "invalid 'provider:physical', bridge '%s' is not one of the
561 # provisioned 'bridge_ifaces' in the configuration file" % bridge_net_name)
564 elif self
.config
['network_type'] == 'bridge' and (net_type
== 'bridge_data' or net_type
== 'bridge_man'):
565 # look for a free precreated nets
566 for brnet
in self
.config
['bridge_nets']:
567 if not brnet
[3]: # free
569 if net_type
== 'bridge_man': # look for the smaller speed
570 if brnet
[2] < bridge_net
[2]:
572 else: # look for the larger speed
573 if brnet
[2] > bridge_net
[2]:
579 raise ovimException("Max limits of bridge networks reached. Future versions of VIM "
580 "will overcome this limit", HTTP_Bad_Request
)
582 self
.logger
.debug("using net " + bridge_net
)
583 net_provider
= "bridge:" + bridge_net
[0]
584 net_vlan
= bridge_net
[1]
585 elif net_type
== 'bridge_data' or net_type
== 'bridge_man' and self
.config
['network_type'] == 'ovs':
587 if not net_vlan
and (net_type
== "data" or net_type
== "ptp" or net_provider
== "OVS"):
588 net_vlan
= self
.db
.get_free_net_vlan()
590 raise ovimException("Error getting an available vlan", HTTP_Internal_Server_Error
)
591 if net_provider
== 'OVS':
592 net_provider
= 'OVS' + ":" + str(net_vlan
)
594 network
['provider'] = net_provider
595 network
['type'] = net_type
596 network
['vlan'] = net_vlan
597 dhcp_integrity
= True
598 if 'enable_dhcp' in network
and network
['enable_dhcp']:
599 dhcp_integrity
= self
._check
_dhcp
_data
_integrity
(network
)
601 result
, content
= self
.db
.new_row('nets', network
, True, True)
603 if result
>= 0 and dhcp_integrity
:
605 bridge_net
[3] = content
606 if self
.config
.get("dhcp_server") and self
.config
['network_type'] == 'bridge':
607 if network
["name"] in self
.config
["dhcp_server"].get("nets", ()):
608 self
.config
["dhcp_nets"].append(content
)
609 self
.logger
.debug("dhcp_server: add new net", content
)
610 elif not bridge_net
and bridge_net
[0] in self
.config
["dhcp_server"].get("bridge_ifaces", ()):
611 self
.config
["dhcp_nets"].append(content
)
612 self
.logger
.debug("dhcp_server: add new net", content
, content
)
615 raise ovimException("Error posting network", HTTP_Internal_Server_Error
)
616 # TODO kei change update->edit
618 def edit_network(self
, network_id
, network
):
620 Update entwork data byt id
623 # Look for the previous data
624 where_
= {'uuid': network_id
}
625 result
, network_old
= self
.db
.get_table(FROM
='nets', WHERE
=where_
)
627 raise ovimException("Error updating network %s" % network_old
, HTTP_Internal_Server_Error
)
629 raise ovimException('network %s not found' % network_id
, HTTP_Not_Found
)
631 nbports
, content
= self
.db
.get_table(FROM
='ports', SELECT
=('uuid as port_id',),
632 WHERE
={'net_id': network_id
}, LIMIT
=100)
634 raise ovimException("http_put_network_id error %d %s" % (result
, network_old
), HTTP_Internal_Server_Error
)
636 if 'type' in network
and network
['type'] != network_old
[0]['type']:
637 raise ovimException("Can not change type of network while having ports attached",
638 HTTP_Method_Not_Allowed
)
639 if 'vlan' in network
and network
['vlan'] != network_old
[0]['vlan']:
640 raise ovimException("Can not change vlan of network while having ports attached",
641 HTTP_Method_Not_Allowed
)
644 net_provider
= network
.get('provider', network_old
[0]['provider'])
645 net_type
= network
.get('type', network_old
[0]['type'])
646 net_bind_net
= network
.get("bind_net")
647 net_bind_type
= network
.get("bind_type")
649 # look for a valid net
650 if self
._check
_valid
_uuid
(net_bind_net
):
651 net_bind_key
= "uuid"
653 net_bind_key
= "name"
654 result
, content
= self
.db
.get_table(FROM
='nets', WHERE
={net_bind_key
: net_bind_net
})
656 raise ovimException('Getting nets from db ' + content
, HTTP_Internal_Server_Error
)
658 raise ovimException("bind_net %s '%s'not found" % (net_bind_key
, net_bind_net
), HTTP_Bad_Request
)
660 raise ovimException("More than one bind_net %s '%s' found, use uuid" % (net_bind_key
, net_bind_net
),
662 network
["bind_net"] = content
[0]["uuid"]
664 if net_bind_type
[0:5] != "vlan:":
665 raise ovimException("Bad format for 'bind_type', must be 'vlan:<tag>'", HTTP_Bad_Request
)
666 if int(net_bind_type
[5:]) > 4095 or int(net_bind_type
[5:]) <= 0:
667 raise ovimException("bad format for 'bind_type', must be 'vlan:<tag>' with a tag between 1 and 4095",
670 if net_provider
[:9] == "openflow:":
671 if net_type
!= "ptp" and net_type
!= "data":
672 raise ovimException("Only 'ptp' or 'data' net types can be bound to 'openflow'", HTTP_Bad_Request
)
674 if net_type
!= "bridge_man" and net_type
!= "bridge_data":
675 raise ovimException("Only 'bridge_man' or 'bridge_data' net types can be bound to "
676 "'bridge', 'macvtap' or 'default", HTTP_Bad_Request
)
678 # insert in data base
679 result
, content
= self
.db
.update_rows('nets', network
, WHERE
={'uuid': network_id
}, log
=True)
681 # if result > 0 and nbports>0 and 'admin_state_up' in network
682 # and network['admin_state_up'] != network_old[0]['admin_state_up']:
687 self
.net_update_ofc_thread(network_id
)
688 except ovimException
as e
:
689 raise ovimException("Error while launching openflow rules in network '{}' {}"
690 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
691 except Exception as e
:
692 raise ovimException("Error while launching openflow rules in network '{}' {}"
693 .format(network_id
, str(e
)), HTTP_Internal_Server_Error
)
695 if self
.config
.get("dhcp_server"):
696 if network_id
in self
.config
["dhcp_nets"]:
697 self
.config
["dhcp_nets"].remove(network_id
)
698 if network
.get("name", network_old
[0]["name"]) in self
.config
["dhcp_server"].get("nets", ()):
699 self
.config
["dhcp_nets"].append(network_id
)
701 net_bind
= network
.get("bind_type", network_old
[0]["bind_type"])
702 if net_bind
and net_bind
and net_bind
[:7] == "bridge:" and net_bind
[7:] in self
.config
["dhcp_server"].get(
703 "bridge_ifaces", ()):
704 self
.config
["dhcp_nets"].append(network_id
)
707 raise ovimException(content
, -result
)
709 def delete_network(self
, network_id
):
711 Delete network by network id
712 :param network_id: network id
716 # delete from the data base
717 result
, content
= self
.db
.delete_row('nets', network_id
)
720 raise ovimException("Network %s not found " % network_id
, HTTP_Not_Found
)
722 for brnet
in self
.config
['bridge_nets']:
723 if brnet
[3] == network_id
:
726 if self
.config
.get("dhcp_server") and network_id
in self
.config
["dhcp_nets"]:
727 self
.config
["dhcp_nets"].remove(network_id
)
730 raise ovimException("Error deleting network %s" % network_id
, HTTP_Internal_Server_Error
)
732 def get_openflow_rules(self
, network_id
=None):
734 Get openflow id from DB
735 :param network_id: Network id, if none all networks will be retrieved
736 :return: Return a list with Openflow rules per net
742 where_
= {"net_id": network_id
}
743 result
, content
= self
.db
.get_table(
744 SELECT
=("name", "net_id", "ofc_id", "priority", "vlan_id", "ingress_port", "src_mac", "dst_mac", "actions"),
745 WHERE
=where_
, FROM
='of_flows')
748 raise ovimException(str(content
), -result
)
751 def edit_openflow_rules(self
, network_id
=None):
754 To make actions over the net. The action is to reinstall the openflow rules
755 network_id can be 'all'
756 :param network_id: Network id, if none all networks will be retrieved
757 :return : Number of nets updated
764 where_
= {"uuid": network_id
}
765 result
, content
= self
.db
.get_table(SELECT
=("uuid", "type"), WHERE
=where_
, FROM
='nets')
768 raise ovimException(str(content
), -result
)
771 if net
["type"] != "ptp" and net
["type"] != "data":
776 self
.net_update_ofc_thread(net
['uuid'])
777 except ovimException
as e
:
778 raise ovimException("Error updating network'{}' {}".format(net
['uuid'], str(e
)),
779 HTTP_Internal_Server_Error
)
780 except Exception as e
:
781 raise ovimException("Error updating network '{}' {}".format(net
['uuid'], str(e
)),
782 HTTP_Internal_Server_Error
)
786 def delete_openflow_rules(self
, ofc_id
=None):
788 To make actions over the net. The action is to delete ALL openflow rules
789 :return: return operation result
793 if 'Default' in self
.config
['ofcs_thread']:
794 r
, c
= self
.config
['ofcs_thread']['Default'].insert_task("clear-all")
796 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
798 elif ofc_id
in self
.config
['ofcs_thread']:
799 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("clear-all")
803 raise ovimException(str(c
), -r
)
805 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
808 def get_openflow_ports(self
, ofc_id
=None):
810 Obtain switch ports names of openflow controller
811 :return: Return flow ports in DB
814 if 'Default' in self
.config
['ofcs_thread']:
815 conn
= self
.config
['ofcs_thread']['Default'].OF_connector
817 raise ovimException("Default Openflow controller not not running", HTTP_Not_Found
)
819 if ofc_id
in self
.config
['ofcs_thread']:
820 conn
= self
.config
['ofcs_thread'][ofc_id
].OF_connector
822 raise ovimException("Openflow controller not found with ofc_id={}".format(ofc_id
), HTTP_Not_Found
)
825 def get_ports(self
, columns
=None, filter={}, limit
=None):
826 # result, content = my.db.get_ports(where_)
827 result
, content
= self
.db
.get_table(SELECT
=columns
, WHERE
=filter, FROM
='ports', LIMIT
=limit
)
829 self
.logger
.error("http_get_ports Error %d %s", result
, content
)
830 raise ovimException(str(content
), -result
)
832 convert_boolean(content
, ('admin_state_up',))
835 def new_port(self
, port_data
):
836 port_data
['type'] = 'external'
837 if port_data
.get('net_id'):
838 # check that new net has the correct type
839 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
841 raise ovimException(str(new_net
), -result
)
842 # insert in data base
843 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
845 if 'net_id' in port_data
:
847 self
.net_update_ofc_thread(port_data
['net_id'])
848 except ovimException
as e
:
849 raise ovimException("Cannot insert a task for updating network '{}' {}"
850 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
851 except Exception as e
:
852 raise ovimException("Cannot insert a task for updating network '{}' {}"
853 .format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
857 raise ovimException(str(uuid
), -result
)
859 def new_external_port(self
, port_data
):
861 Create new external port and check port mapping correspondence
862 :param port_data: port_data = {
863 'region': 'datacenter region',
864 'compute_node': 'compute node id',
865 'pci': 'pci port address',
868 'tenant_id': 'tenant id',
871 'ip_address': 'ip address - optional'}
875 port_data
['type'] = 'external'
877 if port_data
.get('net_id'):
878 # check that new net has the correct type
879 result
, new_net
= self
.db
.check_target_net(port_data
['net_id'], None, 'external')
881 raise ovimException(str(new_net
), -result
)
882 # insert in data base
885 if port_data
.get('region'):
886 db_filter
['region'] = port_data
['region']
887 if port_data
.get('pci'):
888 db_filter
['pci'] = port_data
['pci']
889 if port_data
.get('compute_node'):
890 db_filter
['compute_node'] = port_data
['compute_node']
892 columns
= ['ofc_id', 'switch_dpid', 'switch_port', 'switch_mac', 'pci']
893 port_mapping_data
= self
.get_of_port_mappings(columns
, db_filter
)
895 if not len(port_mapping_data
):
896 raise ovimException("No port mapping founded for '{}'".format(str(db_filter
)),
898 elif len(port_mapping_data
) > 1:
899 raise ovimException("Wrong port data was given, please check pci, region & compute id data",
902 port_data
['ofc_id'] = port_mapping_data
[0]['ofc_id']
903 port_data
['switch_dpid'] = port_mapping_data
[0]['switch_dpid']
904 port_data
['switch_port'] = port_mapping_data
[0]['switch_port']
905 port_data
['switch_mac'] = port_mapping_data
[0]['switch_mac']
907 # remove from compute_node, region and pci of_port_data to adapt to 'ports' structure
908 if 'region' in port_data
:
909 del port_data
['region']
910 if 'pci' in port_data
:
912 if 'compute_node' in port_data
:
913 del port_data
['compute_node']
915 result
, uuid
= self
.db
.new_row('ports', port_data
, True, True)
918 self
.net_update_ofc_thread(port_data
['net_id'], port_data
['ofc_id'])
919 except ovimException
as e
:
920 raise ovimException("Cannot insert a task for updating network '{}' {}".
921 format(port_data
['net_id'], str(e
)), HTTP_Internal_Server_Error
)
922 except Exception as e
:
923 raise ovimException("Cannot insert a task for updating network '{}' {}"
924 .format(port_data
['net_id'], e
), HTTP_Internal_Server_Error
)
927 raise ovimException(str(uuid
), -result
)
929 def net_update_ofc_thread(self
, net_id
, ofc_id
=None, switch_dpid
=None):
931 Insert a update net task by net id or ofc_id for each ofc thread
932 :param net_id: network id
933 :param ofc_id: openflow controller id
934 :param switch_dpid: switch dpid
938 raise ovimException("No net_id received", HTTP_Internal_Server_Error
)
941 c
= 'No valid ofc_id or switch_dpid received'
944 ports
= self
.get_ports(filter={"net_id": net_id
})
946 port_ofc_id
= port
.get('ofc_id', None)
948 ofc_id
= port
['ofc_id']
949 switch_dpid
= port
['switch_dpid']
951 #TODO if not ofc_id: look at database table ofcs
954 # If no ofc_id found it, default ofc_id is used.
955 if not ofc_id
and not switch_dpid
:
958 if ofc_id
and ofc_id
in self
.config
['ofcs_thread']:
959 r
, c
= self
.config
['ofcs_thread'][ofc_id
].insert_task("update-net", net_id
)
962 ofcs_dpid_list
= self
.config
['ofcs_thread_dpid']
963 for ofc_t
in ofcs_dpid_list
:
964 if switch_dpid
in ofc_t
:
965 r
, c
= ofc_t
[switch_dpid
].insert_task("update-net", net_id
)
968 message
= "Cannot insert a task for updating network '{}', {}".format(net_id
, c
)
969 self
.logger
.error(message
)
970 raise ovimException(message
, HTTP_Internal_Server_Error
)
972 def delete_port(self
, port_id
):
973 # Look for the previous port data
974 result
, ports
= self
.db
.get_table(WHERE
={'uuid': port_id
, "type": "external"}, FROM
='ports')
976 raise ovimException("Cannot get port info from database: {}".format(ports
), http_code
=-result
)
977 # delete from the data base
978 result
, content
= self
.db
.delete_row('ports', port_id
)
980 raise ovimException("External port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
982 raise ovimException("Cannot delete port from database: {}".format(content
), http_code
=-result
)
984 network
= ports
[0].get('net_id', None)
989 self
.net_update_ofc_thread(network
, ofc_id
=ports
[0]["ofc_id"], switch_dpid
=ports
[0]["switch_dpid"])
990 except ovimException
as e
:
991 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
992 HTTP_Internal_Server_Error
)
993 except Exception as e
:
994 raise ovimException("Cannot insert a task for delete network '{}' {}".format(network
, str(e
)),
995 HTTP_Internal_Server_Error
)
999 def edit_port(self
, port_id
, port_data
, admin
=True):
1000 # Look for the previous port data
1001 result
, content
= self
.db
.get_table(FROM
="ports", WHERE
={'uuid': port_id
})
1003 raise ovimException("Cannot get port info from database: {}".format(content
), http_code
=-result
)
1005 raise ovimException("Port '{}' not found".format(port_id
), http_code
=HTTP_Not_Found
)
1010 if 'net_id' in port_data
:
1012 old_net
= port
.get('net_id', None)
1013 new_net
= port_data
['net_id']
1014 if old_net
!= new_net
:
1017 nets
.append(new_net
) # put first the new net, so that new openflow rules are created before removing the old ones
1019 nets
.append(old_net
)
1020 if port
['type'] == 'instance:bridge' or port
['type'] == 'instance:ovs':
1021 raise ovimException("bridge interfaces cannot be attached to a different net", http_code
=HTTP_Forbidden
)
1022 elif port
['type'] == 'external' and not admin
:
1023 raise ovimException("Needed admin privileges",http_code
=HTTP_Unauthorized
)
1025 # check that new net has the correct type
1026 result
, new_net_dict
= self
.db
.check_target_net(new_net
, None, port
['type'])
1028 raise ovimException("Error {}".format(new_net_dict
), http_code
=HTTP_Conflict
)
1029 # change VLAN for SR-IOV ports
1030 if result
>= 0 and port
["type"] == "instance:data" and port
["model"] == "VF": # TODO consider also VFnotShared
1032 port_data
["vlan"] = None
1034 port_data
["vlan"] = new_net_dict
["vlan"]
1035 # get host where this VM is allocated
1036 result
, content
= self
.db
.get_table(FROM
="instances", WHERE
={"uuid": port
["instance_id"]})
1038 host_id
= content
[0]["host_id"]
1040 # insert in data base
1042 result
, content
= self
.db
.update_rows('ports', port_data
, WHERE
={'uuid': port_id
}, log
=False)
1043 port
.update(port_data
)
1045 # Insert task to complete actions
1049 self
.net_update_ofc_thread(net_id
, port
["ofc_id"], switch_dpid
=port
["switch_dpid"])
1050 except ovimException
as e
:
1051 raise ovimException("Error updating network'{}' {}".format(net_id
, str(e
)),
1052 HTTP_Internal_Server_Error
)
1053 except Exception as e
:
1054 raise ovimException("Error updating network '{}' {}".format(net_id
, str(e
)),
1055 HTTP_Internal_Server_Error
)
1058 r
, v
= self
.config
['host_threads'][host_id
].insert_task("edit-iface", port_id
, old_net
, new_net
)
1060 self
.logger
.error("Error updating network '{}' {}".format(r
,v
))
1061 # TODO Do something if fails
1065 raise ovimException("Error {}".format(content
), http_code
=-result
)
1067 def new_of_controller(self
, ofc_data
):
1069 Create a new openflow controller into DB
1070 :param ofc_data: Dict openflow controller data
1071 :return: openflow controller dpid
1074 result
, ofc_uuid
= self
.db
.new_row('ofcs', ofc_data
, True, True)
1076 raise ovimException("New ofc Error %s" % ofc_uuid
, HTTP_Internal_Server_Error
)
1078 ofc_data
['uuid'] = ofc_uuid
1079 of_conn
= self
._load
_of
_module
(ofc_data
)
1080 self
._create
_ofc
_task
(ofc_uuid
, ofc_data
['dpid'], of_conn
)
1084 def edit_of_controller(self
, of_id
, ofc_data
):
1086 Edit an openflow controller entry from DB
1090 raise ovimException("No data received during uptade OF contorller", http_code
=HTTP_Internal_Server_Error
)
1092 old_of_controller
= self
.show_of_controller(of_id
)
1094 if old_of_controller
:
1095 result
, content
= self
.db
.update_rows('ofcs', ofc_data
, WHERE
={'uuid': of_id
}, log
=False)
1099 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1102 raise ovimException("Error uptating OF contorller with uuid {}".format(of_id
),
1103 http_code
=HTTP_Internal_Server_Error
)
1105 def delete_of_controller(self
, of_id
):
1107 Delete an openflow controller from DB.
1108 :param of_id: openflow controller dpid
1112 ofc
= self
.show_of_controller(of_id
)
1114 result
, content
= self
.db
.delete_row("ofcs", of_id
)
1116 raise ovimException("Cannot delete ofc from database: {}".format(content
), http_code
=-result
)
1118 raise ovimException("ofc {} not found ".format(content
), http_code
=HTTP_Not_Found
)
1120 ofc_thread
= self
.config
['ofcs_thread'][of_id
]
1121 del self
.config
['ofcs_thread'][of_id
]
1122 for ofc_th
in self
.config
['ofcs_thread_dpid']:
1123 if ofc
['dpid'] in ofc_th
:
1124 self
.config
['ofcs_thread_dpid'].remove(ofc_th
)
1126 ofc_thread
.insert_task("exit")
1131 def show_of_controller(self
, uuid
):
1133 Show an openflow controller by dpid from DB.
1134 :param db_filter: List with where query parameters
1138 result
, content
= self
.db
.get_table(FROM
='ofcs', WHERE
={"uuid": uuid
}, LIMIT
=100)
1141 raise ovimException("Openflow controller with uuid '{}' not found".format(uuid
),
1142 http_code
=HTTP_Not_Found
)
1144 raise ovimException("Openflow controller with uuid '{}' error".format(uuid
),
1145 http_code
=HTTP_Internal_Server_Error
)
1148 def get_of_controllers(self
, columns
=None, db_filter
={}, limit
=None):
1150 Show an openflow controllers from DB.
1151 :param columns: List with SELECT query parameters
1152 :param db_filter: List with where query parameters
1153 :param limit: result Limit
1156 result
, content
= self
.db
.get_table(SELECT
=columns
, FROM
='ofcs', WHERE
=db_filter
, LIMIT
=limit
)
1159 raise ovimException(str(content
), -result
)
1163 def get_tenants(self
, columns
=None, db_filter
={}, limit
=None):
1165 Retrieve tenant list from DB
1166 :param columns: List with SELECT query parameters
1167 :param db_filter: List with where query parameters
1168 :param limit: result limit
1171 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=columns
, WHERE
=db_filter
, LIMIT
=limit
)
1173 raise ovimException('get_tenatns Error {}'.format(str(content
)), -result
)
1175 convert_boolean(content
, ('enabled',))
1178 def show_tenant_id(self
, tenant_id
):
1180 Get tenant from DB by id
1181 :param tenant_id: tenant id
1184 result
, content
= self
.db
.get_table(FROM
='tenants', SELECT
=('uuid', 'name', 'description', 'enabled'),
1185 WHERE
={"uuid": tenant_id
})
1187 raise ovimException(str(content
), -result
)
1189 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1191 convert_boolean(content
, ('enabled',))
1194 def new_tentant(self
, tenant
):
1196 Create a tenant and store in DB
1197 :param tenant: Dictionary with tenant data
1198 :return: the uuid of created tenant. Raise exception upon error
1201 # insert in data base
1202 result
, tenant_uuid
= self
.db
.new_tenant(tenant
)
1207 raise ovimException(str(tenant_uuid
), -result
)
1209 def delete_tentant(self
, tenant_id
):
1211 Delete a tenant from the database.
1212 :param tenant_id: Tenant id
1213 :return: delete tenant id
1217 r
, tenants_flavors
= self
.db
.get_table(FROM
='tenants_flavors', SELECT
=('flavor_id', 'tenant_id'),
1218 WHERE
={'tenant_id': tenant_id
})
1220 tenants_flavors
= ()
1221 r
, tenants_images
= self
.db
.get_table(FROM
='tenants_images', SELECT
=('image_id', 'tenant_id'),
1222 WHERE
={'tenant_id': tenant_id
})
1226 result
, content
= self
.db
.delete_row('tenants', tenant_id
)
1228 raise ovimException("tenant '%s' not found" % tenant_id
, HTTP_Not_Found
)
1230 for flavor
in tenants_flavors
:
1231 self
.db
.delete_row_by_key("flavors", "uuid", flavor
['flavor_id'])
1232 for image
in tenants_images
:
1233 self
.db
.delete_row_by_key("images", "uuid", image
['image_id'])
1236 raise ovimException("Error deleting tenant '%s' " % tenant_id
, HTTP_Internal_Server_Error
)
1238 def edit_tenant(self
, tenant_id
, tenant_data
):
1240 Update a tenant data identified by tenant id
1241 :param tenant_id: tenant id
1242 :param tenant_data: Dictionary with tenant data
1246 # Look for the previous data
1247 result
, tenant_data_old
= self
.db
.get_table(FROM
='tenants', WHERE
={'uuid': tenant_id
})
1249 raise ovimException("Error updating tenant with uuid='{}': {}".format(tenant_id
, tenant_data_old
),
1250 HTTP_Internal_Server_Error
)
1252 raise ovimException("tenant with uuid='{}' not found".format(tenant_id
), HTTP_Not_Found
)
1254 # insert in data base
1255 result
, content
= self
.db
.update_rows('tenants', tenant_data
, WHERE
={'uuid': tenant_id
}, log
=True)
1259 raise ovimException(str(content
), -result
)
1261 def set_of_port_mapping(self
, of_maps
, ofc_id
=None, switch_dpid
=None, region
=None):
1263 Create new port mapping entry
1264 :param of_maps: List with port mapping information
1265 # maps =[{"ofc_id": <ofc_id>,"region": datacenter region,"compute_node": compute uuid,"pci": pci adress,
1266 "switch_dpid": swith dpid,"switch_port": port name,"switch_mac": mac}]
1267 :param ofc_id: ofc id
1268 :param switch_dpid: switch dpid
1269 :param region: datacenter region id
1275 map['ofc_id'] = ofc_id
1277 map['switch_dpid'] = switch_dpid
1279 map['region'] = region
1281 for of_map
in of_maps
:
1282 result
, uuid
= self
.db
.new_row('of_port_mappings', of_map
, True)
1284 of_map
["uuid"] = uuid
1286 raise ovimException(str(uuid
), -result
)
1289 def clear_of_port_mapping(self
, db_filter
={}):
1291 Clear port mapping filtering using db_filter dict
1292 :param db_filter: Parameter to filter during remove process
1295 result
, content
= self
.db
.delete_row_by_dict(FROM
='of_port_mappings', WHERE
=db_filter
)
1300 raise ovimException("Error deleting of_port_mappings with filter='{}'".format(str(db_filter
)),
1301 HTTP_Internal_Server_Error
)
1303 def get_of_port_mappings(self
, column
=None, db_filter
=None, db_limit
=None):
1305 Retrive port mapping from DB
1310 result
, content
= self
.db
.get_table(SELECT
=column
, WHERE
=db_filter
, FROM
='of_port_mappings', LIMIT
=db_limit
)
1313 self
.logger
.error("get_of_port_mappings Error %d %s", result
, content
)
1314 raise ovimException(str(content
), -result
)
1318 def get_dhcp_controller(self
):
1320 Create an host_thread object for manage openvim controller and not create a thread for itself
1321 :return: dhcp_host openvim controller object
1324 if 'openvim_controller' in self
.config
['host_threads']:
1325 return self
.config
['host_threads']['openvim_controller']
1328 controller_ip
= self
.config
['ovs_controller_ip']
1329 ovs_controller_user
= self
.config
['ovs_controller_user']
1331 host_test_mode
= True if self
.config
['mode'] == 'test' or self
.config
['mode'] == "OF only" else False
1332 host_develop_mode
= True if self
.config
['mode'] == 'development' else False
1334 dhcp_host
= ht
.host_thread(name
='openvim_controller', user
=ovs_controller_user
, host
=controller_ip
,
1336 db_lock
=self
.db_lock
, test
=host_test_mode
,
1337 image_path
=self
.config
['image_path'], version
=self
.config
['version'],
1338 host_id
='openvim_controller', develop_mode
=host_develop_mode
,
1339 develop_bridge_iface
=bridge_ifaces
)
1341 self
.config
['host_threads']['openvim_controller'] = dhcp_host
1342 if not host_test_mode
:
1343 dhcp_host
.ssh_connect()
1346 def launch_dhcp_server(self
, vlan
, first_ip
, last_ip
, cidr
, gateway
):
1348 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
1349 :param vlan: vlan identifier
1350 :param first_ip: First dhcp range ip
1351 :param last_ip: Last dhcp range ip
1352 :param cidr: net cidr
1353 :param gateway: net gateway
1356 ip_tools
= IPNetwork(cidr
)
1357 dhcp_netmask
= str(ip_tools
.netmask
)
1358 ip_range
= [first_ip
, last_ip
]
1360 dhcp_path
= self
.config
['ovs_controller_file_path']
1362 controller_host
= self
.get_dhcp_controller()
1363 controller_host
.create_linux_bridge(vlan
)
1364 controller_host
.create_dhcp_interfaces(vlan
, first_ip
, dhcp_netmask
)
1365 controller_host
.launch_dhcp_server(vlan
, ip_range
, dhcp_netmask
, dhcp_path
, gateway
)