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
39 LOG
= logging
.getLogger("api.openstack.compute")
42 class HeatApiStackInvalidException(Exception):
44 Exception thrown when a submitted stack is invalid.
47 def __init__(self
, value
):
51 return repr(self
.value
)
54 class OpenstackCompute(object):
56 This class is a datacenter specific compute object that tracks all containers that are running in a datacenter,
57 as well as networks and configured ports.
58 It has some stack dependet logic and can check if a received stack is valid.
60 It also handles start and stop of containers.
66 self
.computeUnits
= dict()
72 self
.port_pairs
= dict()
73 self
.port_pair_groups
= dict()
74 self
.flow_classifiers
= dict()
75 self
.port_chains
= dict()
76 self
.compute_nets
= dict()
77 self
.dcli
= DockerClient(base_url
='unix://var/run/docker.sock')
82 Updates the known images. Asks the docker daemon for a list of all known images and returns
85 :return: Returns the new image dictionary.
88 for image
in self
.dcli
.images
.list():
89 if len(image
.tags
) > 0:
91 t
= t
.replace(":latest", "") # only use short tag names for OSM compatibility
92 if t
not in self
._images
:
93 self
._images
[t
] = Image(t
)
96 def add_stack(self
, stack
):
98 Adds a new stack to the compute node.
100 :param stack: Stack dictionary.
101 :type stack: :class:`heat.resources.stack`
103 if not self
.check_stack(stack
):
104 self
.clean_broken_stack(stack
)
105 raise HeatApiStackInvalidException("Stack did not pass validity checks")
106 self
.stacks
[stack
.id] = stack
108 def clean_broken_stack(self
, stack
):
109 for port
in stack
.ports
.values():
110 if port
.id in self
.ports
:
111 del self
.ports
[port
.id]
112 for server
in stack
.servers
.values():
113 if server
.id in self
.computeUnits
:
114 del self
.computeUnits
[server
.id]
115 for net
in stack
.nets
.values():
116 if net
.id in self
.nets
:
117 del self
.nets
[net
.id]
119 def check_stack(self
, stack
):
121 Checks all dependencies of all servers, ports and routers and their most important parameters.
123 :param stack: A reference of the stack that should be checked.
124 :type stack: :class:`heat.resources.stack`
125 :return: * *True*: If the stack is completely fine.
130 for server
in stack
.servers
.values():
131 for port_name
in server
.port_names
:
132 if port_name
not in stack
.ports
:
133 LOG
.warning("Server %s of stack %s has a port named %s that is not known." %
134 (server
.name
, stack
.stack_name
, port_name
))
135 everything_ok
= False
136 if server
.image
is None:
137 LOG
.warning("Server %s holds no image." % (server
.name
))
138 everything_ok
= False
139 if server
.command
is None:
140 LOG
.warning("Server %s holds no command." % (server
.name
))
141 everything_ok
= False
142 for port
in stack
.ports
.values():
143 if port
.net_name
not in stack
.nets
:
144 LOG
.warning("Port %s of stack %s has a network named %s that is not known." %
145 (port
.name
, stack
.stack_name
, port
.net_name
))
146 everything_ok
= False
147 if port
.intf_name
is None:
148 LOG
.warning("Port %s has no interface name." % (port
.name
))
149 everything_ok
= False
150 if port
.ip_address
is None:
151 LOG
.warning("Port %s has no IP address." % (port
.name
))
152 everything_ok
= False
153 for router
in stack
.routers
.values():
154 for subnet_name
in router
.subnet_names
:
156 for net
in stack
.nets
.values():
157 if net
.subnet_name
== subnet_name
:
161 LOG
.warning("Router %s of stack %s has a network named %s that is not known." %
162 (router
.name
, stack
.stack_name
, subnet_name
))
163 everything_ok
= False
166 def add_flavor(self
, name
, cpu
, memory
, memory_unit
, storage
, storage_unit
):
168 Adds a flavor to the stack.
170 :param name: Specifies the name of the flavor.
175 :type memory: ``str``
177 :type memory_unit: ``str``
179 :type storage: ``str``
181 :type storage_unit: ``str``
183 flavor
= InstanceFlavor(name
, cpu
, memory
, memory_unit
, storage
, storage_unit
)
184 self
.flavors
[flavor
.name
] = flavor
187 def deploy_stack(self
, stackid
):
189 Deploys the stack and starts the emulation.
191 :param stackid: An UUID str of the stack
192 :type stackid: ``str``
193 :return: * *False*: If the Datacenter is None
200 stack
= self
.stacks
[stackid
]
201 self
.update_compute_dicts(stack
)
203 # Create the networks first
204 for server
in stack
.servers
.values():
205 self
._start
_compute
(server
)
208 def delete_stack(self
, stack_id
):
210 Delete a stack and all its components.
212 :param stack_id: An UUID str of the stack
213 :type stack_id: ``str``
214 :return: * *False*: If the Datacenter is None
221 # Stop all servers and their links of this stack
222 for server
in self
.stacks
[stack_id
].servers
.values():
223 self
.stop_compute(server
)
224 self
.delete_server(server
)
225 for net
in self
.stacks
[stack_id
].nets
.values():
226 self
.delete_network(net
.id)
227 for port
in self
.stacks
[stack_id
].ports
.values():
228 self
.delete_port(port
.id)
230 del self
.stacks
[stack_id
]
233 def update_stack(self
, old_stack_id
, new_stack
):
235 Determines differences within the old and the new stack and deletes, create or changes only parts that
236 differ between the two stacks.
238 :param old_stack_id: The ID of the old stack.
239 :type old_stack_id: ``str``
240 :param new_stack: A reference of the new stack.
241 :type new_stack: :class:`heat.resources.stack`
242 :return: * *True*: if the old stack could be updated to the new stack without any error.
246 LOG
.debug("updating stack {} with new_stack {}".format(old_stack_id
, new_stack
))
247 if old_stack_id
not in self
.stacks
:
249 old_stack
= self
.stacks
[old_stack_id
]
252 for server
in old_stack
.servers
.values():
253 if server
.name
in new_stack
.servers
:
254 new_stack
.servers
[server
.name
].id = server
.id
255 for net
in old_stack
.nets
.values():
256 if net
.name
in new_stack
.nets
:
257 new_stack
.nets
[net
.name
].id = net
.id
258 for subnet
in new_stack
.nets
.values():
259 if subnet
.subnet_name
== net
.subnet_name
:
260 subnet
.subnet_id
= net
.subnet_id
262 for port
in old_stack
.ports
.values():
263 if port
.name
in new_stack
.ports
:
264 new_stack
.ports
[port
.name
].id = port
.id
265 for router
in old_stack
.routers
.values():
266 if router
.name
in new_stack
.routers
:
267 new_stack
.routers
[router
.name
].id = router
.id
269 # Update the compute dicts to now contain the new_stack components
270 self
.update_compute_dicts(new_stack
)
272 self
.update_ip_addresses(old_stack
, new_stack
)
274 # Update all interface names - after each port has the correct UUID!!
275 for port
in new_stack
.ports
.values():
276 port
.create_intf_name()
278 if not self
.check_stack(new_stack
):
281 # Remove unnecessary networks
282 for net
in old_stack
.nets
.values():
283 if not net
.name
in new_stack
.nets
:
284 self
.delete_network(net
.id)
286 # Remove all unnecessary servers
287 for server
in old_stack
.servers
.values():
288 if server
.name
in new_stack
.servers
:
289 if not server
.compare_attributes(new_stack
.servers
[server
.name
]):
290 self
.stop_compute(server
)
292 # Delete unused and changed links
293 for port_name
in server
.port_names
:
294 if port_name
in old_stack
.ports
and port_name
in new_stack
.ports
:
295 if not old_stack
.ports
.get(port_name
) == new_stack
.ports
.get(port_name
):
296 my_links
= self
.dc
.net
.links
297 for link
in my_links
:
298 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
299 str(link
.intf1
.ip
) == \
300 old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
301 self
._remove
_link
(server
.name
, link
)
304 self
._add
_link
(server
.name
,
305 new_stack
.ports
[port_name
].ip_address
,
306 new_stack
.ports
[port_name
].intf_name
,
307 new_stack
.ports
[port_name
].net_name
)
310 my_links
= self
.dc
.net
.links
311 for link
in my_links
:
312 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
313 str(link
.intf1
.ip
) == old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
314 self
._remove
_link
(server
.name
, link
)
318 for port_name
in new_stack
.servers
[server
.name
].port_names
:
319 if port_name
not in server
.port_names
:
320 self
._add
_link
(server
.name
,
321 new_stack
.ports
[port_name
].ip_address
,
322 new_stack
.ports
[port_name
].intf_name
,
323 new_stack
.ports
[port_name
].net_name
)
325 self
.stop_compute(server
)
327 # Start all new servers
328 for server
in new_stack
.servers
.values():
329 if server
.name
not in self
.dc
.containers
:
330 self
._start
_compute
(server
)
332 server
.emulator_compute
= self
.dc
.containers
.get(server
.name
)
334 del self
.stacks
[old_stack_id
]
335 self
.stacks
[new_stack
.id] = new_stack
338 def update_ip_addresses(self
, old_stack
, new_stack
):
340 Updates the subnet and the port IP addresses - which should always be in this order!
342 :param old_stack: The currently running stack
343 :type old_stack: :class:`heat.resources.stack`
344 :param new_stack: The new created stack
345 :type new_stack: :class:`heat.resources.stack`
347 self
.update_subnet_cidr(old_stack
, new_stack
)
348 self
.update_port_addresses(old_stack
, new_stack
)
350 def update_port_addresses(self
, old_stack
, new_stack
):
352 Updates the port IP addresses. First resets all issued addresses. Then get all IP addresses from the old
353 stack and sets them to the same ports in the new stack. Finally all new or changed instances will get new
356 :param old_stack: The currently running stack
357 :type old_stack: :class:`heat.resources.stack`
358 :param new_stack: The new created stack
359 :type new_stack: :class:`heat.resources.stack`
361 for net
in new_stack
.nets
.values():
362 net
.reset_issued_ip_addresses()
364 for old_port
in old_stack
.ports
.values():
365 for port
in new_stack
.ports
.values():
366 if port
.compare_attributes(old_port
):
367 for net
in new_stack
.nets
.values():
368 if net
.name
== port
.net_name
:
369 if net
.assign_ip_address(old_port
.ip_address
, port
.name
):
370 port
.ip_address
= old_port
.ip_address
371 port
.mac_address
= old_port
.mac_address
373 port
.ip_address
= net
.get_new_ip_address(port
.name
)
375 for port
in new_stack
.ports
.values():
376 for net
in new_stack
.nets
.values():
377 if port
.net_name
== net
.name
and not net
.is_my_ip(port
.ip_address
, port
.name
):
378 port
.ip_address
= net
.get_new_ip_address(port
.name
)
380 def update_subnet_cidr(self
, old_stack
, new_stack
):
382 Updates the subnet IP addresses. If the new stack contains subnets from the old stack it will take those
383 IP addresses. Otherwise it will create new IP addresses for the subnet.
385 :param old_stack: The currently running stack
386 :type old_stack: :class:`heat.resources.stack`
387 :param new_stack: The new created stack
388 :type new_stack: :class:`heat.resources.stack`
390 for old_subnet
in old_stack
.nets
.values():
391 IP
.free_cidr(old_subnet
.get_cidr(), old_subnet
.subnet_id
)
393 for subnet
in new_stack
.nets
.values():
395 for old_subnet
in old_stack
.nets
.values():
396 if subnet
.subnet_name
== old_subnet
.subnet_name
:
397 if IP
.assign_cidr(old_subnet
.get_cidr(), subnet
.subnet_id
):
398 subnet
.set_cidr(old_subnet
.get_cidr())
400 for subnet
in new_stack
.nets
.values():
401 if IP
.is_cidr_issued(subnet
.get_cidr()):
404 cird
= IP
.get_new_cidr(subnet
.subnet_id
)
405 subnet
.set_cidr(cird
)
408 def update_compute_dicts(self
, stack
):
410 Update and add all stack components tho the compute dictionaries.
412 :param stack: A stack reference, to get all required components.
413 :type stack: :class:`heat.resources.stack`
415 for server
in stack
.servers
.values():
416 self
.computeUnits
[server
.id] = server
417 if isinstance(server
.flavor
, dict):
418 self
.add_flavor(server
.flavor
['flavorName'],
419 server
.flavor
['vcpu'],
420 server
.flavor
['ram'], 'MB',
421 server
.flavor
['storage'], 'GB')
422 server
.flavor
= server
.flavor
['flavorName']
423 for router
in stack
.routers
.values():
424 self
.routers
[router
.id] = router
425 for net
in stack
.nets
.values():
426 self
.nets
[net
.id] = net
427 for port
in stack
.ports
.values():
428 self
.ports
[port
.id] = port
430 def _start_compute(self
, server
):
432 Starts a new compute object (docker container) inside the emulator.
433 Should only be called by stack modifications and not directly.
435 :param server: Specifies the compute resource.
436 :type server: :class:`heat.resources.server`
438 LOG
.debug("Starting new compute resources %s" % server
.name
)
440 network_dict
= dict()
442 for port_name
in server
.port_names
:
443 network_dict
= dict()
444 port
= self
.find_port_by_name_or_id(port_name
)
446 network_dict
['id'] = port
.intf_name
447 network_dict
['ip'] = port
.ip_address
448 network_dict
[network_dict
['id']] = self
.find_network_by_name_or_id(port
.net_name
).name
449 network
.append(network_dict
)
450 # default network dict
452 network_dict
['id'] = server
.name
+ "-eth0"
453 network_dict
[network_dict
['id']] = network_dict
['id']
454 network
.append(network_dict
)
456 self
.compute_nets
[server
.name
] = network
457 LOG
.debug("Network dict: {}".format(network
))
458 c
= self
.dc
.startCompute(server
.name
, image
=server
.image
, command
=server
.command
,
459 network
=network
, flavor_name
=server
.flavor
,
460 properties
=server
.properties
)
461 server
.emulator_compute
= c
463 for intf
in c
.intfs
.values():
464 for port_name
in server
.port_names
:
465 port
= self
.find_port_by_name_or_id(port_name
)
467 if intf
.name
== port
.intf_name
:
468 # wait up to one second for the intf to come up
469 self
.timeout_sleep(intf
.isUp
, 1)
470 if port
.mac_address
is not None:
471 intf
.setMAC(port
.mac_address
)
473 port
.mac_address
= intf
.MAC()
475 # Start the real emulator command now as specified in the dockerfile
477 config
= c
.dcinfo
.get("Config", dict())
478 env
= config
.get("Env", list())
480 if "SON_EMU_CMD=" in env_var
:
481 cmd
= str(env_var
.split("=")[1])
482 server
.son_emu_command
= cmd
483 # execute command in new thread to ensure that GK is not blocked by VNF
484 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
488 def stop_compute(self
, server
):
490 Determines which links should be removed before removing the server itself.
492 :param server: The server that should be removed
493 :type server: ``heat.resources.server``
495 LOG
.debug("Stopping container %s with full name %s" % (server
.name
, server
.full_name
))
497 for port_name
in server
.port_names
:
498 prt
= self
.find_port_by_name_or_id(port_name
)
500 link_names
.append(prt
.intf_name
)
501 my_links
= self
.dc
.net
.links
502 for link
in my_links
:
503 if str(link
.intf1
) in link_names
:
504 # Remove all self created links that connect the server to the main switch
505 self
._remove
_link
(server
.name
, link
)
507 # Stop the server and the remaining connection to the datacenter switch
508 self
.dc
.stopCompute(server
.name
)
509 # Only now delete all its ports and the server itself
510 for port_name
in server
.port_names
:
511 self
.delete_port(port_name
)
512 self
.delete_server(server
)
514 def find_server_by_name_or_id(self
, name_or_id
):
516 Tries to find the server by ID and if this does not succeed then tries to find it via name.
518 :param name_or_id: UUID or name of the server.
519 :type name_or_id: ``str``
520 :return: Returns the server reference if it was found or None
521 :rtype: :class:`heat.resources.server`
523 if name_or_id
in self
.computeUnits
:
524 return self
.computeUnits
[name_or_id
]
526 if self
._shorten
_server
_name
(name_or_id
) in self
.computeUnits
:
527 return self
.computeUnits
[name_or_id
]
529 for server
in self
.computeUnits
.values():
530 if server
.name
== name_or_id
or server
.template_name
== name_or_id
or server
.full_name
== name_or_id
:
532 if (server
.name
== self
._shorten
_server
_name
(name_or_id
)
533 or server
.template_name
== self
._shorten
_server
_name
(name_or_id
)
534 or server
.full_name
== self
._shorten
_server
_name
(name_or_id
)):
538 def create_server(self
, name
, stack_operation
=False):
540 Creates a server with the specified name. Raises an exception when a server with the given name already
543 :param name: Name of the new server.
545 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
546 :type stack_operation: ``bool``
547 :return: Returns the created server.
548 :rtype: :class:`heat.resources.server`
550 if self
.find_server_by_name_or_id(name
) is not None and not stack_operation
:
551 raise Exception("Server with name %s already exists." % name
)
552 safe_name
= self
._shorten
_server
_name
(name
)
553 server
= Server(safe_name
)
554 server
.id = str(uuid
.uuid4())
555 if not stack_operation
:
556 self
.computeUnits
[server
.id] = server
559 def _shorten_server_name(self
, name
, char_limit
=9):
561 Docker does not like too long instance names.
562 This function provides a shorter name if needed
564 # fix for NetSoft'17 demo
565 # TODO remove this after the demo
566 #if "http" in name or "apache" in name:
568 #elif "l4fw" in name or "socat" in name:
570 #elif "proxy" in name or "squid" in name:
572 # this is a ugly fix, but we cannot do better for now (interface names are to long)
573 if len(name
) > char_limit
:
574 LOG
.info("Long server name: {}".format(name
))
575 # construct a short name
576 # name = name.strip("-_ .")
577 # name = name.replace("_vnf", "")
578 # p = name.split("_")
581 name
= name
[-char_limit
:].strip("-_ .")
582 LOG
.info("Short server name: {}".format(name
))
586 def delete_server(self
, server
):
588 Deletes the given server from the stack dictionary and the computeUnits dictionary.
590 :param server: Reference of the server that should be deleted.
591 :type server: :class:`heat.resources.server`
592 :return: * *False*: If the server name is not in the correct format ('datacentername_stackname_servername') \
593 or when no stack with the correct stackname was found.
599 name_parts
= server
.name
.split('_')
600 if len(name_parts
) < 3:
603 for stack
in self
.stacks
.values():
604 if stack
.stack_name
== name_parts
[1]:
605 stack
.servers
.pop(server
.id, None)
606 if self
.computeUnits
.pop(server
.id, None) is None:
610 def find_network_by_name_or_id(self
, name_or_id
):
612 Tries to find the network by ID and if this does not succeed then tries to find it via name.
614 :param name_or_id: UUID or name of the network.
615 :type name_or_id: ``str``
616 :return: Returns the network reference if it was found or None
617 :rtype: :class:`heat.resources.net`
619 if name_or_id
in self
.nets
:
620 return self
.nets
[name_or_id
]
621 for net
in self
.nets
.values():
622 if net
.name
== name_or_id
:
627 def create_network(self
, name
, stack_operation
=False):
629 Creates a new network with the given name. Raises an exception when a network with the given name already
632 :param name: Name of the new network.
634 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
635 :type stack_operation: ``bool``
636 :return: :class:`heat.resources.net`
638 LOG
.debug("Creating network with name %s" % name
)
639 if self
.find_network_by_name_or_id(name
) is not None and not stack_operation
:
640 LOG
.warning("Creating network with name %s failed, as it already exists" % name
)
641 raise Exception("Network with name %s already exists." % name
)
643 network
.id = str(uuid
.uuid4())
644 if not stack_operation
:
645 self
.nets
[network
.id] = network
648 def delete_network(self
, name_or_id
):
650 Deletes the given network.
652 :param name_or_id: Name or UUID of the network.
653 :type name_or_id: ``str``
655 net
= self
.find_network_by_name_or_id(name_or_id
)
657 raise Exception("Network with name or id %s does not exists." % name_or_id
)
659 for stack
in self
.stacks
.values():
660 stack
.nets
.pop(net
.name
, None)
662 self
.nets
.pop(net
.id, None)
664 def create_port(self
, name
, stack_operation
=False):
666 Creates a new port with the given name. Raises an exception when a port with the given name already
669 :param name: Name of the new port.
671 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
672 :type stack_operation: ``bool``
673 :return: Returns the created port.
674 :rtype: :class:`heat.resources.port`
676 port
= self
.find_port_by_name_or_id(name
)
677 if port
is not None and not stack_operation
:
678 LOG
.warning("Creating port with name %s failed, as it already exists" % name
)
679 raise Exception("Port with name %s already exists." % name
)
680 LOG
.debug("Creating port with name %s" % name
)
682 if not stack_operation
:
683 self
.ports
[port
.id] = port
684 port
.create_intf_name()
687 def find_port_by_name_or_id(self
, name_or_id
):
689 Tries to find the port by ID and if this does not succeed then tries to find it via name.
691 :param name_or_id: UUID or name of the network.
692 :type name_or_id: ``str``
693 :return: Returns the port reference if it was found or None
694 :rtype: :class:`heat.resources.port`
696 if name_or_id
in self
.ports
:
697 return self
.ports
[name_or_id
]
698 for port
in self
.ports
.values():
699 if port
.name
== name_or_id
or port
.template_name
== name_or_id
:
704 def delete_port(self
, name_or_id
):
706 Deletes the given port. Raises an exception when the port was not found!
708 :param name_or_id: UUID or name of the port.
709 :type name_or_id: ``str``
711 port
= self
.find_port_by_name_or_id(name_or_id
)
713 LOG
.warning("Port with name or id %s does not exist. Can't delete it." % name_or_id
)
716 my_links
= self
.dc
.net
.links
717 for link
in my_links
:
718 if str(link
.intf1
) == port
.intf_name
and \
719 str(link
.intf1
.ip
) == port
.ip_address
.split('/')[0]:
720 self
._remove
_link
(link
.intf1
.node
.name
, link
)
723 self
.ports
.pop(port
.id, None)
724 for stack
in self
.stacks
.values():
725 stack
.ports
.pop(port
.name
, None)
727 def create_port_pair(self
, name
, stack_operation
=False):
729 Creates a new port pair with the given name. Raises an exception when a port pair with the given name already
732 :param name: Name of the new port pair.
734 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
735 :type stack_operation: ``bool``
736 :return: Returns the created port pair.
737 :rtype: :class:`openstack.resources.port_pair`
739 port_pair
= self
.find_port_pair_by_name_or_id(name
)
740 if port_pair
is not None and not stack_operation
:
741 logging
.warning("Creating port pair with name %s failed, as it already exists" % name
)
742 raise Exception("Port pair with name %s already exists." % name
)
743 logging
.debug("Creating port pair with name %s" % name
)
744 port_pair
= PortPair(name
)
745 if not stack_operation
:
746 self
.port_pairs
[port_pair
.id] = port_pair
749 def find_port_pair_by_name_or_id(self
, name_or_id
):
751 Tries to find the port pair by ID and if this does not succeed then tries to find it via name.
753 :param name_or_id: UUID or name of the port pair.
754 :type name_or_id: ``str``
755 :return: Returns the port pair reference if it was found or None
756 :rtype: :class:`openstack.resources.port_pair`
758 if name_or_id
in self
.port_pairs
:
759 return self
.port_pairs
[name_or_id
]
760 for port_pair
in self
.port_pairs
.values():
761 if port_pair
.name
== name_or_id
:
766 def delete_port_pair(self
, name_or_id
):
768 Deletes the given port pair. Raises an exception when the port pair was not found!
770 :param name_or_id: UUID or name of the port pair.
771 :type name_or_id: ``str``
773 port_pair
= self
.find_port_pair_by_name_or_id(name_or_id
)
774 if port_pair
is None:
775 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
777 self
.port_pairs
.pop(port_pair
.id, None)
779 def create_port_pair_group(self
, name
, stack_operation
=False):
781 Creates a new port pair group with the given name. Raises an exception when a port pair group
782 with the given name already exists!
784 :param name: Name of the new port pair group.
786 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
787 :type stack_operation: ``bool``
788 :return: Returns the created port pair group .
789 :rtype: :class:`openstack.resources.port_pair_group`
791 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name
)
792 if port_pair_group
is not None and not stack_operation
:
793 logging
.warning("Creating port pair group with name %s failed, as it already exists" % name
)
794 raise Exception("Port pair group with name %s already exists." % name
)
795 logging
.debug("Creating port pair group with name %s" % name
)
796 port_pair_group
= PortPairGroup(name
)
797 if not stack_operation
:
798 self
.port_pair_groups
[port_pair_group
.id] = port_pair_group
799 return port_pair_group
801 def find_port_pair_group_by_name_or_id(self
, name_or_id
):
803 Tries to find the port pair group by ID and if this does not succeed then tries to find it via name.
805 :param name_or_id: UUID or name of the port pair group.
806 :type name_or_id: ``str``
807 :return: Returns the port pair group reference if it was found or None
808 :rtype: :class:`openstack.resources.port_pair_group`
810 if name_or_id
in self
.port_pair_groups
:
811 return self
.port_pair_groups
[name_or_id
]
812 for port_pair_group
in self
.port_pair_groups
.values():
813 if port_pair_group
.name
== name_or_id
:
814 return port_pair_group
818 def delete_port_pair_group(self
, name_or_id
):
820 Deletes the given port pair group. Raises an exception when the port pair group was not found!
822 :param name_or_id: UUID or name of the port pair group.
823 :type name_or_id: ``str``
825 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name_or_id
)
826 if port_pair_group
is None:
827 raise Exception("Port pair with name or id %s does not exists." % name_or_id
)
829 self
.port_pair_groups
.pop(port_pair_group
.id, None)
831 def create_port_chain(self
, name
, stack_operation
=False):
833 Creates a new port chain with the given name. Raises an exception when a port chain with the given name already
836 :param name: Name of the new port chain
838 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
839 :type stack_operation: ``bool``
840 :return: Returns the created port chain.
841 :rtype: :class:`openstack.resources.port_chain.PortChain`
843 port_chain
= self
.find_port_chain_by_name_or_id(name
)
844 if port_chain
is not None and not stack_operation
:
845 logging
.warning("Creating port chain with name %s failed, as it already exists" % name
)
846 raise Exception("Port chain with name %s already exists." % name
)
847 logging
.debug("Creating port chain with name %s" % name
)
848 port_chain
= PortChain(name
)
849 if not stack_operation
:
850 self
.port_chains
[port_chain
.id] = port_chain
853 def find_port_chain_by_name_or_id(self
, name_or_id
):
855 Tries to find the port chain by ID and if this does not succeed then tries to find it via name.
857 :param name_or_id: UUID or name of the port chain.
858 :type name_or_id: ``str``
859 :return: Returns the port chain reference if it was found or None
860 :rtype: :class:`openstack.resources.port_chain.PortChain`
862 if name_or_id
in self
.port_chains
:
863 return self
.port_chains
[name_or_id
]
864 for port_chain
in self
.port_chains
.values():
865 if port_chain
.name
== name_or_id
:
869 def delete_port_chain(self
, name_or_id
):
871 Deletes the given port chain. Raises an exception when the port chain was not found!
873 :param name_or_id: UUID or name of the port chain.
874 :type name_or_id: ``str``
876 port_chain
= self
.find_port_chain_by_name_or_id(name_or_id
)
877 port_chain
.uninstall(self
)
878 if port_chain
is None:
879 raise Exception("Port chain with name or id %s does not exists." % name_or_id
)
881 self
.port_chains
.pop(port_chain
.id, None)
883 def create_flow_classifier(self
, name
, stack_operation
=False):
885 Creates a new flow classifier with the given name. Raises an exception when a flow classifier with the given name already
888 :param name: Name of the new flow classifier.
890 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
891 :type stack_operation: ``bool``
892 :return: Returns the created flow classifier.
893 :rtype: :class:`openstack.resources.flow_classifier`
895 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name
)
896 if flow_classifier
is not None and not stack_operation
:
897 logging
.warning("Creating flow classifier with name %s failed, as it already exists" % name
)
898 raise Exception("Flow classifier with name %s already exists." % name
)
899 logging
.debug("Creating flow classifier with name %s" % name
)
900 flow_classifier
= FlowClassifier(name
)
901 if not stack_operation
:
902 self
.flow_classifiers
[flow_classifier
.id] = flow_classifier
903 return flow_classifier
905 def find_flow_classifier_by_name_or_id(self
, name_or_id
):
907 Tries to find the flow classifier by ID and if this does not succeed then tries to find it via name.
909 :param name_or_id: UUID or name of the flow classifier.
910 :type name_or_id: ``str``
911 :return: Returns the flow classifier reference if it was found or None
912 :rtype: :class:`openstack.resources.flow_classifier`
914 if name_or_id
in self
.flow_classifiers
:
915 return self
.flow_classifiers
[name_or_id
]
916 for flow_classifier
in self
.flow_classifiers
.values():
917 if flow_classifier
.name
== name_or_id
:
918 return flow_classifier
922 def delete_flow_classifier(self
, name_or_id
):
924 Deletes the given flow classifier. Raises an exception when the flow classifier was not found!
926 :param name_or_id: UUID or name of the flow classifier.
927 :type name_or_id: ``str``
929 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name_or_id
)
930 if flow_classifier
is None:
931 raise Exception("Flow classifier with name or id %s does not exists." % name_or_id
)
933 self
.flow_classifiers
.pop(flow_classifier
.id, None)
935 def _add_link(self
, node_name
, ip_address
, link_name
, net_name
):
937 Adds a new link between datacenter switch and the node with the given name.
939 :param node_name: Name of the required node.
940 :type node_name: ``str``
941 :param ip_address: IP-Address of the node.
942 :type ip_address: ``str``
943 :param link_name: Link name.
944 :type link_name: ``str``
945 :param net_name: Network name.
946 :type net_name: ``str``
948 node
= self
.dc
.net
.get(node_name
)
949 params
= {'params1': {'ip': ip_address
,
951 link_name
: net_name
},
952 'intfName1': link_name
,
954 link
= self
.dc
.net
.addLink(node
, self
.dc
.switch
, **params
)
955 OpenstackCompute
.timeout_sleep(link
.intf1
.isUp
, 1)
957 def _remove_link(self
, server_name
, link
):
959 Removes a link between server and datacenter switch.
961 :param server_name: Specifies the server where the link starts.
962 :type server_name: ``str``
963 :param link: A reference of the link which should be removed.
964 :type link: :class:`mininet.link`
966 self
.dc
.switch
.detach(link
.intf2
)
967 del self
.dc
.switch
.intfs
[self
.dc
.switch
.ports
[link
.intf2
]]
968 del self
.dc
.switch
.ports
[link
.intf2
]
969 del self
.dc
.switch
.nameToIntf
[link
.intf2
.name
]
970 self
.dc
.net
.removeLink(link
=link
)
971 for intf_key
in self
.dc
.net
[server_name
].intfs
.keys():
972 if self
.dc
.net
[server_name
].intfs
[intf_key
].link
== link
:
973 self
.dc
.net
[server_name
].intfs
[intf_key
].delete()
974 del self
.dc
.net
[server_name
].intfs
[intf_key
]
977 def timeout_sleep(function
, max_sleep
):
979 This function will execute a function all 0.1 seconds until it successfully returns.
980 Will return after `max_sleep` seconds if not successful.
982 :param function: The function to execute. Should return true if done.
983 :type function: ``function``
984 :param max_sleep: Max seconds to sleep. 1 equals 1 second.
985 :type max_sleep: ``float``
987 current_time
= time
.time()
988 stop_time
= current_time
+ max_sleep
989 while not function() and current_time
< stop_time
:
990 current_time
= time
.time()