88d951161c0a0c51a9c01a061f572844b228edf6
1 from mininet
.link
import Link
3 from resources
import *
4 from docker
import DockerClient
9 import ip_handler
as IP
12 LOG
= logging
.getLogger("api.openstack.compute")
15 class HeatApiStackInvalidException(Exception):
17 Exception thrown when a submitted stack is invalid.
20 def __init__(self
, value
):
24 return repr(self
.value
)
27 class OpenstackCompute(object):
29 This class is a datacenter specific compute object that tracks all containers that are running in a datacenter,
30 as well as networks and configured ports.
31 It has some stack dependet logic and can check if a received stack is valid.
33 It also handles start and stop of containers.
39 self
.computeUnits
= dict()
45 self
.port_pairs
= dict()
46 self
.port_pair_groups
= dict()
47 self
.flow_classifiers
= dict()
48 self
.port_chains
= dict()
49 self
.compute_nets
= dict()
50 self
.dcli
= DockerClient(base_url
='unix://var/run/docker.sock')
55 Updates the known images. Asks the docker daemon for a list of all known images and returns
58 :return: Returns the new image dictionary.
61 for image
in self
.dcli
.images
.list():
62 if len(image
.tags
) > 0:
64 t
= t
.replace(":latest", "") # only use short tag names for OSM compatibility
65 if t
not in self
._images
:
66 self
._images
[t
] = Image(t
)
69 def add_stack(self
, stack
):
71 Adds a new stack to the compute node.
73 :param stack: Stack dictionary.
74 :type stack: :class:`heat.resources.stack`
76 if not self
.check_stack(stack
):
77 self
.clean_broken_stack(stack
)
78 raise HeatApiStackInvalidException("Stack did not pass validity checks")
79 self
.stacks
[stack
.id] = stack
81 def clean_broken_stack(self
, stack
):
82 for port
in stack
.ports
.values():
83 if port
.id in self
.ports
:
84 del self
.ports
[port
.id]
85 for server
in stack
.servers
.values():
86 if server
.id in self
.computeUnits
:
87 del self
.computeUnits
[server
.id]
88 for net
in stack
.nets
.values():
89 if net
.id in self
.nets
:
92 def check_stack(self
, stack
):
94 Checks all dependencies of all servers, ports and routers and their most important parameters.
96 :param stack: A reference of the stack that should be checked.
97 :type stack: :class:`heat.resources.stack`
98 :return: * *True*: If the stack is completely fine.
103 for server
in stack
.servers
.values():
104 for port_name
in server
.port_names
:
105 if port_name
not in stack
.ports
:
106 LOG
.warning("Server %s of stack %s has a port named %s that is not known." %
107 (server
.name
, stack
.stack_name
, port_name
))
108 everything_ok
= False
109 if server
.image
is None:
110 LOG
.warning("Server %s holds no image." % (server
.name
))
111 everything_ok
= False
112 if server
.command
is None:
113 LOG
.warning("Server %s holds no command." % (server
.name
))
114 everything_ok
= False
115 for port
in stack
.ports
.values():
116 if port
.net_name
not in stack
.nets
:
117 LOG
.warning("Port %s of stack %s has a network named %s that is not known." %
118 (port
.name
, stack
.stack_name
, port
.net_name
))
119 everything_ok
= False
120 if port
.intf_name
is None:
121 LOG
.warning("Port %s has no interface name." % (port
.name
))
122 everything_ok
= False
123 if port
.ip_address
is None:
124 LOG
.warning("Port %s has no IP address." % (port
.name
))
125 everything_ok
= False
126 for router
in stack
.routers
.values():
127 for subnet_name
in router
.subnet_names
:
129 for net
in stack
.nets
.values():
130 if net
.subnet_name
== subnet_name
:
134 LOG
.warning("Router %s of stack %s has a network named %s that is not known." %
135 (router
.name
, stack
.stack_name
, subnet_name
))
136 everything_ok
= False
139 def add_flavor(self
, name
, cpu
, memory
, memory_unit
, storage
, storage_unit
):
141 Adds a flavor to the stack.
143 :param name: Specifies the name of the flavor.
148 :type memory: ``str``
150 :type memory_unit: ``str``
152 :type storage: ``str``
154 :type storage_unit: ``str``
156 flavor
= InstanceFlavor(name
, cpu
, memory
, memory_unit
, storage
, storage_unit
)
157 self
.flavors
[flavor
.name
] = flavor
160 def deploy_stack(self
, stackid
):
162 Deploys the stack and starts the emulation.
164 :param stackid: An UUID str of the stack
165 :type stackid: ``str``
166 :return: * *False*: If the Datacenter is None
173 stack
= self
.stacks
[stackid
]
174 self
.update_compute_dicts(stack
)
176 # Create the networks first
177 for server
in stack
.servers
.values():
178 self
._start
_compute
(server
)
181 def delete_stack(self
, stack_id
):
183 Delete a stack and all its components.
185 :param stack_id: An UUID str of the stack
186 :type stack_id: ``str``
187 :return: * *False*: If the Datacenter is None
194 # Stop all servers and their links of this stack
195 for server
in self
.stacks
[stack_id
].servers
.values():
196 self
.stop_compute(server
)
197 self
.delete_server(server
)
198 for net
in self
.stacks
[stack_id
].nets
.values():
199 self
.delete_network(net
.id)
200 for port
in self
.stacks
[stack_id
].ports
.values():
201 self
.delete_port(port
.id)
203 del self
.stacks
[stack_id
]
206 def update_stack(self
, old_stack_id
, new_stack
):
208 Determines differences within the old and the new stack and deletes, create or changes only parts that
209 differ between the two stacks.
211 :param old_stack_id: The ID of the old stack.
212 :type old_stack_id: ``str``
213 :param new_stack: A reference of the new stack.
214 :type new_stack: :class:`heat.resources.stack`
215 :return: * *True*: if the old stack could be updated to the new stack without any error.
219 LOG
.debug("updating stack {} with new_stack {}".format(old_stack_id
, new_stack
))
220 if old_stack_id
not in self
.stacks
:
222 old_stack
= self
.stacks
[old_stack_id
]
225 for server
in old_stack
.servers
.values():
226 if server
.name
in new_stack
.servers
:
227 new_stack
.servers
[server
.name
].id = server
.id
228 for net
in old_stack
.nets
.values():
229 if net
.name
in new_stack
.nets
:
230 new_stack
.nets
[net
.name
].id = net
.id
231 for subnet
in new_stack
.nets
.values():
232 if subnet
.subnet_name
== net
.subnet_name
:
233 subnet
.subnet_id
= net
.subnet_id
235 for port
in old_stack
.ports
.values():
236 if port
.name
in new_stack
.ports
:
237 new_stack
.ports
[port
.name
].id = port
.id
238 for router
in old_stack
.routers
.values():
239 if router
.name
in new_stack
.routers
:
240 new_stack
.routers
[router
.name
].id = router
.id
242 # Update the compute dicts to now contain the new_stack components
243 self
.update_compute_dicts(new_stack
)
245 self
.update_ip_addresses(old_stack
, new_stack
)
247 # Update all interface names - after each port has the correct UUID!!
248 for port
in new_stack
.ports
.values():
249 port
.create_intf_name()
251 if not self
.check_stack(new_stack
):
254 # Remove unnecessary networks
255 for net
in old_stack
.nets
.values():
256 if not net
.name
in new_stack
.nets
:
257 self
.delete_network(net
.id)
259 # Remove all unnecessary servers
260 for server
in old_stack
.servers
.values():
261 if server
.name
in new_stack
.servers
:
262 if not server
.compare_attributes(new_stack
.servers
[server
.name
]):
263 self
.stop_compute(server
)
265 # Delete unused and changed links
266 for port_name
in server
.port_names
:
267 if port_name
in old_stack
.ports
and port_name
in new_stack
.ports
:
268 if not old_stack
.ports
.get(port_name
) == new_stack
.ports
.get(port_name
):
269 my_links
= self
.dc
.net
.links
270 for link
in my_links
:
271 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
272 str(link
.intf1
.ip
) == \
273 old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
274 self
._remove
_link
(server
.name
, link
)
277 self
._add
_link
(server
.name
,
278 new_stack
.ports
[port_name
].ip_address
,
279 new_stack
.ports
[port_name
].intf_name
,
280 new_stack
.ports
[port_name
].net_name
)
283 my_links
= self
.dc
.net
.links
284 for link
in my_links
:
285 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
286 str(link
.intf1
.ip
) == old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
287 self
._remove
_link
(server
.name
, link
)
291 for port_name
in new_stack
.servers
[server
.name
].port_names
:
292 if port_name
not in server
.port_names
:
293 self
._add
_link
(server
.name
,
294 new_stack
.ports
[port_name
].ip_address
,
295 new_stack
.ports
[port_name
].intf_name
,
296 new_stack
.ports
[port_name
].net_name
)
298 self
.stop_compute(server
)
300 # Start all new servers
301 for server
in new_stack
.servers
.values():
302 if server
.name
not in self
.dc
.containers
:
303 self
._start
_compute
(server
)
305 server
.emulator_compute
= self
.dc
.containers
.get(server
.name
)
307 del self
.stacks
[old_stack_id
]
308 self
.stacks
[new_stack
.id] = new_stack
311 def update_ip_addresses(self
, old_stack
, new_stack
):
313 Updates the subnet and the port IP addresses - which should always be in this order!
315 :param old_stack: The currently running stack
316 :type old_stack: :class:`heat.resources.stack`
317 :param new_stack: The new created stack
318 :type new_stack: :class:`heat.resources.stack`
320 self
.update_subnet_cidr(old_stack
, new_stack
)
321 self
.update_port_addresses(old_stack
, new_stack
)
323 def update_port_addresses(self
, old_stack
, new_stack
):
325 Updates the port IP addresses. First resets all issued addresses. Then get all IP addresses from the old
326 stack and sets them to the same ports in the new stack. Finally all new or changed instances will get new
329 :param old_stack: The currently running stack
330 :type old_stack: :class:`heat.resources.stack`
331 :param new_stack: The new created stack
332 :type new_stack: :class:`heat.resources.stack`
334 for net
in new_stack
.nets
.values():
335 net
.reset_issued_ip_addresses()
337 for old_port
in old_stack
.ports
.values():
338 for port
in new_stack
.ports
.values():
339 if port
.compare_attributes(old_port
):
340 for net
in new_stack
.nets
.values():
341 if net
.name
== port
.net_name
:
342 if net
.assign_ip_address(old_port
.ip_address
, port
.name
):
343 port
.ip_address
= old_port
.ip_address
344 port
.mac_address
= old_port
.mac_address
346 port
.ip_address
= net
.get_new_ip_address(port
.name
)
348 for port
in new_stack
.ports
.values():
349 for net
in new_stack
.nets
.values():
350 if port
.net_name
== net
.name
and not net
.is_my_ip(port
.ip_address
, port
.name
):
351 port
.ip_address
= net
.get_new_ip_address(port
.name
)
353 def update_subnet_cidr(self
, old_stack
, new_stack
):
355 Updates the subnet IP addresses. If the new stack contains subnets from the old stack it will take those
356 IP addresses. Otherwise it will create new IP addresses for the subnet.
358 :param old_stack: The currently running stack
359 :type old_stack: :class:`heat.resources.stack`
360 :param new_stack: The new created stack
361 :type new_stack: :class:`heat.resources.stack`
363 for old_subnet
in old_stack
.nets
.values():
364 IP
.free_cidr(old_subnet
.get_cidr(), old_subnet
.subnet_id
)
366 for subnet
in new_stack
.nets
.values():
368 for old_subnet
in old_stack
.nets
.values():
369 if subnet
.subnet_name
== old_subnet
.subnet_name
:
370 if IP
.assign_cidr(old_subnet
.get_cidr(), subnet
.subnet_id
):
371 subnet
.set_cidr(old_subnet
.get_cidr())
373 for subnet
in new_stack
.nets
.values():
374 if IP
.is_cidr_issued(subnet
.get_cidr()):
377 cird
= IP
.get_new_cidr(subnet
.subnet_id
)
378 subnet
.set_cidr(cird
)
381 def update_compute_dicts(self
, stack
):
383 Update and add all stack components tho the compute dictionaries.
385 :param stack: A stack reference, to get all required components.
386 :type stack: :class:`heat.resources.stack`
388 for server
in stack
.servers
.values():
389 self
.computeUnits
[server
.id] = server
390 if isinstance(server
.flavor
, dict):
391 self
.add_flavor(server
.flavor
['flavorName'],
392 server
.flavor
['vcpu'],
393 server
.flavor
['ram'], 'MB',
394 server
.flavor
['storage'], 'GB')
395 server
.flavor
= server
.flavor
['flavorName']
396 for router
in stack
.routers
.values():
397 self
.routers
[router
.id] = router
398 for net
in stack
.nets
.values():
399 self
.nets
[net
.id] = net
400 for port
in stack
.ports
.values():
401 self
.ports
[port
.id] = port
403 def _start_compute(self
, server
):
405 Starts a new compute object (docker container) inside the emulator.
406 Should only be called by stack modifications and not directly.
408 :param server: Specifies the compute resource.
409 :type server: :class:`heat.resources.server`
411 LOG
.debug("Starting new compute resources %s" % server
.name
)
413 network_dict
= dict()
415 for port_name
in server
.port_names
:
416 network_dict
= dict()
417 port
= self
.find_port_by_name_or_id(port_name
)
419 network_dict
['id'] = port
.intf_name
420 network_dict
['ip'] = port
.ip_address
421 network_dict
[network_dict
['id']] = self
.find_network_by_name_or_id(port
.net_name
).name
422 network
.append(network_dict
)
423 # default network dict
425 network_dict
['id'] = server
.name
+ "-eth0"
426 network_dict
[network_dict
['id']] = network_dict
['id']
427 network
.append(network_dict
)
429 self
.compute_nets
[server
.name
] = network
430 LOG
.debug("Network dict: {}".format(network
))
431 c
= self
.dc
.startCompute(server
.name
, image
=server
.image
, command
=server
.command
,
432 network
=network
, flavor_name
=server
.flavor
,
433 properties
=server
.properties
)
434 server
.emulator_compute
= c
436 for intf
in c
.intfs
.values():
437 for port_name
in server
.port_names
:
438 port
= self
.find_port_by_name_or_id(port_name
)
440 if intf
.name
== port
.intf_name
:
441 # wait up to one second for the intf to come up
442 self
.timeout_sleep(intf
.isUp
, 1)
443 if port
.mac_address
is not None:
444 intf
.setMAC(port
.mac_address
)
446 port
.mac_address
= intf
.MAC()
448 # Start the real emulator command now as specified in the dockerfile
450 config
= c
.dcinfo
.get("Config", dict())
451 env
= config
.get("Env", list())
453 if "SON_EMU_CMD=" in env_var
:
454 cmd
= str(env_var
.split("=")[1])
455 server
.son_emu_command
= cmd
456 # execute command in new thread to ensure that GK is not blocked by VNF
457 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
461 def stop_compute(self
, server
):
463 Determines which links should be removed before removing the server itself.
465 :param server: The server that should be removed
466 :type server: ``heat.resources.server``
468 LOG
.debug("Stopping container %s with full name %s" % (server
.name
, server
.full_name
))
470 for port_name
in server
.port_names
:
471 prt
= self
.find_port_by_name_or_id(port_name
)
473 link_names
.append(prt
.intf_name
)
474 my_links
= self
.dc
.net
.links
475 for link
in my_links
:
476 if str(link
.intf1
) in link_names
:
477 # Remove all self created links that connect the server to the main switch
478 self
._remove
_link
(server
.name
, link
)
480 # Stop the server and the remaining connection to the datacenter switch
481 self
.dc
.stopCompute(server
.name
)
482 # Only now delete all its ports and the server itself
483 for port_name
in server
.port_names
:
484 self
.delete_port(port_name
)
485 self
.delete_server(server
)
487 def find_server_by_name_or_id(self
, name_or_id
):
489 Tries to find the server by ID and if this does not succeed then tries to find it via name.
491 :param name_or_id: UUID or name of the server.
492 :type name_or_id: ``str``
493 :return: Returns the server reference if it was found or None
494 :rtype: :class:`heat.resources.server`
496 if name_or_id
in self
.computeUnits
:
497 return self
.computeUnits
[name_or_id
]
499 if self
._shorten
_server
_name
(name_or_id
) in self
.computeUnits
:
500 return self
.computeUnits
[name_or_id
]
502 for server
in self
.computeUnits
.values():
503 if server
.name
== name_or_id
or server
.template_name
== name_or_id
or server
.full_name
== name_or_id
:
505 if (server
.name
== self
._shorten
_server
_name
(name_or_id
)
506 or server
.template_name
== self
._shorten
_server
_name
(name_or_id
)
507 or server
.full_name
== self
._shorten
_server
_name
(name_or_id
)):
511 def create_server(self
, name
, stack_operation
=False):
513 Creates a server with the specified name. Raises an exception when a server with the given name already
516 :param name: Name of the new server.
518 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
519 :type stack_operation: ``bool``
520 :return: Returns the created server.
521 :rtype: :class:`heat.resources.server`
523 if self
.find_server_by_name_or_id(name
) is not None and not stack_operation
:
524 raise Exception("Server with name %s already exists." % name
)
525 safe_name
= self
._shorten
_server
_name
(name
)
526 server
= Server(safe_name
)
527 server
.id = str(uuid
.uuid4())
528 if not stack_operation
:
529 self
.computeUnits
[server
.id] = server
532 def _shorten_server_name(self
, name
, char_limit
=9):
534 Docker does not like too long instance names.
535 This function provides a shorter name if needed
537 # fix for NetSoft'17 demo
538 # TODO remove this after the demo
539 if "http" in name
or "apache" in name
:
541 elif "l4fw" in name
or "socat" in name
:
543 elif "proxy" in name
or "squid" in name
:
545 # this is a ugly fix, but we cannot do better for now (interface names are to long)
546 if len(name
) > char_limit
:
547 LOG
.info("Long server name: {}".format(name
))
548 # construct a short name
549 name
= name
.strip("-_ .")
550 name
= name
.replace("_vnf", "")
554 name
= name
[-char_limit
:].strip("-_ .")
555 LOG
.info("Short server name: {}".format(name
))
559 def delete_server(self
, server
):
561 Deletes the given server from the stack dictionary and the computeUnits dictionary.
563 :param server: Reference of the server that should be deleted.
564 :type server: :class:`heat.resources.server`
565 :return: * *False*: If the server name is not in the correct format ('datacentername_stackname_servername') \
566 or when no stack with the correct stackname was found.
572 name_parts
= server
.name
.split('_')
573 if len(name_parts
) < 3:
576 for stack
in self
.stacks
.values():
577 if stack
.stack_name
== name_parts
[1]:
578 stack
.servers
.pop(server
.id, None)
579 if self
.computeUnits
.pop(server
.id, None) is None:
583 def find_network_by_name_or_id(self
, name_or_id
):
585 Tries to find the network by ID and if this does not succeed then tries to find it via name.
587 :param name_or_id: UUID or name of the network.
588 :type name_or_id: ``str``
589 :return: Returns the network reference if it was found or None
590 :rtype: :class:`heat.resources.net`
592 if name_or_id
in self
.nets
:
593 return self
.nets
[name_or_id
]
594 for net
in self
.nets
.values():
595 if net
.name
== name_or_id
:
600 def create_network(self
, name
, stack_operation
=False):
602 Creates a new network with the given name. Raises an exception when a network with the given name already
605 :param name: Name of the new network.
607 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
608 :type stack_operation: ``bool``
609 :return: :class:`heat.resources.net`
611 LOG
.debug("Creating network with name %s" % name
)
612 if self
.find_network_by_name_or_id(name
) is not None and not stack_operation
:
613 LOG
.warning("Creating network with name %s failed, as it already exists" % name
)
614 raise Exception("Network with name %s already exists." % name
)
616 network
.id = str(uuid
.uuid4())
617 if not stack_operation
:
618 self
.nets
[network
.id] = network
621 def delete_network(self
, name_or_id
):
623 Deletes the given network.
625 :param name_or_id: Name or UUID of the network.
626 :type name_or_id: ``str``
628 net
= self
.find_network_by_name_or_id(name_or_id
)
630 raise Exception("Network with name or id %s does not exists." % name_or_id
)
632 for stack
in self
.stacks
.values():
633 stack
.nets
.pop(net
.name
, None)
635 self
.nets
.pop(net
.id, None)
637 def create_port(self
, name
, stack_operation
=False):
639 Creates a new port with the given name. Raises an exception when a port with the given name already
642 :param name: Name of the new port.
644 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
645 :type stack_operation: ``bool``
646 :return: Returns the created port.
647 :rtype: :class:`heat.resources.port`
649 port
= self
.find_port_by_name_or_id(name
)
650 if port
is not None and not stack_operation
:
651 LOG
.warning("Creating port with name %s failed, as it already exists" % name
)
652 raise Exception("Port with name %s already exists." % name
)
653 LOG
.debug("Creating port with name %s" % name
)
655 if not stack_operation
:
656 self
.ports
[port
.id] = port
657 port
.create_intf_name()
660 def find_port_by_name_or_id(self
, name_or_id
):
662 Tries to find the port by ID and if this does not succeed then tries to find it via name.
664 :param name_or_id: UUID or name of the network.
665 :type name_or_id: ``str``
666 :return: Returns the port reference if it was found or None
667 :rtype: :class:`heat.resources.port`
669 if name_or_id
in self
.ports
:
670 return self
.ports
[name_or_id
]
671 for port
in self
.ports
.values():
672 if port
.name
== name_or_id
or port
.template_name
== name_or_id
:
677 def delete_port(self
, name_or_id
):
679 Deletes the given port. Raises an exception when the port was not found!
681 :param name_or_id: UUID or name of the port.
682 :type name_or_id: ``str``
684 port
= self
.find_port_by_name_or_id(name_or_id
)
686 LOG
.warning("Port with name or id %s does not exist. Can't delete it." % name_or_id
)
689 my_links
= self
.dc
.net
.links
690 for link
in my_links
:
691 if str(link
.intf1
) == port
.intf_name
and \
692 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
693 self
._remove
_link
(link
.intf1
.node
.name
, link
)
696 self
.ports
.pop(port
.id, None)
697 for stack
in self
.stacks
.values():
698 stack
.ports
.pop(port
.name
, None)
700 def create_port_pair(self
, name
, stack_operation
=False):
702 Creates a new port pair with the given name. Raises an exception when a port pair with the given name already
705 :param name: Name of the new port pair.
707 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
708 :type stack_operation: ``bool``
709 :return: Returns the created port pair.
710 :rtype: :class:`openstack.resources.port_pair`
712 port_pair
= self
.find_port_pair_by_name_or_id(name
)
713 if port_pair
is not None and not stack_operation
:
714 logging
.warning("Creating port pair with name %s failed, as it already exists" % name
)
715 raise Exception("Port pair with name %s already exists." % name
)
716 logging
.debug("Creating port pair with name %s" % name
)
717 port_pair
= PortPair(name
)
718 if not stack_operation
:
719 self
.port_pairs
[port_pair
.id] = port_pair
722 def find_port_pair_by_name_or_id(self
, name_or_id
):
724 Tries to find the port pair by ID and if this does not succeed then tries to find it via name.
726 :param name_or_id: UUID or name of the port pair.
727 :type name_or_id: ``str``
728 :return: Returns the port pair reference if it was found or None
729 :rtype: :class:`openstack.resources.port_pair`
731 if name_or_id
in self
.port_pairs
:
732 return self
.port_pairs
[name_or_id
]
733 for port_pair
in self
.port_pairs
.values():
734 if port_pair
.name
== name_or_id
:
739 def delete_port_pair(self
, name_or_id
):
741 Deletes the given port pair. Raises an exception when the port pair was not found!
743 :param name_or_id: UUID or name of the port pair.
744 :type name_or_id: ``str``
746 port_pair
= self
.find_port_pair_by_name_or_id(name_or_id
)
747 if port_pair
is None:
748 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
750 self
.port_pairs
.pop(port_pair
.id, None)
752 def create_port_pair_group(self
, name
, stack_operation
=False):
754 Creates a new port pair group with the given name. Raises an exception when a port pair group
755 with the given name already exists!
757 :param name: Name of the new port pair group.
759 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
760 :type stack_operation: ``bool``
761 :return: Returns the created port pair group .
762 :rtype: :class:`openstack.resources.port_pair_group`
764 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name
)
765 if port_pair_group
is not None and not stack_operation
:
766 logging
.warning("Creating port pair group with name %s failed, as it already exists" % name
)
767 raise Exception("Port pair group with name %s already exists." % name
)
768 logging
.debug("Creating port pair group with name %s" % name
)
769 port_pair_group
= PortPairGroup(name
)
770 if not stack_operation
:
771 self
.port_pair_groups
[port_pair_group
.id] = port_pair_group
772 return port_pair_group
774 def find_port_pair_group_by_name_or_id(self
, name_or_id
):
776 Tries to find the port pair group by ID and if this does not succeed then tries to find it via name.
778 :param name_or_id: UUID or name of the port pair group.
779 :type name_or_id: ``str``
780 :return: Returns the port pair group reference if it was found or None
781 :rtype: :class:`openstack.resources.port_pair_group`
783 if name_or_id
in self
.port_pair_groups
:
784 return self
.port_pair_groups
[name_or_id
]
785 for port_pair_group
in self
.port_pair_groups
.values():
786 if port_pair_group
.name
== name_or_id
:
787 return port_pair_group
791 def delete_port_pair_group(self
, name_or_id
):
793 Deletes the given port pair group. Raises an exception when the port pair group was not found!
795 :param name_or_id: UUID or name of the port pair group.
796 :type name_or_id: ``str``
798 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name_or_id
)
799 if port_pair_group
is None:
800 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
802 self
.port_pair_groups
.pop(port_pair_group
.id, None)
804 def create_port_chain(self
, name
, stack_operation
=False):
806 Creates a new port chain with the given name. Raises an exception when a port chain with the given name already
809 :param name: Name of the new port chain
811 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
812 :type stack_operation: ``bool``
813 :return: Returns the created port chain.
814 :rtype: :class:`openstack.resources.port_chain.PortChain`
816 port_chain
= self
.find_port_chain_by_name_or_id(name
)
817 if port_chain
is not None and not stack_operation
:
818 logging
.warning("Creating port chain with name %s failed, as it already exists" % name
)
819 raise Exception("Port chain with name %s already exists." % name
)
820 logging
.debug("Creating port chain with name %s" % name
)
821 port_chain
= PortChain(name
)
822 if not stack_operation
:
823 self
.port_chains
[port_chain
.id] = port_chain
826 def find_port_chain_by_name_or_id(self
, name_or_id
):
828 Tries to find the port chain by ID and if this does not succeed then tries to find it via name.
830 :param name_or_id: UUID or name of the port chain.
831 :type name_or_id: ``str``
832 :return: Returns the port chain reference if it was found or None
833 :rtype: :class:`openstack.resources.port_chain.PortChain`
835 if name_or_id
in self
.port_chains
:
836 return self
.port_chains
[name_or_id
]
837 for port_chain
in self
.port_chains
.values():
838 if port_chain
.name
== name_or_id
:
842 def delete_port_chain(self
, name_or_id
):
844 Deletes the given port chain. Raises an exception when the port chain was not found!
846 :param name_or_id: UUID or name of the port chain.
847 :type name_or_id: ``str``
849 port_chain
= self
.find_port_chain_by_name_or_id(name_or_id
)
850 port_chain
.uninstall(self
)
851 if port_chain
is None:
852 raise Exception("Port chain with name or id %s does not exists." % name_or_id
)
854 self
.port_chains
.pop(port_chain
.id, None)
856 def create_flow_classifier(self
, name
, stack_operation
=False):
858 Creates a new flow classifier with the given name. Raises an exception when a flow classifier with the given name already
861 :param name: Name of the new flow classifier.
863 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
864 :type stack_operation: ``bool``
865 :return: Returns the created flow classifier.
866 :rtype: :class:`openstack.resources.flow_classifier`
868 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name
)
869 if flow_classifier
is not None and not stack_operation
:
870 logging
.warning("Creating flow classifier with name %s failed, as it already exists" % name
)
871 raise Exception("Flow classifier with name %s already exists." % name
)
872 logging
.debug("Creating flow classifier with name %s" % name
)
873 flow_classifier
= FlowClassifier(name
)
874 if not stack_operation
:
875 self
.flow_classifiers
[flow_classifier
.id] = flow_classifier
876 return flow_classifier
878 def find_flow_classifier_by_name_or_id(self
, name_or_id
):
880 Tries to find the flow classifier by ID and if this does not succeed then tries to find it via name.
882 :param name_or_id: UUID or name of the flow classifier.
883 :type name_or_id: ``str``
884 :return: Returns the flow classifier reference if it was found or None
885 :rtype: :class:`openstack.resources.flow_classifier`
887 if name_or_id
in self
.flow_classifiers
:
888 return self
.flow_classifiers
[name_or_id
]
889 for flow_classifier
in self
.flow_classifiers
.values():
890 if flow_classifier
.name
== name_or_id
:
891 return flow_classifier
895 def delete_flow_classifier(self
, name_or_id
):
897 Deletes the given flow classifier. Raises an exception when the flow classifier was not found!
899 :param name_or_id: UUID or name of the flow classifier.
900 :type name_or_id: ``str``
902 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name_or_id
)
903 if flow_classifier
is None:
904 raise Exception("Flow classifier with name or id %s does not exists." % name_or_id
)
906 self
.flow_classifiers
.pop(flow_classifier
.id, None)
908 def _add_link(self
, node_name
, ip_address
, link_name
, net_name
):
910 Adds a new link between datacenter switch and the node with the given name.
912 :param node_name: Name of the required node.
913 :type node_name: ``str``
914 :param ip_address: IP-Address of the node.
915 :type ip_address: ``str``
916 :param link_name: Link name.
917 :type link_name: ``str``
918 :param net_name: Network name.
919 :type net_name: ``str``
921 node
= self
.dc
.net
.get(node_name
)
922 params
= {'params1': {'ip': ip_address
,
924 link_name
: net_name
},
925 'intfName1': link_name
,
927 link
= self
.dc
.net
.addLink(node
, self
.dc
.switch
, **params
)
928 OpenstackCompute
.timeout_sleep(link
.intf1
.isUp
, 1)
930 def _remove_link(self
, server_name
, link
):
932 Removes a link between server and datacenter switch.
934 :param server_name: Specifies the server where the link starts.
935 :type server_name: ``str``
936 :param link: A reference of the link which should be removed.
937 :type link: :class:`mininet.link`
939 self
.dc
.switch
.detach(link
.intf2
)
940 del self
.dc
.switch
.intfs
[self
.dc
.switch
.ports
[link
.intf2
]]
941 del self
.dc
.switch
.ports
[link
.intf2
]
942 del self
.dc
.switch
.nameToIntf
[link
.intf2
.name
]
943 self
.dc
.net
.removeLink(link
=link
)
944 for intf_key
in self
.dc
.net
[server_name
].intfs
.keys():
945 if self
.dc
.net
[server_name
].intfs
[intf_key
].link
== link
:
946 self
.dc
.net
[server_name
].intfs
[intf_key
].delete()
947 del self
.dc
.net
[server_name
].intfs
[intf_key
]
950 def timeout_sleep(function
, max_sleep
):
952 This function will execute a function all 0.1 seconds until it successfully returns.
953 Will return after `max_sleep` seconds if not successful.
955 :param function: The function to execute. Should return true if done.
956 :type function: ``function``
957 :param max_sleep: Max seconds to sleep. 1 equals 1 second.
958 :type max_sleep: ``float``
960 current_time
= time
.time()
961 stop_time
= current_time
+ max_sleep
962 while not function() and current_time
< stop_time
:
963 current_time
= time
.time()