def test_as_library(self): engine = PlumberyEngine(myEuropeanPlan, myAmericanBinding) DimensionDataNodeDriver.connectionCls.conn_classes = ( None, DimensionDataMockHttp) DimensionDataMockHttp.type = None self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS) engine.set_shared_secret('fake_secret') engine.set_user_name('fake_name') engine.set_user_password('fake_password') facilities = engine.list_facility('NA9') self.assertEqual(len(facilities), 1) facility = facilities[0] self.assertEqual(facility.get_setting('regionId'), 'dd-na') self.assertEqual(facility.get_setting('locationId'), 'NA9') self.assertTrue(facility.get_blueprint('fake') is None) blueprint = facility.get_blueprint('myBlueprint') node = blueprint['nodes'][0] self.assertEqual(list(node)[0], 'toto') config = node['toto']['cloud-config'] self.assertEqual(config['hostname'], 'toto') self.assertEqual(config['write_files'][0]['content'].count('toto'), 6) engine.do('deploy') engine.do('dispose')
def test_as_library(self): engine = PlumberyEngine(myFacility) DimensionDataNodeDriver.connectionCls.conn_classes = ( None, DimensionDataMockHttp) DimensionDataMockHttp.type = None self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS) engine.set_shared_secret('fake_secret') engine.set_user_name('fake_name') engine.set_user_password('fake_password') facilities = engine.list_facility('NA9') self.assertEqual(len(facilities), 1) facility = facilities[0] self.assertEqual(facility.get_setting('regionId'), 'dd-na') self.assertEqual(facility.get_setting('locationId'), 'NA9') blueprint = facility.get_blueprint('fake') self.assertEqual(blueprint.keys(), ['ethernet', 'domain', 'nodes', 'target']) engine.do('deploy') engine.do('refresh') engine.do('dispose')
def process(self, item, counter): """ Processes one action Example actions: ('deploy', '') ('dispose', '') """ print('Worker is working on {}'.format(counter)) (verb, parameters) = item try: fittings = self.context.get('plumbery.fittings', '.') \ +'/'+self.context.get('worker.template', 'example/first') \ +'/fittings.yaml' print('- reading {}'.format(fittings)) print('- loading plumbery engine') engine = PlumberyEngine(fittings) except Exception as feedback: print("Error while reading fittings plan") self.outbox.put("Error while reading fittings plan") return try: engine.do(verb) self.outbox.put(engine.document_elapsed()) except Exception as feedback: print("Unable to do '{}'".format(verb)) self.outbox.put("Unable to do '{}'".format(verb)) return
def test_lifecycle(self): engine = PlumberyEngine() DimensionDataNodeDriver.connectionCls.conn_classes = ( None, DimensionDataMockHttp) DimensionDataMockHttp.type = None self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS) engine.set_shared_secret('fake_secret') engine.set_user_name('fake_name') engine.set_user_password('fake_password') engine.do('build') engine.build_all_blueprints() engine.build_blueprint('myBlueprint') engine.do('start') engine.start_all_blueprints() engine.start_blueprint('myBlueprint') engine.do('polish') engine.polish_all_blueprints() engine.polish_blueprint('myBlueprint') engine.do('stop') engine.stop_all_blueprints() engine.stop_blueprint('myBlueprint') engine.wipe_all_blueprints() engine.wipe_blueprint('myBlueprint') engine.do('destroy') engine.destroy_all_blueprints() engine.destroy_blueprint('myBlueprint') banner = engine.document_elapsed() self.assertEqual('Worked for you' in banner, True)
def main(args=None, engine=None): """ Runs plumbery from the command line :param args: arguments to be considered for this invocation :type args: a list of ``str`` :param engine: an instance of the plumbery engine :type engine: :class:`plumbery.PlumberEngine` Example:: $ python -m plumbery fittings.yaml build web In this example, plumbery loads fittings plan from ``fittings.yaml``, then it builds the blueprint named ``web``. If no blueprint is mentioned, then plumbery looks at all blueprint definitions in the fittings plan. In other terms, the following command builds the entire fittings plan, eventually across multiple facilities:: $ python -m plumbery fittings.yaml build Of course, plumbery can be invoked through the entire life cycle of your fittings:: $ python -m plumbery fittings.yaml build $ python -m plumbery fittings.yaml start $ python -m plumbery fittings.yaml polish ... nodes are up and running ... $ python -m plumbery fittings.yaml stop ... nodes have been stopped ... $ python -m plumbery fittings.yaml wipe ... nodes have been destroyed, but the infrastructure remains ... $ python -m plumbery fittings.yaml destroy ... every virtual resources has been removed ... To focus at a single location, put the character '@' followed by the id. For example, to build fittings only at 'NA12' you would type:: $ python -m plumbery fittings.yaml build @NA12 To focus on one blueprint just mention its name on the command line. For example, if fittings plan has a blueprint for nodes running Docker, then you may use following statements to bootstrap each node:: $ python -m plumbery fittings.yaml build docker $ python -m plumbery fittings.yaml start docker $ python -m plumbery fittings.yaml prepare docker ... Docker is up and running at multiple nodes ... If you create a new polisher and put it in the directory ``plumbery\polishers``, then it will become automatically available:: $ python -m plumbery fittings.yaml my_special_stuff To get some help, you can type:: $ python -m plumbery -h """ # part 1 - understand what the user wants if args is None: args = sys.argv[1:] try: args = parse_args(args) except Exception as feedback: plogging.error("Incorrect arguments. " "Maybe the following can help: python -m plumbery -h") if plogging.getEffectiveLevel() == logging.DEBUG: raise else: plogging.error("{}: {}".format(feedback.__class__.__name__, str(feedback))) sys.exit(2) # part 2 - get a valid and configured engine if engine is None: try: engine = PlumberyEngine(args.fittings, args.parameters) if args.safe: engine.safeMode = True except Exception as feedback: if plogging.getEffectiveLevel() == logging.DEBUG: plogging.error("Cannot read fittings plan from '{}'".format( args.fittings)) raise else: plogging.error("Cannot read fittings plan from '{}'" ", run with -d for debug".format(args.fittings)) plogging.error("{}: {}".format(feedback.__class__.__name__, str(feedback))) sys.exit(2) # part 3 - do the job try: engine.do(args.action, args.blueprints, args.facilities) plogging.info(engine.document_elapsed()) except Exception as feedback: if plogging.getEffectiveLevel() == logging.DEBUG: plogging.error("Unable to do '{}'".format(args.action)) raise else: plogging.error("Unable to do '{}', run with -d for debug".format( args.action)) plogging.error("{}: {}".format(feedback.__class__.__name__, str(feedback))) sys.exit(1)
class TestPlumberyEngine(unittest.TestCase): def test_set(self): settings = { 'safeMode': False, 'polishers': [ {'ansible': {}}, {'spit': {}}, ] } self.engine = PlumberyEngine() self.engine.set_shared_secret('fake_secret') self.assertEqual(self.engine.get_shared_secret(), 'fake_secret') random = self.engine.get_secret('random') self.assertEqual(len(random), 9) self.assertEqual(self.engine.get_secret('random'), random) self.engine.set_user_name('fake_name') self.assertEqual(self.engine.get_user_name(), 'fake_name') self.engine.set_user_password('fake_password') self.assertEqual(self.engine.get_user_password(), 'fake_password') self.engine.set(settings) self.assertEqual(self.engine.safeMode, False) try: self.engine.from_text(myPlan) cloudConfig = self.engine.get_cloud_config() self.assertEqual(len(cloudConfig.keys()), 3) self.engine.add_facility(myFacility) self.assertEqual(len(self.engine.facilities), 2) except socket.gaierror: pass except InvalidCredsError: pass def test_lifecycle(self): self.engine = PlumberyEngine() self.engine.set_shared_secret('fake_secret') self.assertEqual(self.engine.get_shared_secret(), 'fake_secret') self.engine.set_user_name('fake_name') self.assertEqual(self.engine.get_user_name(), 'fake_name') self.engine.set_user_password('fake_password') self.assertEqual(self.engine.get_user_password(), 'fake_password') try: self.engine.do('build') self.engine.build_all_blueprints() self.engine.build_blueprint('myBlueprint') self.engine.do('start') self.engine.start_all_blueprints() self.engine.start_blueprint('myBlueprint') self.engine.do('polish') self.engine.polish_all_blueprints() self.engine.polish_blueprint('myBlueprint') self.engine.do('stop') self.engine.stop_all_blueprints() self.engine.stop_blueprint('myBlueprint') self.engine.wipe_all_blueprints() self.engine.wipe_blueprint('myBlueprint') self.engine.do('destroy') self.engine.destroy_all_blueprints() self.engine.destroy_blueprint('myBlueprint') except socket.gaierror: pass except InvalidCredsError: pass def test_lookup(self): self.engine = PlumberyEngine() self.assertEqual(self.engine.lookup('plumbery.version'), __version__) self.engine.secrets = {} random = self.engine.lookup('random.secret') self.assertEqual(len(random), 9) self.assertEqual(self.engine.lookup('random.secret'), random) md5 = self.engine.lookup('random.md5.secret') self.assertEqual(len(md5), 32) self.assertNotEqual(md5, random) sha = self.engine.lookup('random.sha1.secret') self.assertEqual(len(sha), 40) self.assertNotEqual(sha, random) sha = self.engine.lookup('random.sha256.secret') self.assertEqual(len(sha), 64) self.assertNotEqual(sha, random) id1 = self.engine.lookup('id1.uuid') self.assertEqual(len(id1), 36) self.assertEqual(self.engine.lookup('id1.uuid'), id1) id2 = self.engine.lookup('id2.uuid') self.assertEqual(len(id2), 36) self.assertNotEqual(id1, id2) self.engine.lookup('application.secret') self.engine.lookup('database.secret') self.engine.lookup('master.secret') self.engine.lookup('slave.secret') original = 'hello world' text = self.engine.lookup('pair1.rsa_public') self.assertEqual(text.startswith('ssh-rsa '), True) key = RSA.importKey(text) encrypted = key.publickey().encrypt(original, 32) privateKey = self.engine.lookup('pair1.rsa_private') self.assertEqual(privateKey.startswith( '-----BEGIN RSA PRIVATE KEY-----'), True) key = RSA.importKey(self.engine.lookup('pair1.rsa_private')) decrypted = key.decrypt(ast.literal_eval(str(encrypted))) self.assertEqual(decrypted, original) self.assertEqual(len(self.engine.secrets), 12) with self.assertRaises(LookupError): localKey = self.engine.lookup('local.rsa_private') localKey = self.engine.lookup('local.rsa_public') try: path = '~/.ssh/id_rsa.pub' with open(os.path.expanduser(path)) as stream: text = stream.read() stream.close() self.assertEqual(localKey.strip(), text.strip()) logging.info("Successful lookup of local public key") except IOError: pass def test_secrets(self): engine = PlumberyEngine() engine.secrets = {'hello': 'world'} engine.save_secrets(plan='test_engine.yaml') self.assertEqual(os.path.isfile('.test_engine.secrets'), True) engine.secrets = {} engine.load_secrets(plan='test_engine.yaml') self.assertEqual(engine.secrets['hello'], 'world') engine.forget_secrets(plan='test_engine.yaml') self.assertEqual(os.path.isfile('.test_engine.secrets'), False) def test_defaults(self): engine = PlumberyEngine() engine.from_text(defaultsPlan) self.assertEqual(engine.get_default('locationId'), 'EU6') self.assertEqual(engine.get_default('regionId'), 'dd-eu') self.assertEqual(engine.get_default('ipv4'), 'auto') def test_parser(self): args = parse_args(['fittings.yaml', 'build', 'web']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'build') self.assertEqual(args.blueprints, ['web']) self.assertEqual(args.facilities, None) args = parse_args(['fittings.yaml', 'build', 'web', '-d']) self.assertEqual( logging.getLogger().getEffectiveLevel(), logging.DEBUG) args = parse_args(['fittings.yaml', 'build', 'web', '-q']) self.assertEqual( logging.getLogger().getEffectiveLevel(), logging.WARNING) args = parse_args(['fittings.yaml', 'start', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'start') self.assertEqual(args.blueprints, None) self.assertEqual(args.facilities, ['NA12']) args = parse_args([ 'fittings.yaml', 'rub', 'web', 'sql', '@NA9', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'rub') self.assertEqual(args.blueprints, ['web', 'sql']) self.assertEqual(args.facilities, ['NA9', 'NA12']) args = parse_args([ 'fittings.yaml', 'rub', 'web', '@NA9', 'sql', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'rub') self.assertEqual(args.blueprints, ['web', 'sql']) self.assertEqual(args.facilities, ['NA9', 'NA12']) args = parse_args(['fittings.yaml', 'polish']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'polish') self.assertEqual(args.blueprints, None) self.assertEqual(args.facilities, None) def test_main(self): engine = PlumberyEngine() engine.from_text(myPlan) engine.set_user_name('fake_name') engine.set_user_password('fake_password') with self.assertRaises(SystemExit): main(['bad args'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml', 'xyz123', 'web'], engine) with self.assertRaises(SystemExit): main(['-v'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml', 'build', 'web', '-v'], engine)
def main(args=[], engine=None): """ Runs plumbery from the command line Example:: $ python -m plumbery fittings.yaml build web In this example, plumbery loads fittings plan from ``fittings.yaml``, then it builds the blueprint named ``web``. If no blueprint is mentioned, then plumbery looks at all blueprint definitions in the fittings plan. In other terms, the following command builds the entire fittings plan, eventually across multiple facilities:: $ python -m plumbery fittings.yaml build Of course, plumbery can be invoked through the entire life cycle of your fittings:: $ python -m plumbery fittings.yaml build $ python -m plumbery fittings.yaml start $ python -m plumbery fittings.yaml polish ... nodes are up and running ... $ python -m plumbery fittings.yaml stop ... nodes have been stopped ... $ python -m plumbery fittings.yaml wipe ... nodes have been destroyed, but the infrastructure remains ... $ python -m plumbery fittings.yaml destroy ... every virtual resources has been removed ... To focus at a single location, put the character '@' followed by the id. For example, to build fittings only at 'NA12' you would type:: $ python -m plumbery fittings.yaml build @NA12 To apply a polisher just mention its name on the command line. For example, if fittings plan has a blueprint for nodes running Docker, then you may use following statements to bootstrap each node:: $ python -m plumbery fittings.yaml build docker $ python -m plumbery fittings.yaml start docker $ python -m plumbery fittings.yaml prepare docker ... Docker is up and running at multiple nodes ... If you create a new polisher and put it in the directory ``plumbery\polishers``, then it will become automatically available:: $ python -m plumbery fittings.yaml my_special_stuff To get some help, you can type:: $ python -m plumbery -h """ # part 1 - understand what the user wants try: args = parse_args(args) except Exception as feedback: logging.error("Incorrect arguments. " "Maybe the following can help: python -m plumbery -h") if logging.getLogger().getEffectiveLevel() == logging.DEBUG: raise else: logging.error("{}: {}".format( feedback.__class__.__name__, str(feedback))) sys.exit(2) # part 2 - acquire the toolbox if engine is None: try: engine = PlumberyEngine(args.fittings, args.parameters) if args.safe: engine.safeMode = True except Exception as feedback: if logging.getLogger().getEffectiveLevel() == logging.DEBUG: logging.error("Cannot read fittings plan from '{}'".format( args.fittings)) raise else: logging.error("Cannot read fittings plan from '{}'" ", run with -d for debug".format( args.fittings)) logging.error("{}: {}".format( feedback.__class__.__name__, str(feedback))) sys.exit(2) # part 3 - do the job try: engine.do(args.action, args.blueprints, args.facilities) logging.info(engine.document_elapsed()) except Exception as feedback: if logging.getLogger().getEffectiveLevel() == logging.DEBUG: logging.error("Unable to do '{}'".format(args.action)) raise else: logging.error("Unable to do '{}', run with -d for debug".format( args.action)) logging.error("{}: {}".format( feedback.__class__.__name__, str(feedback))) sys.exit(2)
def test_lifecycle(self): engine = PlumberyEngine() DimensionDataNodeDriver.connectionCls.conn_classes = ( None, DimensionDataMockHttp) DimensionDataMockHttp.type = None self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS) engine.set_shared_secret('fake_secret') engine.set_user_name('fake_name') engine.set_user_password('fake_password') engine.do('build') engine.build_all_blueprints() engine.do('build', 'myBlueprint') engine.build_blueprint('myBlueprint') engine.do('deploy') engine.do('deploy', 'myBlueprint') engine.do('destroy') engine.destroy_all_blueprints() engine.do('destroy', 'myBlueprint') engine.destroy_blueprint('myBlueprint') engine.do('dispose') engine.do('dispose', 'myBlueprint') engine.do('polish') engine.polish_all_blueprints() engine.do('polish', 'myBlueprint') engine.polish_blueprint('myBlueprint') engine.do('secrets') engine.do('start') engine.start_all_blueprints() engine.do('start', 'myBlueprint') engine.start_blueprint('myBlueprint') engine.do('stop') engine.stop_all_blueprints() engine.do('stop', 'myBlueprint') engine.stop_blueprint('myBlueprint') engine.do('wipe') engine.wipe_all_blueprints() engine.do('wipe', 'myBlueprint') engine.wipe_blueprint('myBlueprint') banner = engine.document_elapsed() self.assertEqual('Worked for you' in banner, True)
def main(args=[], engine=None): """ Runs plumbery from the command line Example:: $ python -m plumbery fittings.yaml build web In this example, plumbery loads fittings plan from ``fittings.yaml``, then it builds the blueprint named ``web``. If no blueprint is mentioned, then plumbery looks at all blueprint definitions in the fittings plan. In other terms, the following command builds the entire fittings plan, eventually across multiple facilities:: $ python -m plumbery fittings.yaml build Of course, plumbery can be invoked through the entire life cycle of your fittings:: $ python -m plumbery fittings.yaml build $ python -m plumbery fittings.yaml start $ python -m plumbery fittings.yaml polish ... nodes are up and running here ... $ python -m plumbery fittings.yaml stop $ python -m plumbery fittings.yaml destroy To focus at a single location, put the character '@' followed by the id. For example, to build fittings only at 'NA12' you would type:: $ python -m plumbery fittings.yaml build @NA12 To apply a polisher just mention its name on the command line. For example, if fittings plan has a blueprint for nodes running Docker, then you may use following statements to bootstrap each node:: $ python -m plumbery fittings.yaml build docker $ python -m plumbery fittings.yaml start docker $ python -m plumbery fittings.yaml rub docker ... Docker is up and running at multiple nodes ... If you create a new polisher and put it in the directory ``plumbery\polishers``, then it will become automatically available:: $ python -m plumbery fittings.yaml my_special_stuff To get some help, you can type:: $ python -m plumbery -h """ args = parse_args(args) if engine is None: try: engine = PlumberyEngine(args.fittings) except Exception as feedback: logging.info( "{}: error: cannot read fittings plan from '{}'".format( 'plumbery', args.fittings)) logging.debug(str(feedback)) sys.exit(2) try: engine.do(args.action, args.blueprints, args.facilities) except PlumberyException as feedback: logging.getLogger().setLevel(logging.INFO) print("{}: error: unrecognised action '{}'".format( 'plumbery', args.action)) sys.exit(2)
class TestPlumberyEngine(unittest.TestCase): def test_set(self): settings = { 'safeMode': False, 'polishers': [ {'ansible': {}}, {'spit': {}}, ] } self.engine = PlumberyEngine() self.engine.set_shared_secret('fake_secret') self.assertEqual(self.engine.get_shared_secret(), 'fake_secret') random = self.engine.get_random_secret() self.assertEqual(len(random), 9) self.assertEqual(self.engine.get_random_secret(), random) self.engine.set_user_name('fake_name') self.assertEqual(self.engine.get_user_name(), 'fake_name') self.engine.set_user_password('fake_password') self.assertEqual(self.engine.get_user_password(), 'fake_password') self.engine.set(settings) self.assertEqual(self.engine.safeMode, False) try: self.engine.from_text(myPlan) self.engine.add_facility(myFacility) self.assertEqual(len(self.engine.facilities), 2) except socket.gaierror: pass except InvalidCredsError: pass def test_lifecycle(self): self.engine = PlumberyEngine() self.engine.set_shared_secret('fake_secret') self.assertEqual(self.engine.get_shared_secret(), 'fake_secret') self.engine.set_user_name('fake_name') self.assertEqual(self.engine.get_user_name(), 'fake_name') self.engine.set_user_password('fake_password') self.assertEqual(self.engine.get_user_password(), 'fake_password') try: self.engine.do('build') self.engine.build_all_blueprints() self.engine.build_blueprint('myBlueprint') self.engine.do('start') self.engine.start_all_blueprints() self.engine.start_blueprint('myBlueprint') self.engine.do('polish') self.engine.polish_all_blueprints() self.engine.polish_blueprint('myBlueprint') self.engine.do('stop') self.engine.stop_all_blueprints() self.engine.stop_blueprint('myBlueprint') self.engine.wipe_all_blueprints() self.engine.wipe_blueprint('myBlueprint') self.engine.do('destroy') self.engine.destroy_all_blueprints() self.engine.destroy_blueprint('myBlueprint') except socket.gaierror: pass except InvalidCredsError: pass def test_lookup(self): self.engine = PlumberyEngine() self.assertEqual(self.engine.lookup('plumbery.version'), __version__) random = self.engine.lookup('random.secret') self.assertEqual(len(random), 9) self.assertEqual(self.engine.lookup('random.secret'), random) def test_parser(self): args = parse_args(['fittings.yaml', 'build', 'web']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'build') self.assertEqual(args.blueprints, ['web']) self.assertEqual(args.facilities, None) args = parse_args(['fittings.yaml', 'build', 'web', '-d']) self.assertEqual( logging.getLogger().getEffectiveLevel(), logging.DEBUG) args = parse_args(['fittings.yaml', 'build', 'web', '-q']) self.assertEqual( logging.getLogger().getEffectiveLevel(), logging.WARNING) args = parse_args(['fittings.yaml', 'start', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'start') self.assertEqual(args.blueprints, None) self.assertEqual(args.facilities, ['NA12']) args = parse_args([ 'fittings.yaml', 'rub', 'web', 'sql', '@NA9', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'rub') self.assertEqual(args.blueprints, ['web', 'sql']) self.assertEqual(args.facilities, ['NA9', 'NA12']) args = parse_args([ 'fittings.yaml', 'rub', 'web', '@NA9', 'sql', '@NA12']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'rub') self.assertEqual(args.blueprints, ['web', 'sql']) self.assertEqual(args.facilities, ['NA9', 'NA12']) args = parse_args(['fittings.yaml', 'polish']) self.assertEqual(args.fittings, 'fittings.yaml') self.assertEqual(args.action, 'polish') self.assertEqual(args.blueprints, None) self.assertEqual(args.facilities, None) def test_main(self): engine = PlumberyEngine() engine.from_text(myPlan) engine.set_user_name('fake_name') engine.set_user_password('fake_password') with self.assertRaises(SystemExit): main(['bad args'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml', 'xyz123', 'web'], engine) with self.assertRaises(SystemExit): main(['-v'], engine) with self.assertRaises(SystemExit): main(['fittings.yaml', 'build', 'web', '-v'], engine)