def polish_blueprint(self, names, polishers): """ Walks a named blueprint for this facility and polish related resources :param names: the name(s) of the blueprint(s) to polish :type names: ``str`` or ``list`` of ``str`` :param polishers: polishers to be applied :type polishers: list of :class:`plumbery.PlumberyPolisher` """ if isinstance(polishers, str): polishers = PlumberyPolisher.filter(self.plumbery.polishers, polishers) self.power_on() infrastructure = PlumberyInfrastructure(self) nodes = PlumberyNodes(self) for polisher in polishers: polisher.move_to(self) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) container = infrastructure.get_container(blueprint) for polisher in polishers: polisher.shine_container(container) nodes.polish_blueprint(blueprint, polishers, container)
def stop_blueprint(self, names): """ Stops nodes of the given blueprint at this facility :param names: the name(s) of the target blueprint(s) :type names: ``str`` or ``list`` of ``str`` You can use the following setting to prevent plumbery from stopping a node:: - sql: domain: *vdc1 ethernet: *data nodes: - slaveSQL: running: always """ nodes = PlumberyNodes(self) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) if 'nodes' not in blueprint: continue nodes.stop_blueprint(blueprint)
def destroy_all_blueprints(self): """ Destroys all blueprints at this facility """ self.power_on() nodes = PlumberyNodes(self) infrastructure = PlumberyInfrastructure(self) basement = self.list_basement() for name in self.expand_blueprint('*'): if name in basement: continue blueprint = self.get_blueprint(name) plogging.debug("Destroying blueprint '{}'".format(name)) nodes.destroy_blueprint(blueprint) infrastructure.destroy_blueprint(blueprint) for name in basement: blueprint = self.get_blueprint(name) plogging.debug("Destroying blueprint '{}'".format(name)) nodes.destroy_blueprint(blueprint) infrastructure.destroy_blueprint(blueprint)
def build_blueprint(self, names): """ Builds a named blueprint for this facility :param names: the name(s) of the blueprint(s) to build :type names: ``str`` or ``list`` of ``str`` This function builds the named blueprint in two steps: the infrastructure comes first, and then the nodes themselves. >>>facility.build_blueprint('sql') If the keyword ``basement`` mentions one or several blueprints, then network domains of these special blueprints are built before the actual target blueprint. Example ``fittings.yaml``:: --- basement: admin blueprints: - admin: ethernet: control - sql: ethernet: data nodes: - server1: glue: control In this example, the node ``server1``has two network interfaces. The main network interface is connected to the network ``data``, and the secondary network interface is connected to the network ``control``. """ self.power_on() infrastructure = PlumberyInfrastructure(self) nodes = PlumberyNodes(self) basement = self.list_basement() for name in basement: blueprint = self.get_blueprint(name) infrastructure.build(blueprint) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) if name not in basement: infrastructure.build(blueprint) nodes.build_blueprint(blueprint, infrastructure.get_container(blueprint))
def lookup(self, token): """ Retrieves the value attached to a token :param token: the token, e.g., 'node.ipv6' :type token: ``str`` :return: the value attached to this token, or `None` """ if token in self.cache: return str(self.cache[token]) value = None if self.context is not None: value = self.context.lookup(token) if value is not None: return value if self.container is None: return None tokens = token.split('.') if len(tokens) < 2: tokens.append('private') nodes = PlumberyNodes(self.container.facility) node = nodes.get_node(tokens[0]) if node is None: return None if self.context is not None: self.context.remember(tokens[0], node.private_ips[0]) self.context.remember(tokens[0] + '.private', node.private_ips[0]) self.context.remember(tokens[0] + '.ipv6', node.extra['ipv6']) if len(node.public_ips) > 0: self.context.remember(tokens[0] + '.public', node.public_ips[0]) if tokens[1] == 'private': return node.private_ips[0] if tokens[1] == 'ipv6': return node.extra['ipv6'] if tokens[1] == 'public': if len(node.public_ips) > 0: return node.public_ips[0] else: return '' return None
def move_to(self, facility): """ Moves to another API endpoint :param facility: access to local parameters and functions :type facility: :class:`plumbery.PlumberyFacility` """ self.facility = facility self.region = facility.region self.nodes = PlumberyNodes(facility)
def shine_container(self, container): """ Rubs a container until it shines :param container: the container to be polished :type container: :class:`plumbery.PlumberyInfrastructure` This is where the hard work is done. You have to override this function in your own polisher. Note that you can compare the reality versus the theoritical settings if you want. """ if container.network is None: return nodes = PlumberyNodes(self.facility) names = nodes.list_nodes(container.blueprint) logging.info("Waiting for nodes to be deployed") for name in names: while True: node = nodes.get_node(name) if node is None: logging.info("- aborted - missing node '{}'".format(name)) return if node.extra['status'].action is None: break if node is not None \ and node.extra['status'].failure_reason is not None: logging.info( "- aborted - failed deployment of node '{}'".format( name)) return time.sleep(20) logging.info("- done") container._build_firewall_rules() container._reserve_ipv4() container._build_balancer()
def wipe_blueprint(self, names): """ Destroys nodes of a given blueprint at this facility :param names: the names of the blueprint to destroy :type names: ``str`` or ``list`` of ``str`` """ self.power_on() nodes = PlumberyNodes(self) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) nodes.destroy_blueprint(blueprint)
def destroy_blueprint(self, names): """ Destroys a given blueprint at this facility :param names: the name(s) of the blueprint(s) to destroy :type names: ``str`` or ``list`` of ``str`` """ self.power_on() nodes = PlumberyNodes(self) infrastructure = PlumberyInfrastructure(self) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) nodes.destroy_blueprint(blueprint) infrastructure.destroy_blueprint(blueprint)
def do_polish(polisher): engine = PlumberyEngine(myInformation) engine.set_shared_secret('fake_secret') engine.set_user_name('fake_name') engine.set_user_password('fake_password') polisher.go(engine) facility = engine.list_facility('NA9')[0] DimensionDataNodeDriver.connectionCls.conn_classes = ( None, DimensionDataMockHttp) DimensionDataMockHttp.type = None facility.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS) polisher.move_to(facility) blueprint = facility.get_blueprint('test') infrastructure = PlumberyInfrastructure(facility) container = infrastructure.get_container(blueprint) polisher.shine_container(container) nodes = PlumberyNodes(facility) node = nodes.get_node('stackstorm') polisher.shine_node(node=node, settings=fakeNodeSettings, container=container) node = nodes.get_node('node1') polisher.shine_node(node=node, settings=fakeNodeSettings, container=container) polisher.move_to(FakeFacility()) polisher.shine_container(FakeContainer()) polisher.shine_node(node=FakeNode(), settings=fakeNodeSettings, container=FakeContainer()) polisher.reap()
def start_blueprint(self, names): """ Starts nodes from a given blueprint at this facility :param names: the name(s) of the target blueprint(s) :type names: ``str`` or ``list`` of ``str`` """ nodes = PlumberyNodes(self) for name in self.expand_blueprint(names): blueprint = self.get_blueprint(name) if 'nodes' not in blueprint: continue nodes.start_blueprint(blueprint)
def build_all_blueprints(self): """ Builds all blueprints defined for this facility This function builds all network domains across all blueprints, then it builds all nodes across all blueprints. If the keyword ``basement`` mentions one or several blueprints, then these are built before the other blueprints. """ self.power_on() infrastructure = PlumberyInfrastructure(self) nodes = PlumberyNodes(self) basement = self.list_basement() for name in basement: blueprint = self.get_blueprint(name) infrastructure.build(blueprint) blueprints = self.expand_blueprint('*') for name in blueprints: if name not in basement: blueprint = self.get_blueprint(name) infrastructure.build(blueprint) for name in basement: blueprint = self.get_blueprint(name) container = infrastructure.get_container(blueprint) nodes.build_blueprint(blueprint, container) for name in blueprints: if name not in basement: blueprint = self.get_blueprint(name) container = infrastructure.get_container(blueprint) nodes.build_blueprint(blueprint, container)
def setUp(self): self.nodes = PlumberyNodes(FakeFacility())
def move_to(self, facility): """ Checks if we can beachhead at this facility :param facility: access to local parameters and functions :type facility: :class:`plumbery.PlumberyFacility` This function lists all addresses of the computer that is running plumbery. If there is at least one routable IPv6 address, then it assumes that communication with nodes is possible. If no suitable IPv6 address can be found, then plumbery falls back to IPv4. Beachheading is granted only if the address of the computer running plumbery matches the fitting parameter ``beachhead``. """ self.facility = facility self.region = facility.region self.nodes = PlumberyNodes(facility) self.beachheading = False try: self.addresses = [] for interface in netifaces.interfaces(): addresses = netifaces.ifaddresses(interface) if netifaces.AF_INET in addresses.keys(): for address in addresses[netifaces.AF_INET]: # strip local loop if address['addr'].startswith('127.0.0.1'): continue self.addresses.append(address['addr']) if netifaces.AF_INET6 in addresses.keys(): for address in addresses[netifaces.AF_INET6]: # strip local loop if address['addr'].startswith('::1'): continue # strip local link addresses if address['addr'].startswith('fe80::'): continue # we have a routable ipv6, so let's go self.beachheading = True except Exception as feedback: plogging.error(str(feedback)) for item in self.facility.get_setting('prepare', []): if not isinstance(item, dict): continue if 'beachhead' not in item.keys(): continue if item['beachhead'] in self.addresses: self.beachheading = True break if self.beachheading: plogging.debug("- beachheading at '{}'".format( self.facility.get_setting('locationId'))) else: plogging.debug("- not beachheading at '{}'".format( self.facility.get_setting('locationId')))