865b770d2ba281f8099356864d7e7ea336d7ea41
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 if t
not in self
._images
:
100 self
._images
[t
] = Image(t
)
103 def add_stack(self
, stack
):
105 Adds a new stack to the compute node.
107 :param stack: Stack dictionary.
108 :type stack: :class:`heat.resources.stack`
110 if not self
.check_stack(stack
):
111 self
.clean_broken_stack(stack
)
112 raise HeatApiStackInvalidException(
113 "Stack did not pass validity checks")
114 self
.stacks
[stack
.id] = stack
116 def clean_broken_stack(self
, stack
):
117 for port
in stack
.ports
.values():
118 if port
.id in self
.ports
:
119 del self
.ports
[port
.id]
120 for server
in stack
.servers
.values():
121 if server
.id in self
.computeUnits
:
122 del self
.computeUnits
[server
.id]
123 for net
in stack
.nets
.values():
124 if net
.id in self
.nets
:
125 del self
.nets
[net
.id]
127 def check_stack(self
, stack
):
129 Checks all dependencies of all servers, ports and routers and their most important parameters.
131 :param stack: A reference of the stack that should be checked.
132 :type stack: :class:`heat.resources.stack`
133 :return: * *True*: If the stack is completely fine.
138 for server
in stack
.servers
.values():
139 for port_name
in server
.port_names
:
140 if port_name
not in stack
.ports
:
141 LOG
.warning("Server %s of stack %s has a port named %s that is not known." %
142 (server
.name
, stack
.stack_name
, port_name
))
143 everything_ok
= False
144 if server
.image
is None:
145 LOG
.warning("Server %s holds no image." % (server
.name
))
146 everything_ok
= False
147 if server
.command
is None:
148 LOG
.warning("Server %s holds no command." % (server
.name
))
149 everything_ok
= False
150 for port
in stack
.ports
.values():
151 if port
.net_name
not in stack
.nets
:
152 LOG
.warning("Port %s of stack %s has a network named %s that is not known." %
153 (port
.name
, stack
.stack_name
, port
.net_name
))
154 everything_ok
= False
155 if port
.intf_name
is None:
156 LOG
.warning("Port %s has no interface name." % (port
.name
))
157 everything_ok
= False
158 if port
.ip_address
is None:
159 LOG
.warning("Port %s has no IP address." % (port
.name
))
160 everything_ok
= False
161 for router
in stack
.routers
.values():
162 for subnet_name
in router
.subnet_names
:
164 for net
in stack
.nets
.values():
165 if net
.subnet_name
== subnet_name
:
169 LOG
.warning("Router %s of stack %s has a network named %s that is not known." %
170 (router
.name
, stack
.stack_name
, subnet_name
))
171 everything_ok
= False
174 def add_flavor(self
, name
, cpu
, memory
,
175 memory_unit
, storage
, storage_unit
):
177 Adds a flavor to the stack.
179 :param name: Specifies the name of the flavor.
184 :type memory: ``str``
186 :type memory_unit: ``str``
188 :type storage: ``str``
190 :type storage_unit: ``str``
192 flavor
= InstanceFlavor(
193 name
, cpu
, memory
, memory_unit
, storage
, storage_unit
)
194 self
.flavors
[flavor
.name
] = flavor
197 def deploy_stack(self
, stackid
):
199 Deploys the stack and starts the emulation.
201 :param stackid: An UUID str of the stack
202 :type stackid: ``str``
203 :return: * *False*: If the Datacenter is None
210 stack
= self
.stacks
[stackid
]
211 self
.update_compute_dicts(stack
)
213 # Create the networks first
214 for server
in stack
.servers
.values():
215 self
._start
_compute
(server
)
218 def delete_stack(self
, stack_id
):
220 Delete a stack and all its components.
222 :param stack_id: An UUID str of the stack
223 :type stack_id: ``str``
224 :return: * *False*: If the Datacenter is None
231 # Stop all servers and their links of this stack
232 for server
in self
.stacks
[stack_id
].servers
.values():
233 self
.stop_compute(server
)
234 self
.delete_server(server
)
235 for net
in self
.stacks
[stack_id
].nets
.values():
236 self
.delete_network(net
.id)
237 for port
in self
.stacks
[stack_id
].ports
.values():
238 self
.delete_port(port
.id)
240 del self
.stacks
[stack_id
]
243 def update_stack(self
, old_stack_id
, new_stack
):
245 Determines differences within the old and the new stack and deletes, create or changes only parts that
246 differ between the two stacks.
248 :param old_stack_id: The ID of the old stack.
249 :type old_stack_id: ``str``
250 :param new_stack: A reference of the new stack.
251 :type new_stack: :class:`heat.resources.stack`
252 :return: * *True*: if the old stack could be updated to the new stack without any error.
256 LOG
.debug("updating stack {} with new_stack {}".format(
257 old_stack_id
, new_stack
))
258 if old_stack_id
not in self
.stacks
:
260 old_stack
= self
.stacks
[old_stack_id
]
263 for server
in old_stack
.servers
.values():
264 if server
.name
in new_stack
.servers
:
265 new_stack
.servers
[server
.name
].id = server
.id
266 for net
in old_stack
.nets
.values():
267 if net
.name
in new_stack
.nets
:
268 new_stack
.nets
[net
.name
].id = net
.id
269 for subnet
in new_stack
.nets
.values():
270 if subnet
.subnet_name
== net
.subnet_name
:
271 subnet
.subnet_id
= net
.subnet_id
273 for port
in old_stack
.ports
.values():
274 if port
.name
in new_stack
.ports
:
275 new_stack
.ports
[port
.name
].id = port
.id
276 for router
in old_stack
.routers
.values():
277 if router
.name
in new_stack
.routers
:
278 new_stack
.routers
[router
.name
].id = router
.id
280 # Update the compute dicts to now contain the new_stack components
281 self
.update_compute_dicts(new_stack
)
283 self
.update_ip_addresses(old_stack
, new_stack
)
285 # Update all interface names - after each port has the correct UUID!!
286 for port
in new_stack
.ports
.values():
287 port
.create_intf_name()
289 if not self
.check_stack(new_stack
):
292 # Remove unnecessary networks
293 for net
in old_stack
.nets
.values():
294 if net
.name
not in new_stack
.nets
:
295 self
.delete_network(net
.id)
297 # Remove all unnecessary servers
298 for server
in old_stack
.servers
.values():
299 if server
.name
in new_stack
.servers
:
300 if not server
.compare_attributes(
301 new_stack
.servers
[server
.name
]):
302 self
.stop_compute(server
)
304 # Delete unused and changed links
305 for port_name
in server
.port_names
:
306 if port_name
in old_stack
.ports
and port_name
in new_stack
.ports
:
307 if not old_stack
.ports
.get(
308 port_name
) == new_stack
.ports
.get(port_name
):
309 my_links
= self
.dc
.net
.links
310 for link
in my_links
:
311 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
312 str(link
.intf1
.ip
) == \
313 old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
314 self
._remove
_link
(server
.name
, link
)
317 self
._add
_link
(server
.name
,
318 new_stack
.ports
[port_name
].ip_address
,
319 new_stack
.ports
[port_name
].intf_name
,
320 new_stack
.ports
[port_name
].net_name
)
323 my_links
= self
.dc
.net
.links
324 for link
in my_links
:
325 if str(link
.intf1
) == old_stack
.ports
[port_name
].intf_name
and \
326 str(link
.intf1
.ip
) == old_stack
.ports
[port_name
].ip_address
.split('/')[0]:
327 self
._remove
_link
(server
.name
, link
)
331 for port_name
in new_stack
.servers
[server
.name
].port_names
:
332 if port_name
not in server
.port_names
:
333 self
._add
_link
(server
.name
,
334 new_stack
.ports
[port_name
].ip_address
,
335 new_stack
.ports
[port_name
].intf_name
,
336 new_stack
.ports
[port_name
].net_name
)
338 self
.stop_compute(server
)
340 # Start all new servers
341 for server
in new_stack
.servers
.values():
342 if server
.name
not in self
.dc
.containers
:
343 self
._start
_compute
(server
)
345 server
.emulator_compute
= self
.dc
.containers
.get(server
.name
)
347 del self
.stacks
[old_stack_id
]
348 self
.stacks
[new_stack
.id] = new_stack
351 def update_ip_addresses(self
, old_stack
, new_stack
):
353 Updates the subnet and the port IP addresses - which should always be in this order!
355 :param old_stack: The currently running stack
356 :type old_stack: :class:`heat.resources.stack`
357 :param new_stack: The new created stack
358 :type new_stack: :class:`heat.resources.stack`
360 self
.update_subnet_cidr(old_stack
, new_stack
)
361 self
.update_port_addresses(old_stack
, new_stack
)
363 def update_port_addresses(self
, old_stack
, new_stack
):
365 Updates the port IP addresses. First resets all issued addresses. Then get all IP addresses from the old
366 stack and sets them to the same ports in the new stack. Finally all new or changed instances will get new
369 :param old_stack: The currently running stack
370 :type old_stack: :class:`heat.resources.stack`
371 :param new_stack: The new created stack
372 :type new_stack: :class:`heat.resources.stack`
374 for net
in new_stack
.nets
.values():
375 net
.reset_issued_ip_addresses()
377 for old_port
in old_stack
.ports
.values():
378 for port
in new_stack
.ports
.values():
379 if port
.compare_attributes(old_port
):
380 for net
in new_stack
.nets
.values():
381 if net
.name
== port
.net_name
:
382 if net
.assign_ip_address(
383 old_port
.ip_address
, port
.name
):
384 port
.ip_address
= old_port
.ip_address
385 port
.mac_address
= old_port
.mac_address
387 port
.ip_address
= net
.get_new_ip_address(
390 for port
in new_stack
.ports
.values():
391 for net
in new_stack
.nets
.values():
392 if port
.net_name
== net
.name
and not net
.is_my_ip(
393 port
.ip_address
, port
.name
):
394 port
.ip_address
= net
.get_new_ip_address(port
.name
)
396 def update_subnet_cidr(self
, old_stack
, new_stack
):
398 Updates the subnet IP addresses. If the new stack contains subnets from the old stack it will take those
399 IP addresses. Otherwise it will create new IP addresses for the subnet.
401 :param old_stack: The currently running stack
402 :type old_stack: :class:`heat.resources.stack`
403 :param new_stack: The new created stack
404 :type new_stack: :class:`heat.resources.stack`
406 for old_subnet
in old_stack
.nets
.values():
407 IP
.free_cidr(old_subnet
.get_cidr(), old_subnet
.subnet_id
)
409 for subnet
in new_stack
.nets
.values():
411 for old_subnet
in old_stack
.nets
.values():
412 if subnet
.subnet_name
== old_subnet
.subnet_name
:
413 if IP
.assign_cidr(old_subnet
.get_cidr(), subnet
.subnet_id
):
414 subnet
.set_cidr(old_subnet
.get_cidr())
416 for subnet
in new_stack
.nets
.values():
417 if IP
.is_cidr_issued(subnet
.get_cidr()):
420 cird
= IP
.get_new_cidr(subnet
.subnet_id
)
421 subnet
.set_cidr(cird
)
424 def update_compute_dicts(self
, stack
):
426 Update and add all stack components tho the compute dictionaries.
428 :param stack: A stack reference, to get all required components.
429 :type stack: :class:`heat.resources.stack`
431 for server
in stack
.servers
.values():
432 self
.computeUnits
[server
.id] = server
433 if isinstance(server
.flavor
, dict):
434 self
.add_flavor(server
.flavor
['flavorName'],
435 server
.flavor
['vcpu'],
436 server
.flavor
['ram'], 'MB',
437 server
.flavor
['storage'], 'GB')
438 server
.flavor
= server
.flavor
['flavorName']
439 for router
in stack
.routers
.values():
440 self
.routers
[router
.id] = router
441 for net
in stack
.nets
.values():
442 self
.nets
[net
.id] = net
443 for port
in stack
.ports
.values():
444 self
.ports
[port
.id] = port
446 def _start_compute(self
, server
):
448 Starts a new compute object (docker container) inside the emulator.
449 Should only be called by stack modifications and not directly.
451 :param server: Specifies the compute resource.
452 :type server: :class:`heat.resources.server`
454 LOG
.debug("Starting new compute resources %s" % server
.name
)
456 network_dict
= dict()
458 for port_name
in server
.port_names
:
459 network_dict
= dict()
460 port
= self
.find_port_by_name_or_id(port_name
)
462 network_dict
['id'] = port
.intf_name
463 network_dict
['ip'] = port
.ip_address
464 network_dict
[network_dict
['id']] = self
.find_network_by_name_or_id(
466 network
.append(network_dict
)
467 # default network dict
469 network_dict
['id'] = server
.name
+ "-eth0"
470 network_dict
[network_dict
['id']] = network_dict
['id']
471 network
.append(network_dict
)
473 self
.compute_nets
[server
.name
] = network
474 LOG
.debug("Network dict: {}".format(network
))
475 c
= self
.dc
.startCompute(server
.name
, image
=server
.image
, command
=server
.command
,
476 network
=network
, flavor_name
=server
.flavor
,
477 properties
=server
.properties
)
478 server
.emulator_compute
= c
480 for intf
in c
.intfs
.values():
481 for port_name
in server
.port_names
:
482 port
= self
.find_port_by_name_or_id(port_name
)
484 if intf
.name
== port
.intf_name
:
485 # wait up to one second for the intf to come up
486 self
.timeout_sleep(intf
.isUp
, 1)
487 if port
.mac_address
is not None:
488 intf
.setMAC(port
.mac_address
)
490 port
.mac_address
= intf
.MAC()
492 # Start the real emulator command now as specified in the dockerfile
494 config
= c
.dcinfo
.get("Config", dict())
495 env
= config
.get("Env", list())
497 if "SON_EMU_CMD=" in env_var
:
498 cmd
= str(env_var
.split("=")[1])
499 server
.son_emu_command
= cmd
500 # execute command in new thread to ensure that GK is not
502 t
= threading
.Thread(target
=c
.cmdPrint
, args
=(cmd
,))
506 def stop_compute(self
, server
):
508 Determines which links should be removed before removing the server itself.
510 :param server: The server that should be removed
511 :type server: ``heat.resources.server``
513 LOG
.debug("Stopping container %s with full name %s" %
514 (server
.name
, server
.full_name
))
516 for port_name
in server
.port_names
:
517 prt
= self
.find_port_by_name_or_id(port_name
)
519 link_names
.append(prt
.intf_name
)
520 my_links
= self
.dc
.net
.links
521 for link
in my_links
:
522 if str(link
.intf1
) in link_names
:
523 # Remove all self created links that connect the server to the
525 self
._remove
_link
(server
.name
, link
)
527 # Stop the server and the remaining connection to the datacenter switch
528 self
.dc
.stopCompute(server
.name
)
529 # Only now delete all its ports and the server itself
530 for port_name
in server
.port_names
:
531 self
.delete_port(port_name
)
532 self
.delete_server(server
)
534 def find_server_by_name_or_id(self
, name_or_id
):
536 Tries to find the server by ID and if this does not succeed then tries to find it via name.
538 :param name_or_id: UUID or name of the server.
539 :type name_or_id: ``str``
540 :return: Returns the server reference if it was found or None
541 :rtype: :class:`heat.resources.server`
543 if name_or_id
in self
.computeUnits
:
544 return self
.computeUnits
[name_or_id
]
546 if self
._shorten
_server
_name
(name_or_id
) in self
.computeUnits
:
547 return self
.computeUnits
[name_or_id
]
549 for server
in self
.computeUnits
.values():
550 if (server
.name
== name_or_id
or
551 server
.template_name
== name_or_id
or
552 server
.full_name
== name_or_id
):
554 if (server
.name
== self
._shorten
_server
_name
(name_or_id
) or
555 server
.template_name
== self
._shorten
_server
_name
(name_or_id
) or
556 server
.full_name
== self
._shorten
_server
_name
(name_or_id
)):
560 def create_server(self
, name
, stack_operation
=False):
562 Creates a server with the specified name. Raises an exception when a server with the given name already
565 :param name: Name of the new server.
567 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
568 :type stack_operation: ``bool``
569 :return: Returns the created server.
570 :rtype: :class:`heat.resources.server`
572 if self
.find_server_by_name_or_id(
573 name
) is not None and not stack_operation
:
574 raise Exception("Server with name %s already exists." % name
)
575 safe_name
= self
._shorten
_server
_name
(name
)
576 server
= Server(safe_name
)
577 server
.id = str(uuid
.uuid4())
578 if not stack_operation
:
579 self
.computeUnits
[server
.id] = server
582 def _shorten_server_name(self
, name
, char_limit
=9):
584 Docker does not like too long instance names.
585 This function provides a shorter name if needed
587 if len(name
) > char_limit
:
588 LOG
.info("Long server name: {}".format(name
))
589 # construct a short name
590 h
= hashlib
.sha224(name
).hexdigest()
592 LOG
.info("Short server name: {}".format(h
))
595 def delete_server(self
, server
):
597 Deletes the given server from the stack dictionary and the computeUnits dictionary.
599 :param server: Reference of the server that should be deleted.
600 :type server: :class:`heat.resources.server`
601 :return: * *False*: If the server name is not in the correct format ('datacentername_stackname_servername') \
602 or when no stack with the correct stackname was found.
608 name_parts
= server
.name
.split('_')
609 if len(name_parts
) > 1:
610 for stack
in self
.stacks
.values():
611 if stack
.stack_name
== name_parts
[1]:
612 stack
.servers
.pop(server
.id, None)
613 if self
.computeUnits
.pop(server
.id, None) is None:
617 def find_network_by_name_or_id(self
, name_or_id
):
619 Tries to find the network by ID and if this does not succeed then tries to find it via name.
621 :param name_or_id: UUID or name of the network.
622 :type name_or_id: ``str``
623 :return: Returns the network reference if it was found or None
624 :rtype: :class:`heat.resources.net`
626 if name_or_id
in self
.nets
:
627 return self
.nets
[name_or_id
]
628 for net
in self
.nets
.values():
629 if net
.name
== name_or_id
:
631 LOG
.warning("Could not find net '{}' in {} or {}"
634 [n
.name
for n
in self
.nets
.values()]))
637 def create_network(self
, name
, stack_operation
=False):
639 Creates a new network with the given name. Raises an exception when a network with the given name already
642 :param name: Name of the new network.
644 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
645 :type stack_operation: ``bool``
646 :return: :class:`heat.resources.net`
648 LOG
.debug("Creating network with name %s" % name
)
649 if self
.find_network_by_name_or_id(
650 name
) is not None and not stack_operation
:
652 "Creating network with name %s failed, as it already exists" % name
)
653 raise Exception("Network with name %s already exists." % name
)
655 network
.id = str(uuid
.uuid4())
656 if not stack_operation
:
657 self
.nets
[network
.id] = network
660 def delete_network(self
, name_or_id
):
662 Deletes the given network.
664 :param name_or_id: Name or UUID of the network.
665 :type name_or_id: ``str``
667 net
= self
.find_network_by_name_or_id(name_or_id
)
670 "Network with name or id %s does not exists." % name_or_id
)
672 for stack
in self
.stacks
.values():
673 stack
.nets
.pop(net
.name
, None)
675 self
.nets
.pop(net
.id, None)
677 def create_port(self
, name
, stack_operation
=False):
679 Creates a new port with the given name. Raises an exception when a port with the given name already
682 :param name: Name of the new port.
684 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
685 :type stack_operation: ``bool``
686 :return: Returns the created port.
687 :rtype: :class:`heat.resources.port`
689 port
= self
.find_port_by_name_or_id(name
)
690 if port
is not None and not stack_operation
:
692 "Creating port with name %s failed, as it already exists" % name
)
693 raise Exception("Port with name %s already exists." % name
)
694 LOG
.debug("Creating port with name %s" % name
)
696 if not stack_operation
:
697 self
.ports
[port
.id] = port
698 port
.create_intf_name()
701 def find_port_by_name_or_id(self
, name_or_id
):
703 Tries to find the port by ID and if this does not succeed then tries to find it via name.
705 :param name_or_id: UUID or name of the network.
706 :type name_or_id: ``str``
707 :return: Returns the port reference if it was found or None
708 :rtype: :class:`heat.resources.port`
710 if name_or_id
in self
.ports
:
711 return self
.ports
[name_or_id
]
712 for port
in self
.ports
.values():
713 if port
.name
== name_or_id
or port
.template_name
== name_or_id
:
718 def delete_port(self
, name_or_id
):
720 Deletes the given port. Raises an exception when the port was not found!
722 :param name_or_id: UUID or name of the port.
723 :type name_or_id: ``str``
725 port
= self
.find_port_by_name_or_id(name_or_id
)
728 "Port with name or id %s does not exist. Can't delete it." % name_or_id
)
731 my_links
= self
.dc
.net
.links
732 for link
in my_links
:
733 if str(link
.intf1
) == port
.intf_name
:
734 self
._remove
_link
(link
.intf1
.node
.name
, link
)
737 self
.ports
.pop(port
.id, None)
738 for stack
in self
.stacks
.values():
739 stack
.ports
.pop(port
.name
, None)
741 def create_port_pair(self
, name
, stack_operation
=False):
743 Creates a new port pair with the given name. Raises an exception when a port pair with the given name already
746 :param name: Name of the new port pair.
748 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
749 :type stack_operation: ``bool``
750 :return: Returns the created port pair.
751 :rtype: :class:`openstack.resources.port_pair`
753 port_pair
= self
.find_port_pair_by_name_or_id(name
)
754 if port_pair
is not None and not stack_operation
:
756 "Creating port pair with name %s failed, as it already exists" % name
)
757 raise Exception("Port pair with name %s already exists." % name
)
758 logging
.debug("Creating port pair with name %s" % name
)
759 port_pair
= PortPair(name
)
760 if not stack_operation
:
761 self
.port_pairs
[port_pair
.id] = port_pair
764 def find_port_pair_by_name_or_id(self
, name_or_id
):
766 Tries to find the port pair by ID and if this does not succeed then tries to find it via name.
768 :param name_or_id: UUID or name of the port pair.
769 :type name_or_id: ``str``
770 :return: Returns the port pair reference if it was found or None
771 :rtype: :class:`openstack.resources.port_pair`
773 if name_or_id
in self
.port_pairs
:
774 return self
.port_pairs
[name_or_id
]
775 for port_pair
in self
.port_pairs
.values():
776 if port_pair
.name
== name_or_id
:
781 def delete_port_pair(self
, name_or_id
):
783 Deletes the given port pair. Raises an exception when the port pair was not found!
785 :param name_or_id: UUID or name of the port pair.
786 :type name_or_id: ``str``
788 port_pair
= self
.find_port_pair_by_name_or_id(name_or_id
)
789 if port_pair
is None:
791 "Port pair with name or id %s does not exists." % name_or_id
)
793 self
.port_pairs
.pop(port_pair
.id, None)
795 def create_port_pair_group(self
, name
, stack_operation
=False):
797 Creates a new port pair group with the given name. Raises an exception when a port pair group
798 with the given name already exists!
800 :param name: Name of the new port pair group.
802 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
803 :type stack_operation: ``bool``
804 :return: Returns the created port pair group .
805 :rtype: :class:`openstack.resources.port_pair_group`
807 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name
)
808 if port_pair_group
is not None and not stack_operation
:
810 "Creating port pair group with name %s failed, as it already exists" % name
)
812 "Port pair group with name %s already exists." % name
)
813 logging
.debug("Creating port pair group with name %s" % name
)
814 port_pair_group
= PortPairGroup(name
)
815 if not stack_operation
:
816 self
.port_pair_groups
[port_pair_group
.id] = port_pair_group
817 return port_pair_group
819 def find_port_pair_group_by_name_or_id(self
, name_or_id
):
821 Tries to find the port pair group by ID and if this does not succeed then tries to find it via name.
823 :param name_or_id: UUID or name of the port pair group.
824 :type name_or_id: ``str``
825 :return: Returns the port pair group reference if it was found or None
826 :rtype: :class:`openstack.resources.port_pair_group`
828 if name_or_id
in self
.port_pair_groups
:
829 return self
.port_pair_groups
[name_or_id
]
830 for port_pair_group
in self
.port_pair_groups
.values():
831 if port_pair_group
.name
== name_or_id
:
832 return port_pair_group
836 def delete_port_pair_group(self
, name_or_id
):
838 Deletes the given port pair group. Raises an exception when the port pair group was not found!
840 :param name_or_id: UUID or name of the port pair group.
841 :type name_or_id: ``str``
843 port_pair_group
= self
.find_port_pair_group_by_name_or_id(name_or_id
)
844 if port_pair_group
is None:
846 "Port pair with name or id %s does not exists." % name_or_id
)
848 self
.port_pair_groups
.pop(port_pair_group
.id, None)
850 def create_port_chain(self
, name
, stack_operation
=False):
852 Creates a new port chain with the given name. Raises an exception when a port chain with the given name already
855 :param name: Name of the new port chain
857 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
858 :type stack_operation: ``bool``
859 :return: Returns the created port chain.
860 :rtype: :class:`openstack.resources.port_chain.PortChain`
862 port_chain
= self
.find_port_chain_by_name_or_id(name
)
863 if port_chain
is not None and not stack_operation
:
865 "Creating port chain with name %s failed, as it already exists" % name
)
866 raise Exception("Port chain with name %s already exists." % name
)
867 logging
.debug("Creating port chain with name %s" % name
)
868 port_chain
= PortChain(name
)
869 if not stack_operation
:
870 self
.port_chains
[port_chain
.id] = port_chain
873 def find_port_chain_by_name_or_id(self
, name_or_id
):
875 Tries to find the port chain by ID and if this does not succeed then tries to find it via name.
877 :param name_or_id: UUID or name of the port chain.
878 :type name_or_id: ``str``
879 :return: Returns the port chain reference if it was found or None
880 :rtype: :class:`openstack.resources.port_chain.PortChain`
882 if name_or_id
in self
.port_chains
:
883 return self
.port_chains
[name_or_id
]
884 for port_chain
in self
.port_chains
.values():
885 if port_chain
.name
== name_or_id
:
889 def delete_port_chain(self
, name_or_id
):
891 Deletes the given port chain. Raises an exception when the port chain was not found!
893 :param name_or_id: UUID or name of the port chain.
894 :type name_or_id: ``str``
896 port_chain
= self
.find_port_chain_by_name_or_id(name_or_id
)
897 port_chain
.uninstall(self
)
898 if port_chain
is None:
900 "Port chain with name or id %s does not exists." % name_or_id
)
902 self
.port_chains
.pop(port_chain
.id, None)
904 def create_flow_classifier(self
, name
, stack_operation
=False):
906 Creates a new flow classifier with the given name. Raises an exception when a flow classifier with the given name already
909 :param name: Name of the new flow classifier.
911 :param stack_operation: Allows the heat parser to create modules without adapting the current emulation.
912 :type stack_operation: ``bool``
913 :return: Returns the created flow classifier.
914 :rtype: :class:`openstack.resources.flow_classifier`
916 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name
)
917 if flow_classifier
is not None and not stack_operation
:
919 "Creating flow classifier with name %s failed, as it already exists" % name
)
921 "Flow classifier with name %s already exists." % name
)
922 logging
.debug("Creating flow classifier with name %s" % name
)
923 flow_classifier
= FlowClassifier(name
)
924 if not stack_operation
:
925 self
.flow_classifiers
[flow_classifier
.id] = flow_classifier
926 return flow_classifier
928 def find_flow_classifier_by_name_or_id(self
, name_or_id
):
930 Tries to find the flow classifier by ID and if this does not succeed then tries to find it via name.
932 :param name_or_id: UUID or name of the flow classifier.
933 :type name_or_id: ``str``
934 :return: Returns the flow classifier reference if it was found or None
935 :rtype: :class:`openstack.resources.flow_classifier`
937 if name_or_id
in self
.flow_classifiers
:
938 return self
.flow_classifiers
[name_or_id
]
939 for flow_classifier
in self
.flow_classifiers
.values():
940 if flow_classifier
.name
== name_or_id
:
941 return flow_classifier
945 def delete_flow_classifier(self
, name_or_id
):
947 Deletes the given flow classifier. Raises an exception when the flow classifier was not found!
949 :param name_or_id: UUID or name of the flow classifier.
950 :type name_or_id: ``str``
952 flow_classifier
= self
.find_flow_classifier_by_name_or_id(name_or_id
)
953 if flow_classifier
is None:
955 "Flow classifier with name or id %s does not exists." % name_or_id
)
957 self
.flow_classifiers
.pop(flow_classifier
.id, None)
959 def _add_link(self
, node_name
, ip_address
, link_name
, net_name
):
961 Adds a new link between datacenter switch and the node with the given name.
963 :param node_name: Name of the required node.
964 :type node_name: ``str``
965 :param ip_address: IP-Address of the node.
966 :type ip_address: ``str``
967 :param link_name: Link name.
968 :type link_name: ``str``
969 :param net_name: Network name.
970 :type net_name: ``str``
972 node
= self
.dc
.net
.get(node_name
)
973 params
= {'params1': {'ip': ip_address
,
975 link_name
: net_name
},
976 'intfName1': link_name
,
978 link
= self
.dc
.net
.addLink(node
, self
.dc
.switch
, **params
)
979 OpenstackCompute
.timeout_sleep(link
.intf1
.isUp
, 1)
981 def _remove_link(self
, server_name
, link
):
983 Removes a link between server and datacenter switch.
985 :param server_name: Specifies the server where the link starts.
986 :type server_name: ``str``
987 :param link: A reference of the link which should be removed.
988 :type link: :class:`mininet.link`
990 self
.dc
.switch
.detach(link
.intf2
)
991 del self
.dc
.switch
.intfs
[self
.dc
.switch
.ports
[link
.intf2
]]
992 del self
.dc
.switch
.ports
[link
.intf2
]
993 del self
.dc
.switch
.nameToIntf
[link
.intf2
.name
]
994 self
.dc
.net
.removeLink(link
=link
)
995 for intf_key
in self
.dc
.net
[server_name
].intfs
.keys():
996 if self
.dc
.net
[server_name
].intfs
[intf_key
].link
== link
:
997 self
.dc
.net
[server_name
].intfs
[intf_key
].delete()
998 del self
.dc
.net
[server_name
].intfs
[intf_key
]
1001 def timeout_sleep(function
, max_sleep
):
1003 This function will execute a function all 0.1 seconds until it successfully returns.
1004 Will return after `max_sleep` seconds if not successful.
1006 :param function: The function to execute. Should return true if done.
1007 :type function: ``function``
1008 :param max_sleep: Max seconds to sleep. 1 equals 1 second.
1009 :type max_sleep: ``float``
1011 current_time
= time
.time()
1012 stop_time
= current_time
+ max_sleep
1013 while not function() and current_time
< stop_time
:
1014 current_time
= time
.time()