1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 from mininet
.link
import Link
28 from resources
.instance_flavor
import InstanceFlavor
29 from resources
.net
import Net
30 from resources
.port
import Port
31 from resources
.port_pair
import PortPair
32 from resources
.port_pair_group
import PortPairGroup
33 from resources
.flow_classifier
import FlowClassifier
34 from resources
.port_chain
import PortChain
35 from resources
.server
import Server
36 from resources
.image
import Image
38 from docker
import DockerClient
43 import ip_handler
as IP
47 LOG
= logging
.getLogger("api.openstack.compute")
50 class HeatApiStackInvalidException(Exception):
52 Exception thrown when a submitted stack is invalid.
55 def __init__(self
, value
):
59 return repr(self
.value
)
62 class OpenstackCompute(object):
64 This class is a datacenter specific compute object that tracks all containers that are running in a datacenter,
65 as well as networks and configured ports.
66 It has some stack dependet logic and can check if a received stack is valid.
68 It also handles start and stop of containers.
74 self
.computeUnits
= dict()
80 self
.port_pairs
= dict()
81 self
.port_pair_groups
= dict()
82 self
.flow_classifiers
= dict()
83 self
.port_chains
= dict()
84 self
.compute_nets
= dict()
85 self
.dcli
= DockerClient(base_url
='unix://var/run/docker.sock')
90 Updates the known images. Asks the docker daemon for a list of all known images and returns
93 :return: Returns the new image dictionary.
96 for image
in self
.dcli
.images
.list():
97 if len(image
.tags
) > 0:
99 # only use short tag names for OSM compatibility
100 t
= t
.replace(":latest", "")
101 if t
not in self
._images
:
102 self
._images
[t
] = Image(t
)
105 def add_stack(self
, stack
):
107 Adds a new stack to the compute node.
109 :param stack: Stack dictionary.
110 :type stack: :class:`heat.resources.stack`
112 if not self
.check_stack(stack
):
113 self
.clean_broken_stack(stack
)
114 raise HeatApiStackInvalidException(
115 "Stack did not pass validity checks")
116 self
.stacks
[stack
.id] = stack
118 def clean_broken_stack(self
, stack
):
119 for port
in stack
.ports
.values():
120 if port
.id in self
.ports
:
121 del self
.ports
[port
.id]
122 for server
in stack
.servers
.values():
123 if server
.id in self
.computeUnits
:
124 del self
.computeUnits
[server
.id]
125 for net
in stack
.nets
.values():
126 if net
.id in self
.nets
:
127 del self
.nets
[net
.id]
129 def check_stack(self
, stack
):
131 Checks all dependencies of all servers, ports and routers and their most important parameters.
133 :param stack: A reference of the stack that should be checked.
134 :type stack: :class:`heat.resources.stack`
135 :return: * *True*: If the stack is completely fine.
140 for server
in stack
.servers
.values():
141 for port_name
in server
.port_names
:
142 if port_name
not in stack
.ports
:
143 LOG
.warning("Server %s of stack %s has a port named %s that is not known." %
144 (server
.name
, stack
.stack_name
, port_name
))
145 everything_ok
= False
146 if server
.image
is None:
147 LOG
.warning("Server %s holds no image." % (server
.name
))
148 everything_ok
= False
149 if server
.command
is None:
150 LOG
.warning("Server %s holds no command." % (server
.name
))
151 everything_ok
= False
152 for port
in stack
.ports
.values():
153 if port
.net_name
not in stack
.nets
:
154 LOG
.warning("Port %s of stack %s has a network named %s that is not known." %
155 (port
.name
, stack
.stack_name
, port
.net_name
))
156 everything_ok
= False
157 if port
.intf_name
is None:
158 LOG
.warning("Port %s has no interface name." % (port
.name
))
159 everything_ok
= False
160 if port
.ip_address
is None:
161 LOG
.warning("Port %s has no IP address." % (port
.name
))
162 everything_ok
= False
163 for router
in stack
.routers
.values():
164 for subnet_name
in router
.subnet_names
:
166 for net
in stack
.nets
.values():
167 if net
.subnet_name
== subnet_name
:
171 LOG
.warning("Router %s of stack %s has a network named %s that is not known." %
172 (router
.name
, stack
.stack_name
, subnet_name
))
173 everything_ok
= False
176 def add_flavor(self
, name
, cpu
, memory
,
177 memory_unit
, storage
, storage_unit
):
179 Adds a flavor to the stack.
181 :param name: Specifies the name of the flavor.
186 :type memory: ``str``
188 :type memory_unit: ``str``
190 :type storage: ``str``
192 :type storage_unit: ``str``
194 flavor
= InstanceFlavor(
195 name
, cpu
, memory
, memory_unit
, storage
, storage_unit
)
196 self
.flavors
[flavor
.name
] = flavor
199 def deploy_stack(self
, stackid
):
201 Deploys the stack and starts the emulation.
203 :param stackid: An UUID str of the stack
204 :type stackid: ``str``
205 :return: * *False*: If the Datacenter is None
212 stack
= self
.stacks
[stackid
]
213 self
.update_compute_dicts(stack
)
215 # Create the networks first
216 for server
in stack
.servers
.values():
217 self
._start
_compute
(server
)
220 def delete_stack(self
, stack_id
):
222 Delete a stack and all its components.
224 :param stack_id: An UUID str of the stack
225 :type stack_id: ``str``
226 :return: * *False*: If the Datacenter is None
233 # Stop all servers and their links of this stack
234 for server
in self
.stacks
[stack_id
].servers
.values():
235 self
.stop_compute(server
)
236 self
.delete_server(server
)
237 for net
in self
.stacks
[stack_id
].nets
.values():
238 self
.delete_network(net
.id)
239 for port
in self
.stacks
[stack_id
].ports
.values():
240 self
.delete_port(port
.id)
242 del self
.stacks
[stack_id
]
245 def update_stack(self
, old_stack_id
, new_stack
):
247 Determines differences within the old and the new stack and deletes, create or changes only parts that
248 differ between the two stacks.
250 :param old_stack_id: The ID of the old stack.
251 :type old_stack_id: ``str``
252 :param new_stack: A reference of the new stack.
253 :type new_stack: :class:`heat.resources.stack`
254 :return: * *True*: if the old stack could be updated to the new stack without any error.
258 LOG
.debug("updating stack {} with new_stack {}".format(
259 old_stack_id
, new_stack
))
260 if old_stack_id
not in self
.stacks
:
262 old_stack
= self
.stacks
[old_stack_id
]
265 for server
in old_stack
.servers
.values():
266 if server
.name
in new_stack
.servers
:
267 new_stack
.servers
[server
.name
].id = server
.id
268 for net
in old_stack
.nets
.values():
269 if net
.name
in new_stack
.nets
:
270 new_stack
.nets
[net
.name
].id = net
.id
271 for subnet
in new_stack
.nets
.values():
272 if subnet
.subnet_name
== net
.subnet_name
:
273 subnet
.subnet_id
= net
.subnet_id
275 for port
in old_stack
.ports
.values():
276 if port
.name
in new_stack
.ports
:
277 new_stack
.ports
[port
.name
].id = port
.id
278 for router
in old_stack
.routers
.values():
279 if router
.name
in new_stack
.routers
:
280 new_stack
.routers
[router
.name
].id = router
.id
282 # Update the compute dicts to now contain the new_stack components
283 self
.update_compute_dicts(new_stack
)
285 self
.update_ip_addresses(old_stack
, new_stack
)
287 # Update all interface names - after each port has the correct UUID!!
288 for port
in new_stack
.ports
.values():
289 port
.create_intf_name()
291 if not self
.check_stack(new_stack
):
294 # Remove unnecessary networks
295 for net
in old_stack
.nets
.values():
296 if net
.name
not in new_stack
.nets
:
297 self
.delete_network(net
.id)
299 # Remove all unnecessary servers
300 for server
in old_stack
.servers
.values():
301 if server
.name
in new_stack
.servers
:
302 if not server
.compare_attributes(
303 new_stack
.servers
[server
.name
]):
304 self
.stop_compute(server
)
306 # Delete unused and changed links
307 for port_name
in server
.port_names
:
308 if port_name
in old_stack
.ports
and port_name
in new_stack
.ports
:
309 if not old_stack
.ports
.get(
310 port_name
) == new_stack
.ports
.get(port_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
) == \
315 old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
316 self
._remove
_link
(server
.name
, link
)
319 self
._add
_link
(server
.name
,
320 new_stack
.ports
[port_name
].ip_address
,
321 new_stack
.ports
[port_name
].intf_name
,
322 new_stack
.ports
[port_name
].net_name
)
325 my_links
= self
.dc
.net
.links
326 for link
in my_links
:
327 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
328 str(link
.intf1
.ip
) == old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
329 self
._remove
_link
(server
.name
, link
)
333 for port_name
in new_stack
.servers
[server
.name
].port_names
:
334 if port_name
not in server
.port_names
:
335 self
._add
_link
(server
.name
,
336 new_stack
.ports
[port_name
].ip_address
,
337 new_stack
.ports
[port_name
].intf_name
,
338 new_stack
.ports
[port_name
].net_name
)
340 self
.stop_compute(server
)
342 # Start all new servers
343 for server
in new_stack
.servers
.values():
344 if server
.name
not in self
.dc
.containers
:
345 self
._start
_compute
(server
)
347 server
.emulator_compute
= self
.dc
.containers
.get(server
.name
)
349 del self
.stacks
[old_stack_id
]
350 self
.stacks
[new_stack
.id] = new_stack
353 def update_ip_addresses(self
, old_stack
, new_stack
):
355 Updates the subnet and the port IP addresses - which should always be in this order!
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 self
.update_subnet_cidr(old_stack
, new_stack
)
363 self
.update_port_addresses(old_stack
, new_stack
)
365 def update_port_addresses(self
, old_stack
, new_stack
):
367 Updates the port IP addresses. First resets all issued addresses. Then get all IP addresses from the old
368 stack and sets them to the same ports in the new stack. Finally all new or changed instances will get new
371 :param old_stack: The currently running stack
372 :type old_stack: :class:`heat.resources.stack`
373 :param new_stack: The new created stack
374 :type new_stack: :class:`heat.resources.stack`
376 for net
in new_stack
.nets
.values():
377 net
.reset_issued_ip_addresses()
379 for old_port
in old_stack
.ports
.values():
380 for port
in new_stack
.ports
.values():
381 if port
.compare_attributes(old_port
):
382 for net
in new_stack
.nets
.values():
383 if net
.name
== port
.net_name
:
384 if net
.assign_ip_address(
385 old_port
.ip_address
, port
.name
):
386 port
.ip_address
= old_port
.ip_address
387 port
.mac_address
= old_port
.mac_address
389 port
.ip_address
= net
.get_new_ip_address(
392 for port
in new_stack
.ports
.values():
393 for net
in new_stack
.nets
.values():
394 if port
.net_name
== net
.name
and not net
.is_my_ip(
395 port
.ip_address
, port
.name
):
396 port
.ip_address
= net
.get_new_ip_address(port
.name
)
398 def update_subnet_cidr(self
, old_stack
, new_stack
):
400 Updates the subnet IP addresses. If the new stack contains subnets from the old stack it will take those
401 IP addresses. Otherwise it will create new IP addresses for the subnet.
403 :param old_stack: The currently running stack
404 :type old_stack: :class:`heat.resources.stack`
405 :param new_stack: The new created stack
406 :type new_stack: :class:`heat.resources.stack`
408 for old_subnet
in old_stack
.nets
.values():
409 IP
.free_cidr(old_subnet
.get_cidr(), old_subnet
.subnet_id
)
411 for subnet
in new_stack
.nets
.values():
413 for old_subnet
in old_stack
.nets
.values():
414 if subnet
.subnet_name
== old_subnet
.subnet_name
:
415 if IP
.assign_cidr(old_subnet
.get_cidr(), subnet
.subnet_id
):
416 subnet
.set_cidr(old_subnet
.get_cidr())
418 for subnet
in new_stack
.nets
.values():
419 if IP
.is_cidr_issued(subnet
.get_cidr()):
422 cird
= IP
.get_new_cidr(subnet
.subnet_id
)
423 subnet
.set_cidr(cird
)
426 def update_compute_dicts(self
, stack
):
428 Update and add all stack components tho the compute dictionaries.
430 :param stack: A stack reference, to get all required components.
431 :type stack: :class:`heat.resources.stack`
433 for server
in stack
.servers
.values():
434 self
.computeUnits
[server
.id] = server
435 if isinstance(server
.flavor
, dict):
436 self
.add_flavor(server
.flavor
['flavorName'],
437 server
.flavor
['vcpu'],
438 server
.flavor
['ram'], 'MB',
439 server
.flavor
['storage'], 'GB')
440 server
.flavor
= server
.flavor
['flavorName']
441 for router
in stack
.routers
.values():
442 self
.routers
[router
.id] = router
443 for net
in stack
.nets
.values():
444 self
.nets
[net
.id] = net
445 for port
in stack
.ports
.values():
446 self
.ports
[port
.id] = port
448 def _start_compute(self
, server
):
450 Starts a new compute object (docker container) inside the emulator.
451 Should only be called by stack modifications and not directly.
453 :param server: Specifies the compute resource.
454 :type server: :class:`heat.resources.server`
456 LOG
.debug("Starting new compute resources %s" % server
.name
)
458 network_dict
= dict()
460 for port_name
in server
.port_names
:
461 network_dict
= dict()
462 port
= self
.find_port_by_name_or_id(port_name
)
464 network_dict
['id'] = port
.intf_name
465 network_dict
['ip'] = port
.ip_address
466 network_dict
[network_dict
['id']] = self
.find_network_by_name_or_id(
468 network
.append(network_dict
)
469 # default network dict
471 network_dict
['id'] = server
.name
+ "-eth0"
472 network_dict
[network_dict
['id']] = network_dict
['id']
473 network
.append(network_dict
)
475 self
.compute_nets
[server
.name
] = network
476 LOG
.debug("Network dict: {}".format(network
))
477 c
= self
.dc
.startCompute(server
.name
, image
=server
.image
, command
=server
.command
,
478 network
=network
, flavor_name
=server
.flavor
,
479 properties
=server
.properties
)
480 server
.emulator_compute
= c
482 for intf
in c
.intfs
.values():
483 for port_name
in server
.port_names
:
484 port
= self
.find_port_by_name_or_id(port_name
)
486 if intf
.name
== port
.intf_name
:
487 # wait up to one second for the intf to come up
488 self
.timeout_sleep(intf
.isUp
, 1)
489 if port
.mac_address
is not None:
490 intf
.setMAC(port
.mac_address
)
492 port
.mac_address
= intf
.MAC()
494 # Start the real emulator command now as specified in the dockerfile
496 config
= c
.dcinfo
.get("Config", dict())
497 env
= config
.get("Env", list())
499 if "SON_EMU_CMD=" in env_var
:
500 cmd
= str(env_var
.split("=")[1])
501 server
.son_emu_command
= cmd
502 # execute command in new thread to ensure that GK is not
504 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
508 def stop_compute(self
, server
):
510 Determines which links should be removed before removing the server itself.
512 :param server: The server that should be removed
513 :type server: ``heat.resources.server``
515 LOG
.debug("Stopping container %s with full name %s" %
516 (server
.name
, server
.full_name
))
518 for port_name
in server
.port_names
:
519 prt
= self
.find_port_by_name_or_id(port_name
)
521 link_names
.append(prt
.intf_name
)
522 my_links
= self
.dc
.net
.links
523 for link
in my_links
:
524 if str(link
.intf1
) in link_names
:
525 # Remove all self created links that connect the server to the
527 self
._remove
_link
(server
.name
, link
)
529 # Stop the server and the remaining connection to the datacenter switch
530 self
.dc
.stopCompute(server
.name
)
531 # Only now delete all its ports and the server itself
532 for port_name
in server
.port_names
:
533 self
.delete_port(port_name
)
534 self
.delete_server(server
)
536 def find_server_by_name_or_id(self
, name_or_id
):
538 Tries to find the server by ID and if this does not succeed then tries to find it via name.
540 :param name_or_id: UUID or name of the server.
541 :type name_or_id: ``str``
542 :return: Returns the server reference if it was found or None
543 :rtype: :class:`heat.resources.server`
545 if name_or_id
in self
.computeUnits
:
546 return self
.computeUnits
[name_or_id
]
548 if self
._shorten
_server
_name
(name_or_id
) in self
.computeUnits
:
549 return self
.computeUnits
[name_or_id
]
551 for server
in self
.computeUnits
.values():
552 if (server
.name
== name_or_id
or
553 server
.template_name
== name_or_id
or
554 server
.full_name
== name_or_id
):
556 if (server
.name
== self
._shorten
_server
_name
(name_or_id
) or
557 server
.template_name
== self
._shorten
_server
_name
(name_or_id
) or
558 server
.full_name
== self
._shorten
_server
_name
(name_or_id
)):
562 def create_server(self
, name
, stack_operation
=False):
564 Creates a server with the specified name. Raises an exception when a server with the given name already
567 :param name: Name of the new server.
569 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
570 :type stack_operation: ``bool``
571 :return: Returns the created server.
572 :rtype: :class:`heat.resources.server`
574 if self
.find_server_by_name_or_id(
575 name
) is not None and not stack_operation
:
576 raise Exception("Server with name %s already exists." % name
)
577 safe_name
= self
._shorten
_server
_name
(name
)
578 server
= Server(safe_name
)
579 server
.id = str(uuid
.uuid4())
580 if not stack_operation
:
581 self
.computeUnits
[server
.id] = server
584 def _shorten_server_name(self
, name
, char_limit
=9):
586 Docker does not like too long instance names.
587 This function provides a shorter name if needed
589 if len(name
) > char_limit
:
590 LOG
.info("Long server name: {}".format(name
))
591 # construct a short name
592 h
= hashlib
.sha224(name
).hexdigest()
594 LOG
.info("Short server name: {}".format(h
))
597 def delete_server(self
, server
):
599 Deletes the given server from the stack dictionary and the computeUnits dictionary.
601 :param server: Reference of the server that should be deleted.
602 :type server: :class:`heat.resources.server`
603 :return: * *False*: If the server name is not in the correct format ('datacentername_stackname_servername') \
604 or when no stack with the correct stackname was found.
610 name_parts
= server
.name
.split('_')
611 if len(name_parts
) > 1:
612 for stack
in self
.stacks
.values():
613 if stack
.stack_name
== name_parts
[1]:
614 stack
.servers
.pop(server
.id, None)
615 if self
.computeUnits
.pop(server
.id, None) is None:
619 def find_network_by_name_or_id(self
, name_or_id
):
621 Tries to find the network by ID and if this does not succeed then tries to find it via name.
623 :param name_or_id: UUID or name of the network.
624 :type name_or_id: ``str``
625 :return: Returns the network reference if it was found or None
626 :rtype: :class:`heat.resources.net`
628 if name_or_id
in self
.nets
:
629 return self
.nets
[name_or_id
]
630 for net
in self
.nets
.values():
631 if net
.name
== name_or_id
:
633 LOG
.warning("Could not find net '{}' in {} or {}"
636 [n
.name
for n
in self
.nets
.values()]))
639 def create_network(self
, name
, stack_operation
=False):
641 Creates a new network with the given name. Raises an exception when a network with the given name already
644 :param name: Name of the new network.
646 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
647 :type stack_operation: ``bool``
648 :return: :class:`heat.resources.net`
650 LOG
.debug("Creating network with name %s" % name
)
651 if self
.find_network_by_name_or_id(
652 name
) is not None and not stack_operation
:
654 "Creating network with name %s failed, as it already exists" % name
)
655 raise Exception("Network with name %s already exists." % name
)
657 network
.id = str(uuid
.uuid4())
658 if not stack_operation
:
659 self
.nets
[network
.id] = network
662 def delete_network(self
, name_or_id
):
664 Deletes the given network.
666 :param name_or_id: Name or UUID of the network.
667 :type name_or_id: ``str``
669 net
= self
.find_network_by_name_or_id(name_or_id
)
672 "Network with name or id %s does not exists." % name_or_id
)
674 for stack
in self
.stacks
.values():
675 stack
.nets
.pop(net
.name
, None)
677 self
.nets
.pop(net
.id, None)
679 def create_port(self
, name
, stack_operation
=False):
681 Creates a new port with the given name. Raises an exception when a port with the given name already
684 :param name: Name of the new port.
686 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
687 :type stack_operation: ``bool``
688 :return: Returns the created port.
689 :rtype: :class:`heat.resources.port`
691 port
= self
.find_port_by_name_or_id(name
)
692 if port
is not None and not stack_operation
:
694 "Creating port with name %s failed, as it already exists" % name
)
695 raise Exception("Port with name %s already exists." % name
)
696 LOG
.debug("Creating port with name %s" % name
)
698 if not stack_operation
:
699 self
.ports
[port
.id] = port
700 port
.create_intf_name()
703 def find_port_by_name_or_id(self
, name_or_id
):
705 Tries to find the port by ID and if this does not succeed then tries to find it via name.
707 :param name_or_id: UUID or name of the network.
708 :type name_or_id: ``str``
709 :return: Returns the port reference if it was found or None
710 :rtype: :class:`heat.resources.port`
712 if name_or_id
in self
.ports
:
713 return self
.ports
[name_or_id
]
714 for port
in self
.ports
.values():
715 if port
.name
== name_or_id
or port
.template_name
== name_or_id
:
720 def delete_port(self
, name_or_id
):
722 Deletes the given port. Raises an exception when the port was not found!
724 :param name_or_id: UUID or name of the port.
725 :type name_or_id: ``str``
727 port
= self
.find_port_by_name_or_id(name_or_id
)
730 "Port with name or id %s does not exist. Can't delete it." % name_or_id
)
733 my_links
= self
.dc
.net
.links
734 for link
in my_links
:
735 if str(link
.intf1
) == port
.intf_name
:
736 self
._remove
_link
(link
.intf1
.node
.name
, link
)
739 self
.ports
.pop(port
.id, None)
740 for stack
in self
.stacks
.values():
741 stack
.ports
.pop(port
.name
, None)
743 def create_port_pair(self
, name
, stack_operation
=False):
745 Creates a new port pair with the given name. Raises an exception when a port pair with the given name already
748 :param name: Name of the new port pair.
750 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
751 :type stack_operation: ``bool``
752 :return: Returns the created port pair.
753 :rtype: :class:`openstack.resources.port_pair`
755 port_pair
= self
.find_port_pair_by_name_or_id(name
)
756 if port_pair
is not None and not stack_operation
:
758 "Creating port pair with name %s failed, as it already exists" % name
)
759 raise Exception("Port pair with name %s already exists." % name
)
760 logging
.debug("Creating port pair with name %s" % name
)
761 port_pair
= PortPair(name
)
762 if not stack_operation
:
763 self
.port_pairs
[port_pair
.id] = port_pair
766 def find_port_pair_by_name_or_id(self
, name_or_id
):
768 Tries to find the port pair by ID and if this does not succeed then tries to find it via name.
770 :param name_or_id: UUID or name of the port pair.
771 :type name_or_id: ``str``
772 :return: Returns the port pair reference if it was found or None
773 :rtype: :class:`openstack.resources.port_pair`
775 if name_or_id
in self
.port_pairs
:
776 return self
.port_pairs
[name_or_id
]
777 for port_pair
in self
.port_pairs
.values():
778 if port_pair
.name
== name_or_id
:
783 def delete_port_pair(self
, name_or_id
):
785 Deletes the given port pair. Raises an exception when the port pair was not found!
787 :param name_or_id: UUID or name of the port pair.
788 :type name_or_id: ``str``
790 port_pair
= self
.find_port_pair_by_name_or_id(name_or_id
)
791 if port_pair
is None:
793 "Port pair with name or id %s does not exists." % name_or_id
)
795 self
.port_pairs
.pop(port_pair
.id, None)
797 def create_port_pair_group(self
, name
, stack_operation
=False):
799 Creates a new port pair group with the given name. Raises an exception when a port pair group
800 with the given name already exists!
802 :param name: Name of the new port pair group.
804 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
805 :type stack_operation: ``bool``
806 :return: Returns the created port pair group .
807 :rtype: :class:`openstack.resources.port_pair_group`
809 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name
)
810 if port_pair_group
is not None and not stack_operation
:
812 "Creating port pair group with name %s failed, as it already exists" % name
)
814 "Port pair group with name %s already exists." % name
)
815 logging
.debug("Creating port pair group with name %s" % name
)
816 port_pair_group
= PortPairGroup(name
)
817 if not stack_operation
:
818 self
.port_pair_groups
[port_pair_group
.id] = port_pair_group
819 return port_pair_group
821 def find_port_pair_group_by_name_or_id(self
, name_or_id
):
823 Tries to find the port pair group by ID and if this does not succeed then tries to find it via name.
825 :param name_or_id: UUID or name of the port pair group.
826 :type name_or_id: ``str``
827 :return: Returns the port pair group reference if it was found or None
828 :rtype: :class:`openstack.resources.port_pair_group`
830 if name_or_id
in self
.port_pair_groups
:
831 return self
.port_pair_groups
[name_or_id
]
832 for port_pair_group
in self
.port_pair_groups
.values():
833 if port_pair_group
.name
== name_or_id
:
834 return port_pair_group
838 def delete_port_pair_group(self
, name_or_id
):
840 Deletes the given port pair group. Raises an exception when the port pair group was not found!
842 :param name_or_id: UUID or name of the port pair group.
843 :type name_or_id: ``str``
845 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name_or_id
)
846 if port_pair_group
is None:
848 "Port pair with name or id %s does not exists." % name_or_id
)
850 self
.port_pair_groups
.pop(port_pair_group
.id, None)
852 def create_port_chain(self
, name
, stack_operation
=False):
854 Creates a new port chain with the given name. Raises an exception when a port chain with the given name already
857 :param name: Name of the new port chain
859 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
860 :type stack_operation: ``bool``
861 :return: Returns the created port chain.
862 :rtype: :class:`openstack.resources.port_chain.PortChain`
864 port_chain
= self
.find_port_chain_by_name_or_id(name
)
865 if port_chain
is not None and not stack_operation
:
867 "Creating port chain with name %s failed, as it already exists" % name
)
868 raise Exception("Port chain with name %s already exists." % name
)
869 logging
.debug("Creating port chain with name %s" % name
)
870 port_chain
= PortChain(name
)
871 if not stack_operation
:
872 self
.port_chains
[port_chain
.id] = port_chain
875 def find_port_chain_by_name_or_id(self
, name_or_id
):
877 Tries to find the port chain by ID and if this does not succeed then tries to find it via name.
879 :param name_or_id: UUID or name of the port chain.
880 :type name_or_id: ``str``
881 :return: Returns the port chain reference if it was found or None
882 :rtype: :class:`openstack.resources.port_chain.PortChain`
884 if name_or_id
in self
.port_chains
:
885 return self
.port_chains
[name_or_id
]
886 for port_chain
in self
.port_chains
.values():
887 if port_chain
.name
== name_or_id
:
891 def delete_port_chain(self
, name_or_id
):
893 Deletes the given port chain. Raises an exception when the port chain was not found!
895 :param name_or_id: UUID or name of the port chain.
896 :type name_or_id: ``str``
898 port_chain
= self
.find_port_chain_by_name_or_id(name_or_id
)
899 port_chain
.uninstall(self
)
900 if port_chain
is None:
902 "Port chain with name or id %s does not exists." % name_or_id
)
904 self
.port_chains
.pop(port_chain
.id, None)
906 def create_flow_classifier(self
, name
, stack_operation
=False):
908 Creates a new flow classifier with the given name. Raises an exception when a flow classifier with the given name already
911 :param name: Name of the new flow classifier.
913 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
914 :type stack_operation: ``bool``
915 :return: Returns the created flow classifier.
916 :rtype: :class:`openstack.resources.flow_classifier`
918 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name
)
919 if flow_classifier
is not None and not stack_operation
:
921 "Creating flow classifier with name %s failed, as it already exists" % name
)
923 "Flow classifier with name %s already exists." % name
)
924 logging
.debug("Creating flow classifier with name %s" % name
)
925 flow_classifier
= FlowClassifier(name
)
926 if not stack_operation
:
927 self
.flow_classifiers
[flow_classifier
.id] = flow_classifier
928 return flow_classifier
930 def find_flow_classifier_by_name_or_id(self
, name_or_id
):
932 Tries to find the flow classifier by ID and if this does not succeed then tries to find it via name.
934 :param name_or_id: UUID or name of the flow classifier.
935 :type name_or_id: ``str``
936 :return: Returns the flow classifier reference if it was found or None
937 :rtype: :class:`openstack.resources.flow_classifier`
939 if name_or_id
in self
.flow_classifiers
:
940 return self
.flow_classifiers
[name_or_id
]
941 for flow_classifier
in self
.flow_classifiers
.values():
942 if flow_classifier
.name
== name_or_id
:
943 return flow_classifier
947 def delete_flow_classifier(self
, name_or_id
):
949 Deletes the given flow classifier. Raises an exception when the flow classifier was not found!
951 :param name_or_id: UUID or name of the flow classifier.
952 :type name_or_id: ``str``
954 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name_or_id
)
955 if flow_classifier
is None:
957 "Flow classifier with name or id %s does not exists." % name_or_id
)
959 self
.flow_classifiers
.pop(flow_classifier
.id, None)
961 def _add_link(self
, node_name
, ip_address
, link_name
, net_name
):
963 Adds a new link between datacenter switch and the node with the given name.
965 :param node_name: Name of the required node.
966 :type node_name: ``str``
967 :param ip_address: IP-Address of the node.
968 :type ip_address: ``str``
969 :param link_name: Link name.
970 :type link_name: ``str``
971 :param net_name: Network name.
972 :type net_name: ``str``
974 node
= self
.dc
.net
.get(node_name
)
975 params
= {'params1': {'ip': ip_address
,
977 link_name
: net_name
},
978 'intfName1': link_name
,
980 link
= self
.dc
.net
.addLink(node
, self
.dc
.switch
, **params
)
981 OpenstackCompute
.timeout_sleep(link
.intf1
.isUp
, 1)
983 def _remove_link(self
, server_name
, link
):
985 Removes a link between server and datacenter switch.
987 :param server_name: Specifies the server where the link starts.
988 :type server_name: ``str``
989 :param link: A reference of the link which should be removed.
990 :type link: :class:`mininet.link`
992 self
.dc
.switch
.detach(link
.intf2
)
993 del self
.dc
.switch
.intfs
[self
.dc
.switch
.ports
[link
.intf2
]]
994 del self
.dc
.switch
.ports
[link
.intf2
]
995 del self
.dc
.switch
.nameToIntf
[link
.intf2
.name
]
996 self
.dc
.net
.removeLink(link
=link
)
997 for intf_key
in self
.dc
.net
[server_name
].intfs
.keys():
998 if self
.dc
.net
[server_name
].intfs
[intf_key
].link
== link
:
999 self
.dc
.net
[server_name
].intfs
[intf_key
].delete()
1000 del self
.dc
.net
[server_name
].intfs
[intf_key
]
1003 def timeout_sleep(function
, max_sleep
):
1005 This function will execute a function all 0.1 seconds until it successfully returns.
1006 Will return after `max_sleep` seconds if not successful.
1008 :param function: The function to execute. Should return true if done.
1009 :type function: ``function``
1010 :param max_sleep: Max seconds to sleep. 1 equals 1 second.
1011 :type max_sleep: ``float``
1013 current_time
= time
.time()
1014 stop_time
= current_time
+ max_sleep
1015 while not function() and current_time
< stop_time
:
1016 current_time
= time
.time()