2 Copyright (c) 2017 SONATA-NFV and Paderborn University
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
17 Neither the name of the SONATA-NFV, Paderborn University
18 nor the names of its contributors may be used to endorse or promote
19 products derived from this software without specific prior written
22 This work has been performed in the framework of the SONATA project,
23 funded by the European Commission under Grant number 671517 through
24 the Horizon 2020 and 5G-PPP programmes. The authors would like to
25 acknowledge the contributions of their colleagues of the SONATA
26 partner consortium (www.sonata-nfv.eu).
28 from mininet
.link
import Link
30 from resources
import *
31 from docker
import DockerClient
36 import ip_handler
as IP
40 LOG
= logging
.getLogger("api.openstack.compute")
43 class HeatApiStackInvalidException(Exception):
45 Exception thrown when a submitted stack is invalid.
48 def __init__(self
, value
):
52 return repr(self
.value
)
55 class OpenstackCompute(object):
57 This class is a datacenter specific compute object that tracks all containers that are running in a datacenter,
58 as well as networks and configured ports.
59 It has some stack dependet logic and can check if a received stack is valid.
61 It also handles start and stop of containers.
67 self
.computeUnits
= dict()
73 self
.port_pairs
= dict()
74 self
.port_pair_groups
= dict()
75 self
.flow_classifiers
= dict()
76 self
.port_chains
= dict()
77 self
.compute_nets
= dict()
78 self
.dcli
= DockerClient(base_url
='unix://var/run/docker.sock')
83 Updates the known images. Asks the docker daemon for a list of all known images and returns
86 :return: Returns the new image dictionary.
89 for image
in self
.dcli
.images
.list():
90 if len(image
.tags
) > 0:
92 t
= t
.replace(":latest", "") # only use short tag names for OSM compatibility
93 if t
not in self
._images
:
94 self
._images
[t
] = Image(t
)
97 def add_stack(self
, stack
):
99 Adds a new stack to the compute node.
101 :param stack: Stack dictionary.
102 :type stack: :class:`heat.resources.stack`
104 if not self
.check_stack(stack
):
105 self
.clean_broken_stack(stack
)
106 raise HeatApiStackInvalidException("Stack did not pass validity checks")
107 self
.stacks
[stack
.id] = stack
109 def clean_broken_stack(self
, stack
):
110 for port
in stack
.ports
.values():
111 if port
.id in self
.ports
:
112 del self
.ports
[port
.id]
113 for server
in stack
.servers
.values():
114 if server
.id in self
.computeUnits
:
115 del self
.computeUnits
[server
.id]
116 for net
in stack
.nets
.values():
117 if net
.id in self
.nets
:
118 del self
.nets
[net
.id]
120 def check_stack(self
, stack
):
122 Checks all dependencies of all servers, ports and routers and their most important parameters.
124 :param stack: A reference of the stack that should be checked.
125 :type stack: :class:`heat.resources.stack`
126 :return: * *True*: If the stack is completely fine.
131 for server
in stack
.servers
.values():
132 for port_name
in server
.port_names
:
133 if port_name
not in stack
.ports
:
134 LOG
.warning("Server %s of stack %s has a port named %s that is not known." %
135 (server
.name
, stack
.stack_name
, port_name
))
136 everything_ok
= False
137 if server
.image
is None:
138 LOG
.warning("Server %s holds no image." % (server
.name
))
139 everything_ok
= False
140 if server
.command
is None:
141 LOG
.warning("Server %s holds no command." % (server
.name
))
142 everything_ok
= False
143 for port
in stack
.ports
.values():
144 if port
.net_name
not in stack
.nets
:
145 LOG
.warning("Port %s of stack %s has a network named %s that is not known." %
146 (port
.name
, stack
.stack_name
, port
.net_name
))
147 everything_ok
= False
148 if port
.intf_name
is None:
149 LOG
.warning("Port %s has no interface name." % (port
.name
))
150 everything_ok
= False
151 if port
.ip_address
is None:
152 LOG
.warning("Port %s has no IP address." % (port
.name
))
153 everything_ok
= False
154 for router
in stack
.routers
.values():
155 for subnet_name
in router
.subnet_names
:
157 for net
in stack
.nets
.values():
158 if net
.subnet_name
== subnet_name
:
162 LOG
.warning("Router %s of stack %s has a network named %s that is not known." %
163 (router
.name
, stack
.stack_name
, subnet_name
))
164 everything_ok
= False
167 def add_flavor(self
, name
, cpu
, memory
, memory_unit
, storage
, storage_unit
):
169 Adds a flavor to the stack.
171 :param name: Specifies the name of the flavor.
176 :type memory: ``str``
178 :type memory_unit: ``str``
180 :type storage: ``str``
182 :type storage_unit: ``str``
184 flavor
= InstanceFlavor(name
, cpu
, memory
, memory_unit
, storage
, storage_unit
)
185 self
.flavors
[flavor
.name
] = flavor
188 def deploy_stack(self
, stackid
):
190 Deploys the stack and starts the emulation.
192 :param stackid: An UUID str of the stack
193 :type stackid: ``str``
194 :return: * *False*: If the Datacenter is None
201 stack
= self
.stacks
[stackid
]
202 self
.update_compute_dicts(stack
)
204 # Create the networks first
205 for server
in stack
.servers
.values():
206 self
._start
_compute
(server
)
209 def delete_stack(self
, stack_id
):
211 Delete a stack and all its components.
213 :param stack_id: An UUID str of the stack
214 :type stack_id: ``str``
215 :return: * *False*: If the Datacenter is None
222 # Stop all servers and their links of this stack
223 for server
in self
.stacks
[stack_id
].servers
.values():
224 self
.stop_compute(server
)
225 self
.delete_server(server
)
226 for net
in self
.stacks
[stack_id
].nets
.values():
227 self
.delete_network(net
.id)
228 for port
in self
.stacks
[stack_id
].ports
.values():
229 self
.delete_port(port
.id)
231 del self
.stacks
[stack_id
]
234 def update_stack(self
, old_stack_id
, new_stack
):
236 Determines differences within the old and the new stack and deletes, create or changes only parts that
237 differ between the two stacks.
239 :param old_stack_id: The ID of the old stack.
240 :type old_stack_id: ``str``
241 :param new_stack: A reference of the new stack.
242 :type new_stack: :class:`heat.resources.stack`
243 :return: * *True*: if the old stack could be updated to the new stack without any error.
247 LOG
.debug("updating stack {} with new_stack {}".format(old_stack_id
, new_stack
))
248 if old_stack_id
not in self
.stacks
:
250 old_stack
= self
.stacks
[old_stack_id
]
253 for server
in old_stack
.servers
.values():
254 if server
.name
in new_stack
.servers
:
255 new_stack
.servers
[server
.name
].id = server
.id
256 for net
in old_stack
.nets
.values():
257 if net
.name
in new_stack
.nets
:
258 new_stack
.nets
[net
.name
].id = net
.id
259 for subnet
in new_stack
.nets
.values():
260 if subnet
.subnet_name
== net
.subnet_name
:
261 subnet
.subnet_id
= net
.subnet_id
263 for port
in old_stack
.ports
.values():
264 if port
.name
in new_stack
.ports
:
265 new_stack
.ports
[port
.name
].id = port
.id
266 for router
in old_stack
.routers
.values():
267 if router
.name
in new_stack
.routers
:
268 new_stack
.routers
[router
.name
].id = router
.id
270 # Update the compute dicts to now contain the new_stack components
271 self
.update_compute_dicts(new_stack
)
273 self
.update_ip_addresses(old_stack
, new_stack
)
275 # Update all interface names - after each port has the correct UUID!!
276 for port
in new_stack
.ports
.values():
277 port
.create_intf_name()
279 if not self
.check_stack(new_stack
):
282 # Remove unnecessary networks
283 for net
in old_stack
.nets
.values():
284 if not net
.name
in new_stack
.nets
:
285 self
.delete_network(net
.id)
287 # Remove all unnecessary servers
288 for server
in old_stack
.servers
.values():
289 if server
.name
in new_stack
.servers
:
290 if not server
.compare_attributes(new_stack
.servers
[server
.name
]):
291 self
.stop_compute(server
)
293 # Delete unused and changed links
294 for port_name
in server
.port_names
:
295 if port_name
in old_stack
.ports
and port_name
in new_stack
.ports
:
296 if not old_stack
.ports
.get(port_name
) == new_stack
.ports
.get(port_name
):
297 my_links
= self
.dc
.net
.links
298 for link
in my_links
:
299 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
300 str(link
.intf1
.ip
) == \
301 old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
302 self
._remove
_link
(server
.name
, link
)
305 self
._add
_link
(server
.name
,
306 new_stack
.ports
[port_name
].ip_address
,
307 new_stack
.ports
[port_name
].intf_name
,
308 new_stack
.ports
[port_name
].net_name
)
311 my_links
= self
.dc
.net
.links
312 for link
in my_links
:
313 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
314 str(link
.intf1
.ip
) == old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
315 self
._remove
_link
(server
.name
, link
)
319 for port_name
in new_stack
.servers
[server
.name
].port_names
:
320 if port_name
not in server
.port_names
:
321 self
._add
_link
(server
.name
,
322 new_stack
.ports
[port_name
].ip_address
,
323 new_stack
.ports
[port_name
].intf_name
,
324 new_stack
.ports
[port_name
].net_name
)
326 self
.stop_compute(server
)
328 # Start all new servers
329 for server
in new_stack
.servers
.values():
330 if server
.name
not in self
.dc
.containers
:
331 self
._start
_compute
(server
)
333 server
.emulator_compute
= self
.dc
.containers
.get(server
.name
)
335 del self
.stacks
[old_stack_id
]
336 self
.stacks
[new_stack
.id] = new_stack
339 def update_ip_addresses(self
, old_stack
, new_stack
):
341 Updates the subnet and the port IP addresses - which should always be in this order!
343 :param old_stack: The currently running stack
344 :type old_stack: :class:`heat.resources.stack`
345 :param new_stack: The new created stack
346 :type new_stack: :class:`heat.resources.stack`
348 self
.update_subnet_cidr(old_stack
, new_stack
)
349 self
.update_port_addresses(old_stack
, new_stack
)
351 def update_port_addresses(self
, old_stack
, new_stack
):
353 Updates the port IP addresses. First resets all issued addresses. Then get all IP addresses from the old
354 stack and sets them to the same ports in the new stack. Finally all new or changed instances will get new
357 :param old_stack: The currently running stack
358 :type old_stack: :class:`heat.resources.stack`
359 :param new_stack: The new created stack
360 :type new_stack: :class:`heat.resources.stack`
362 for net
in new_stack
.nets
.values():
363 net
.reset_issued_ip_addresses()
365 for old_port
in old_stack
.ports
.values():
366 for port
in new_stack
.ports
.values():
367 if port
.compare_attributes(old_port
):
368 for net
in new_stack
.nets
.values():
369 if net
.name
== port
.net_name
:
370 if net
.assign_ip_address(old_port
.ip_address
, port
.name
):
371 port
.ip_address
= old_port
.ip_address
372 port
.mac_address
= old_port
.mac_address
374 port
.ip_address
= net
.get_new_ip_address(port
.name
)
376 for port
in new_stack
.ports
.values():
377 for net
in new_stack
.nets
.values():
378 if port
.net_name
== net
.name
and not net
.is_my_ip(port
.ip_address
, port
.name
):
379 port
.ip_address
= net
.get_new_ip_address(port
.name
)
381 def update_subnet_cidr(self
, old_stack
, new_stack
):
383 Updates the subnet IP addresses. If the new stack contains subnets from the old stack it will take those
384 IP addresses. Otherwise it will create new IP addresses for the subnet.
386 :param old_stack: The currently running stack
387 :type old_stack: :class:`heat.resources.stack`
388 :param new_stack: The new created stack
389 :type new_stack: :class:`heat.resources.stack`
391 for old_subnet
in old_stack
.nets
.values():
392 IP
.free_cidr(old_subnet
.get_cidr(), old_subnet
.subnet_id
)
394 for subnet
in new_stack
.nets
.values():
396 for old_subnet
in old_stack
.nets
.values():
397 if subnet
.subnet_name
== old_subnet
.subnet_name
:
398 if IP
.assign_cidr(old_subnet
.get_cidr(), subnet
.subnet_id
):
399 subnet
.set_cidr(old_subnet
.get_cidr())
401 for subnet
in new_stack
.nets
.values():
402 if IP
.is_cidr_issued(subnet
.get_cidr()):
405 cird
= IP
.get_new_cidr(subnet
.subnet_id
)
406 subnet
.set_cidr(cird
)
409 def update_compute_dicts(self
, stack
):
411 Update and add all stack components tho the compute dictionaries.
413 :param stack: A stack reference, to get all required components.
414 :type stack: :class:`heat.resources.stack`
416 for server
in stack
.servers
.values():
417 self
.computeUnits
[server
.id] = server
418 if isinstance(server
.flavor
, dict):
419 self
.add_flavor(server
.flavor
['flavorName'],
420 server
.flavor
['vcpu'],
421 server
.flavor
['ram'], 'MB',
422 server
.flavor
['storage'], 'GB')
423 server
.flavor
= server
.flavor
['flavorName']
424 for router
in stack
.routers
.values():
425 self
.routers
[router
.id] = router
426 for net
in stack
.nets
.values():
427 self
.nets
[net
.id] = net
428 for port
in stack
.ports
.values():
429 self
.ports
[port
.id] = port
431 def _start_compute(self
, server
):
433 Starts a new compute object (docker container) inside the emulator.
434 Should only be called by stack modifications and not directly.
436 :param server: Specifies the compute resource.
437 :type server: :class:`heat.resources.server`
439 LOG
.debug("Starting new compute resources %s" % server
.name
)
441 network_dict
= dict()
443 for port_name
in server
.port_names
:
444 network_dict
= dict()
445 port
= self
.find_port_by_name_or_id(port_name
)
447 network_dict
['id'] = port
.intf_name
448 network_dict
['ip'] = port
.ip_address
449 network_dict
[network_dict
['id']] = self
.find_network_by_name_or_id(port
.net_name
).name
450 network
.append(network_dict
)
451 # default network dict
453 network_dict
['id'] = server
.name
+ "-eth0"
454 network_dict
[network_dict
['id']] = network_dict
['id']
455 network
.append(network_dict
)
457 self
.compute_nets
[server
.name
] = network
458 LOG
.debug("Network dict: {}".format(network
))
459 c
= self
.dc
.startCompute(server
.name
, image
=server
.image
, command
=server
.command
,
460 network
=network
, flavor_name
=server
.flavor
,
461 properties
=server
.properties
)
462 server
.emulator_compute
= c
464 for intf
in c
.intfs
.values():
465 for port_name
in server
.port_names
:
466 port
= self
.find_port_by_name_or_id(port_name
)
468 if intf
.name
== port
.intf_name
:
469 # wait up to one second for the intf to come up
470 self
.timeout_sleep(intf
.isUp
, 1)
471 if port
.mac_address
is not None:
472 intf
.setMAC(port
.mac_address
)
474 port
.mac_address
= intf
.MAC()
476 # Start the real emulator command now as specified in the dockerfile
478 config
= c
.dcinfo
.get("Config", dict())
479 env
= config
.get("Env", list())
481 if "SON_EMU_CMD=" in env_var
:
482 cmd
= str(env_var
.split("=")[1])
483 server
.son_emu_command
= cmd
484 # execute command in new thread to ensure that GK is not blocked by VNF
485 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
489 def stop_compute(self
, server
):
491 Determines which links should be removed before removing the server itself.
493 :param server: The server that should be removed
494 :type server: ``heat.resources.server``
496 LOG
.debug("Stopping container %s with full name %s" % (server
.name
, server
.full_name
))
498 for port_name
in server
.port_names
:
499 prt
= self
.find_port_by_name_or_id(port_name
)
501 link_names
.append(prt
.intf_name
)
502 my_links
= self
.dc
.net
.links
503 for link
in my_links
:
504 if str(link
.intf1
) in link_names
:
505 # Remove all self created links that connect the server to the main switch
506 self
._remove
_link
(server
.name
, link
)
508 # Stop the server and the remaining connection to the datacenter switch
509 self
.dc
.stopCompute(server
.name
)
510 # Only now delete all its ports and the server itself
511 for port_name
in server
.port_names
:
512 self
.delete_port(port_name
)
513 self
.delete_server(server
)
515 def find_server_by_name_or_id(self
, name_or_id
):
517 Tries to find the server by ID and if this does not succeed then tries to find it via name.
519 :param name_or_id: UUID or name of the server.
520 :type name_or_id: ``str``
521 :return: Returns the server reference if it was found or None
522 :rtype: :class:`heat.resources.server`
524 if name_or_id
in self
.computeUnits
:
525 return self
.computeUnits
[name_or_id
]
527 if self
._shorten
_server
_name
(name_or_id
) in self
.computeUnits
:
528 return self
.computeUnits
[name_or_id
]
530 for server
in self
.computeUnits
.values():
531 if server
.name
== name_or_id
or server
.template_name
== name_or_id
or server
.full_name
== name_or_id
:
533 if (server
.name
== self
._shorten
_server
_name
(name_or_id
)
534 or server
.template_name
== self
._shorten
_server
_name
(name_or_id
)
535 or server
.full_name
== self
._shorten
_server
_name
(name_or_id
)):
539 def create_server(self
, name
, stack_operation
=False):
541 Creates a server with the specified name. Raises an exception when a server with the given name already
544 :param name: Name of the new server.
546 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
547 :type stack_operation: ``bool``
548 :return: Returns the created server.
549 :rtype: :class:`heat.resources.server`
551 if self
.find_server_by_name_or_id(name
) is not None and not stack_operation
:
552 raise Exception("Server with name %s already exists." % name
)
553 safe_name
= self
._shorten
_server
_name
(name
)
554 server
= Server(safe_name
)
555 server
.id = str(uuid
.uuid4())
556 if not stack_operation
:
557 self
.computeUnits
[server
.id] = server
560 def _shorten_server_name(self
, name
, char_limit
=9):
562 Docker does not like too long instance names.
563 This function provides a shorter name if needed
565 if len(name
) > char_limit
:
566 LOG
.info("Long server name: {}".format(name
))
567 # construct a short name
568 h
= hashlib
.sha224(name
).hexdigest()
570 LOG
.info("Short server name: {}".format(h
))
574 def delete_server(self
, server
):
576 Deletes the given server from the stack dictionary and the computeUnits dictionary.
578 :param server: Reference of the server that should be deleted.
579 :type server: :class:`heat.resources.server`
580 :return: * *False*: If the server name is not in the correct format ('datacentername_stackname_servername') \
581 or when no stack with the correct stackname was found.
587 name_parts
= server
.name
.split('_')
588 if len(name_parts
) > 1:
589 for stack
in self
.stacks
.values():
590 if stack
.stack_name
== name_parts
[1]:
591 stack
.servers
.pop(server
.id, None)
592 if self
.computeUnits
.pop(server
.id, None) is None:
596 def find_network_by_name_or_id(self
, name_or_id
):
598 Tries to find the network by ID and if this does not succeed then tries to find it via name.
600 :param name_or_id: UUID or name of the network.
601 :type name_or_id: ``str``
602 :return: Returns the network reference if it was found or None
603 :rtype: :class:`heat.resources.net`
605 if name_or_id
in self
.nets
:
606 return self
.nets
[name_or_id
]
607 for net
in self
.nets
.values():
608 if net
.name
== name_or_id
:
613 def create_network(self
, name
, stack_operation
=False):
615 Creates a new network with the given name. Raises an exception when a network with the given name already
618 :param name: Name of the new network.
620 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
621 :type stack_operation: ``bool``
622 :return: :class:`heat.resources.net`
624 LOG
.debug("Creating network with name %s" % name
)
625 if self
.find_network_by_name_or_id(name
) is not None and not stack_operation
:
626 LOG
.warning("Creating network with name %s failed, as it already exists" % name
)
627 raise Exception("Network with name %s already exists." % name
)
629 network
.id = str(uuid
.uuid4())
630 if not stack_operation
:
631 self
.nets
[network
.id] = network
634 def delete_network(self
, name_or_id
):
636 Deletes the given network.
638 :param name_or_id: Name or UUID of the network.
639 :type name_or_id: ``str``
641 net
= self
.find_network_by_name_or_id(name_or_id
)
643 raise Exception("Network with name or id %s does not exists." % name_or_id
)
645 for stack
in self
.stacks
.values():
646 stack
.nets
.pop(net
.name
, None)
648 self
.nets
.pop(net
.id, None)
650 def create_port(self
, name
, stack_operation
=False):
652 Creates a new port with the given name. Raises an exception when a port with the given name already
655 :param name: Name of the new port.
657 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
658 :type stack_operation: ``bool``
659 :return: Returns the created port.
660 :rtype: :class:`heat.resources.port`
662 port
= self
.find_port_by_name_or_id(name
)
663 if port
is not None and not stack_operation
:
664 LOG
.warning("Creating port with name %s failed, as it already exists" % name
)
665 raise Exception("Port with name %s already exists." % name
)
666 LOG
.debug("Creating port with name %s" % name
)
668 if not stack_operation
:
669 self
.ports
[port
.id] = port
670 port
.create_intf_name()
673 def find_port_by_name_or_id(self
, name_or_id
):
675 Tries to find the port by ID and if this does not succeed then tries to find it via name.
677 :param name_or_id: UUID or name of the network.
678 :type name_or_id: ``str``
679 :return: Returns the port reference if it was found or None
680 :rtype: :class:`heat.resources.port`
682 if name_or_id
in self
.ports
:
683 return self
.ports
[name_or_id
]
684 for port
in self
.ports
.values():
685 if port
.name
== name_or_id
or port
.template_name
== name_or_id
:
690 def delete_port(self
, name_or_id
):
692 Deletes the given port. Raises an exception when the port was not found!
694 :param name_or_id: UUID or name of the port.
695 :type name_or_id: ``str``
697 port
= self
.find_port_by_name_or_id(name_or_id
)
699 LOG
.warning("Port with name or id %s does not exist. Can't delete it." % name_or_id
)
702 my_links
= self
.dc
.net
.links
703 for link
in my_links
:
704 if str(link
.intf1
) == port
.intf_name
and \
705 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
706 self
._remove
_link
(link
.intf1
.node
.name
, link
)
709 self
.ports
.pop(port
.id, None)
710 for stack
in self
.stacks
.values():
711 stack
.ports
.pop(port
.name
, None)
713 def create_port_pair(self
, name
, stack_operation
=False):
715 Creates a new port pair with the given name. Raises an exception when a port pair with the given name already
718 :param name: Name of the new port pair.
720 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
721 :type stack_operation: ``bool``
722 :return: Returns the created port pair.
723 :rtype: :class:`openstack.resources.port_pair`
725 port_pair
= self
.find_port_pair_by_name_or_id(name
)
726 if port_pair
is not None and not stack_operation
:
727 logging
.warning("Creating port pair with name %s failed, as it already exists" % name
)
728 raise Exception("Port pair with name %s already exists." % name
)
729 logging
.debug("Creating port pair with name %s" % name
)
730 port_pair
= PortPair(name
)
731 if not stack_operation
:
732 self
.port_pairs
[port_pair
.id] = port_pair
735 def find_port_pair_by_name_or_id(self
, name_or_id
):
737 Tries to find the port pair by ID and if this does not succeed then tries to find it via name.
739 :param name_or_id: UUID or name of the port pair.
740 :type name_or_id: ``str``
741 :return: Returns the port pair reference if it was found or None
742 :rtype: :class:`openstack.resources.port_pair`
744 if name_or_id
in self
.port_pairs
:
745 return self
.port_pairs
[name_or_id
]
746 for port_pair
in self
.port_pairs
.values():
747 if port_pair
.name
== name_or_id
:
752 def delete_port_pair(self
, name_or_id
):
754 Deletes the given port pair. Raises an exception when the port pair was not found!
756 :param name_or_id: UUID or name of the port pair.
757 :type name_or_id: ``str``
759 port_pair
= self
.find_port_pair_by_name_or_id(name_or_id
)
760 if port_pair
is None:
761 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
763 self
.port_pairs
.pop(port_pair
.id, None)
765 def create_port_pair_group(self
, name
, stack_operation
=False):
767 Creates a new port pair group with the given name. Raises an exception when a port pair group
768 with the given name already exists!
770 :param name: Name of the new port pair group.
772 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
773 :type stack_operation: ``bool``
774 :return: Returns the created port pair group .
775 :rtype: :class:`openstack.resources.port_pair_group`
777 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name
)
778 if port_pair_group
is not None and not stack_operation
:
779 logging
.warning("Creating port pair group with name %s failed, as it already exists" % name
)
780 raise Exception("Port pair group with name %s already exists." % name
)
781 logging
.debug("Creating port pair group with name %s" % name
)
782 port_pair_group
= PortPairGroup(name
)
783 if not stack_operation
:
784 self
.port_pair_groups
[port_pair_group
.id] = port_pair_group
785 return port_pair_group
787 def find_port_pair_group_by_name_or_id(self
, name_or_id
):
789 Tries to find the port pair group by ID and if this does not succeed then tries to find it via name.
791 :param name_or_id: UUID or name of the port pair group.
792 :type name_or_id: ``str``
793 :return: Returns the port pair group reference if it was found or None
794 :rtype: :class:`openstack.resources.port_pair_group`
796 if name_or_id
in self
.port_pair_groups
:
797 return self
.port_pair_groups
[name_or_id
]
798 for port_pair_group
in self
.port_pair_groups
.values():
799 if port_pair_group
.name
== name_or_id
:
800 return port_pair_group
804 def delete_port_pair_group(self
, name_or_id
):
806 Deletes the given port pair group. Raises an exception when the port pair group was not found!
808 :param name_or_id: UUID or name of the port pair group.
809 :type name_or_id: ``str``
811 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name_or_id
)
812 if port_pair_group
is None:
813 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
815 self
.port_pair_groups
.pop(port_pair_group
.id, None)
817 def create_port_chain(self
, name
, stack_operation
=False):
819 Creates a new port chain with the given name. Raises an exception when a port chain with the given name already
822 :param name: Name of the new port chain
824 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
825 :type stack_operation: ``bool``
826 :return: Returns the created port chain.
827 :rtype: :class:`openstack.resources.port_chain.PortChain`
829 port_chain
= self
.find_port_chain_by_name_or_id(name
)
830 if port_chain
is not None and not stack_operation
:
831 logging
.warning("Creating port chain with name %s failed, as it already exists" % name
)
832 raise Exception("Port chain with name %s already exists." % name
)
833 logging
.debug("Creating port chain with name %s" % name
)
834 port_chain
= PortChain(name
)
835 if not stack_operation
:
836 self
.port_chains
[port_chain
.id] = port_chain
839 def find_port_chain_by_name_or_id(self
, name_or_id
):
841 Tries to find the port chain by ID and if this does not succeed then tries to find it via name.
843 :param name_or_id: UUID or name of the port chain.
844 :type name_or_id: ``str``
845 :return: Returns the port chain reference if it was found or None
846 :rtype: :class:`openstack.resources.port_chain.PortChain`
848 if name_or_id
in self
.port_chains
:
849 return self
.port_chains
[name_or_id
]
850 for port_chain
in self
.port_chains
.values():
851 if port_chain
.name
== name_or_id
:
855 def delete_port_chain(self
, name_or_id
):
857 Deletes the given port chain. Raises an exception when the port chain was not found!
859 :param name_or_id: UUID or name of the port chain.
860 :type name_or_id: ``str``
862 port_chain
= self
.find_port_chain_by_name_or_id(name_or_id
)
863 port_chain
.uninstall(self
)
864 if port_chain
is None:
865 raise Exception("Port chain with name or id %s does not exists." % name_or_id
)
867 self
.port_chains
.pop(port_chain
.id, None)
869 def create_flow_classifier(self
, name
, stack_operation
=False):
871 Creates a new flow classifier with the given name. Raises an exception when a flow classifier with the given name already
874 :param name: Name of the new flow classifier.
876 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
877 :type stack_operation: ``bool``
878 :return: Returns the created flow classifier.
879 :rtype: :class:`openstack.resources.flow_classifier`
881 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name
)
882 if flow_classifier
is not None and not stack_operation
:
883 logging
.warning("Creating flow classifier with name %s failed, as it already exists" % name
)
884 raise Exception("Flow classifier with name %s already exists." % name
)
885 logging
.debug("Creating flow classifier with name %s" % name
)
886 flow_classifier
= FlowClassifier(name
)
887 if not stack_operation
:
888 self
.flow_classifiers
[flow_classifier
.id] = flow_classifier
889 return flow_classifier
891 def find_flow_classifier_by_name_or_id(self
, name_or_id
):
893 Tries to find the flow classifier by ID and if this does not succeed then tries to find it via name.
895 :param name_or_id: UUID or name of the flow classifier.
896 :type name_or_id: ``str``
897 :return: Returns the flow classifier reference if it was found or None
898 :rtype: :class:`openstack.resources.flow_classifier`
900 if name_or_id
in self
.flow_classifiers
:
901 return self
.flow_classifiers
[name_or_id
]
902 for flow_classifier
in self
.flow_classifiers
.values():
903 if flow_classifier
.name
== name_or_id
:
904 return flow_classifier
908 def delete_flow_classifier(self
, name_or_id
):
910 Deletes the given flow classifier. Raises an exception when the flow classifier was not found!
912 :param name_or_id: UUID or name of the flow classifier.
913 :type name_or_id: ``str``
915 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name_or_id
)
916 if flow_classifier
is None:
917 raise Exception("Flow classifier with name or id %s does not exists." % name_or_id
)
919 self
.flow_classifiers
.pop(flow_classifier
.id, None)
921 def _add_link(self
, node_name
, ip_address
, link_name
, net_name
):
923 Adds a new link between datacenter switch and the node with the given name.
925 :param node_name: Name of the required node.
926 :type node_name: ``str``
927 :param ip_address: IP-Address of the node.
928 :type ip_address: ``str``
929 :param link_name: Link name.
930 :type link_name: ``str``
931 :param net_name: Network name.
932 :type net_name: ``str``
934 node
= self
.dc
.net
.get(node_name
)
935 params
= {'params1': {'ip': ip_address
,
937 link_name
: net_name
},
938 'intfName1': link_name
,
940 link
= self
.dc
.net
.addLink(node
, self
.dc
.switch
, **params
)
941 OpenstackCompute
.timeout_sleep(link
.intf1
.isUp
, 1)
943 def _remove_link(self
, server_name
, link
):
945 Removes a link between server and datacenter switch.
947 :param server_name: Specifies the server where the link starts.
948 :type server_name: ``str``
949 :param link: A reference of the link which should be removed.
950 :type link: :class:`mininet.link`
952 self
.dc
.switch
.detach(link
.intf2
)
953 del self
.dc
.switch
.intfs
[self
.dc
.switch
.ports
[link
.intf2
]]
954 del self
.dc
.switch
.ports
[link
.intf2
]
955 del self
.dc
.switch
.nameToIntf
[link
.intf2
.name
]
956 self
.dc
.net
.removeLink(link
=link
)
957 for intf_key
in self
.dc
.net
[server_name
].intfs
.keys():
958 if self
.dc
.net
[server_name
].intfs
[intf_key
].link
== link
:
959 self
.dc
.net
[server_name
].intfs
[intf_key
].delete()
960 del self
.dc
.net
[server_name
].intfs
[intf_key
]
963 def timeout_sleep(function
, max_sleep
):
965 This function will execute a function all 0.1 seconds until it successfully returns.
966 Will return after `max_sleep` seconds if not successful.
968 :param function: The function to execute. Should return true if done.
969 :type function: ``function``
970 :param max_sleep: Max seconds to sleep. 1 equals 1 second.
971 :type max_sleep: ``float``
973 current_time
= time
.time()
974 stop_time
= current_time
+ max_sleep
975 while not function() and current_time
< stop_time
:
976 current_time
= time
.time()