def test_do_update_add_clts_fails(self, add_config, get_configs, add_clts, reverse_ng_updates, reverse_ng_creates): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', "neutron_management_network": str(uuid.uuid4()) } option_values = { "tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None } template_api.set_conf(Config(option_values)) add_clts.return_value = True template_api.do_update() self.assertEqual(1, reverse_ng_creates.call_count) self.assertEqual(1, reverse_ng_updates.call_count) clts = self.api.cluster_template_get_all(ctx) self.assertEqual([], clts) msg = ("Skipping processing for {dir}, " "error processing cluster templates".format(dir=tempdir)) self.assertIn(msg, self.logger.warnings)
def main(): # TODO(tmckay): Work on restricting the options # pulled in by imports which show up in the help. # If we find a nice way to do this the calls to # unregister_extra_cli_opt() can be removed CONF(project="sahara") # For some reason, this is necessary to clear cached values # and re-read configs. For instance, if this is not done # here the 'plugins' value will not reflect the value from # the config file on the command line CONF.reload_config_files() log.setup(CONF, "sahara") # If we have to enforce extra option checks, like one option # requires another, do it here extra_option_checks() # Since this may be scripted, record the command in the log # so a user can know exactly what was done LOG.info(_LI("Command: {command}").format(command=" ".join(sys.argv))) api.set_logger(LOG) api.set_conf(CONF) CONF.command.func() LOG.info(_LI("Finished {command}").format(command=CONF.command.name))
def test_do_update_trash(self, add_config, get_configs): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', "neutron_management_network": str(uuid.uuid4()), 'auto_security_group': True, 'security_groups': [], } option_values = { "tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None } template_api.set_conf(Config(option_values)) template_api.do_update() ngs = self.api.node_group_template_get_all(ctx) ng_names = sorted([ng["name"] for ng in ngs]) self.assertEqual(sorted([master_json["name"], worker_json["name"]]), ng_names) clts = self.api.cluster_template_get_all(ctx) clt_names = sorted([clt["name"] for clt in clts]) clts = self.api.cluster_template_get_all(ctx) self.assertEqual([cluster_json["name"]], clt_names)
def test_add_config_section(self): # conf here can't be a mock.Mock() because hasattr will # return true conf = Config() conf.register_group = mock.Mock() conf.register_opts = mock.Mock() template_api.set_conf(conf) opts = ["option"] # Named config section template_api.add_config_section("section", opts) conf.register_group.assert_called() config_group = conf.register_group.call_args[0][0] self.assertEqual("section", config_group.name) conf.register_opts.assert_called_with(opts, config_group) conf.register_group.reset_mock() conf.register_opts.reset_mock() # No config section, opts should be registered against # the default section template_api.add_config_section(None, opts) conf.register_group.assert_not_called() conf.register_opts.assert_called_with(opts)
def test_process_files_bad_json(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() files = self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', 'security_groups': [], 'auto_security_group': False } option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Invalid JSON should cause all files to be skipped fp = tempfile.NamedTemporaryFile(suffix=".json", dir=tempdir, delete=False) fp.write(b"not json") files += [fp.name] fp.close() ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(0, len(ng_templates)) self.assertEqual(0, len(cl_templates)) msg = ("Error processing {name}".format(name=files[-1])) self.assertTrue(self.logger.warnings[0].startswith(msg)) msg = ("Skipping processing for {dir}, " "error processing files".format(dir=tempdir)) self.assertEqual(msg, self.logger.warnings[1])
def main(): # TODO(tmckay): Work on restricting the options # pulled in by imports which show up in the help. # If we find a nice way to do this the calls to # unregister_extra_cli_opt() can be removed CONF(project='sahara') # For some reason, this is necessary to clear cached values # and re-read configs. For instance, if this is not done # here the 'plugins' value will not reflect the value from # the config file on the command line CONF.reload_config_files() log.setup(CONF, "sahara") # If we have to enforce extra option checks, like one option # requires another, do it here extra_option_checks() # Since this may be scripted, record the command in the log # so a user can know exactly what was done LOG.info("Command: {command}".format(command=' '.join(sys.argv))) api.set_logger(LOG) api.set_conf(CONF) CONF.command.func() LOG.info("Finished {command}".format(command=CONF.command.name))
def test_process_files_validation_error(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() files = self._write_files( tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', 'security_groups': [], 'auto_security_group': False } option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Bad JSON validation for ng should cause all files to be skipped bad_worker = copy.copy(worker_json) bad_worker["my_dog"] = ["fido"] new_file = self._write_files(tempdir, [bad_worker])[0] ng_templates, cl_templates = template_api.process_files( tempdir, files + [new_file]) self.assertEqual(0, len(ng_templates)) self.assertEqual(0, len(cl_templates)) msg = ("Validation for {path} failed, " "Additional properties are not allowed".format(path=new_file)) self.assertTrue(self.logger.warnings[0].startswith(msg))
def test_process_files_validation_error(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() files = self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', 'security_groups': [], 'auto_security_group': False } option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Bad JSON validation for ng should cause all files to be skipped bad_worker = copy.copy(worker_json) bad_worker["my_dog"] = ["fido"] new_file = self._write_files(tempdir, [bad_worker])[0] ng_templates, cl_templates = template_api.process_files( tempdir, files + [new_file]) self.assertEqual(0, len(ng_templates)) self.assertEqual(0, len(cl_templates)) msg = ("Validation for {path} failed, " "Additional properties are not allowed".format(path=new_file)) self.assertTrue(self.logger.warnings[0].startswith(msg))
def test_do_update_existing_fails(self, add_config, get_configs, check_existing): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": 2, "neutron_management_network": uuid.uuid4() } option_values = { "tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None } template_api.set_conf(Config(option_values)) check_existing.return_value = True template_api.do_update() ngs = self.api.node_group_template_get_all(ctx) self.assertEqual([], ngs) clts = self.api.cluster_template_get_all(ctx) self.assertEqual([], clts) msg = ("Skipping processing for {dir}, " "templates in use".format(dir=tempdir)) self.assertIn(msg, self.logger.warnings)
def test_add_config_section(self): # conf here can't be a mock.Mock() because hasattr will # return true conf = Config() conf.register_group = mock.Mock() conf.register_opts = mock.Mock() template_api.set_conf(conf) opts = ["option"] # Named config section template_api.add_config_section("section", opts) self.assertEqual(1, conf.register_group.call_count) config_group = conf.register_group.call_args[0][0] self.assertEqual("section", config_group.name) self.assertEqual([mock.call(opts, config_group)], conf.register_opts.call_args_list) conf.register_group.reset_mock() conf.register_opts.reset_mock() # No config section, opts should be registered against # the default section template_api.add_config_section(None, opts) conf.register_group.assert_not_called() conf.register_opts.assert_called_with(opts)
def test_do_update(self, add_config, get_configs): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = {"flavor_id": 2, "neutron_management_network": uuid.uuid4()} option_values = {"tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) template_api.do_update() ngs = self.api.node_group_template_get_all(ctx) ng_names = sorted([ng["name"] for ng in ngs]) self.assertEqual(sorted([master_json["name"], worker_json["name"]]), ng_names) clts = self.api.cluster_template_get_all(ctx) clt_names = sorted([clt["name"] for clt in clts]) clts = self.api.cluster_template_get_all(ctx) self.assertEqual([cluster_json["name"]], clt_names)
def test_do_update_existing_fails(self, add_config, get_configs, check_existing): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = { "flavor_id": '2', "neutron_management_network": str(uuid.uuid4()) } option_values = {"tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) check_existing.return_value = True template_api.do_update() ngs = self.api.node_group_template_get_all(ctx) self.assertEqual([], ngs) clts = self.api.cluster_template_get_all(ctx) self.assertEqual([], clts) msg = ("Skipping processing for {dir}, " "templates in use".format(dir=tempdir)) self.assertIn(msg, self.logger.warnings)
def test_do_update_add_clts_fails(self, add_config, get_configs, add_clts, reverse_ng_creates, reverse_ng_updates): self.logger.clear_log() ctx = context.ctx() tempdir = tempfile.mkdtemp() self._write_files(tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = {"flavor_id": 2, "neutron_management_network": uuid.uuid4()} option_values = {"tenant_id": ctx.tenant_id, "directory": tempdir, "norecurse": None, "plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) add_clts.return_value = True template_api.do_update() reverse_ng_creates.assert_called_once() reverse_ng_updates.assert_called_once() clts = self.api.cluster_template_get_all(ctx) self.assertEqual([], clts) msg = ("Skipping processing for {dir}, " "error processing cluster templates".format(dir=tempdir)) self.assertIn(msg, self.logger.warnings)
def test_process_files_bad_json(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() files = self._write_files( tempdir, [cluster_json, master_json, worker_json]) get_configs.return_value = {"flavor_id": 2} option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Invalid JSON should cause all files to be skipped fp = tempfile.NamedTemporaryFile(suffix=".json", dir=tempdir, delete=False) fp.write("not json") files += [fp.name] fp.close() ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(0, len(ng_templates)) self.assertEqual(0, len(cl_templates)) msg = ("Error processing {name}".format(name=files[-1])) self.assertTrue(self.logger.warnings[0].startswith(msg)) msg = ("Skipping processing for {dir}, " "error processing files".format(dir=tempdir)) self.assertEqual(msg, self.logger.warnings[1])
def test_node_group_template_delete_bad_id(self): self.logger.clear_log() option_values = {"tenant_id": 1, "id": "badid"} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete_by_id() msg = ("Deletion of node group template {id} failed, " "no such template".format(id=option_values["id"])) self.assertIn(msg, self.logger.warnings)
def test_node_group_template_delete_in_use(self): self.logger.clear_log() ctx = context.ctx() t = self.api.node_group_template_create(ctx, c.SAMPLE_NGT) # Make a cluster that references the node group template cluster_values = copy.deepcopy(c.SAMPLE_CLUSTER) cluster_values["node_groups"][0]["node_group_template_id"] = t["id"] cl = self.api.cluster_create(ctx, cluster_values) # Make a cluster template that references the node group template cluster_temp_values = copy.deepcopy(c.SAMPLE_CLT) cluster_temp_values["node_groups"] = cluster_values["node_groups"] clt = self.api.cluster_template_create(ctx, cluster_temp_values) # Set up the expected messages msgs = [ "Node group template {info} in use " "by clusters {clusters}".format(info=u.name_and_id(t), clusters=[cl["name"]]) ] msgs += [ "Node group template {info} in use " "by cluster templates {cluster_temps}".format( info=u.name_and_id(t), cluster_temps=[clt["name"]]) ] msgs += [ "Deletion of node group template {info} failed".format( info=u.name_and_id(t)) ] # Check delete by name option_values = { "tenant_id": t["tenant_id"], "template_name": t["name"] } template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete() for msg in msgs: self.assertIn(msg, self.logger.warnings) self.logger.clear_log() # Check again with delete by id option_values = {"tenant_id": t["tenant_id"], "id": t["id"]} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete_by_id() for msg in msgs: self.assertIn(msg, self.logger.warnings) self.logger.clear_log()
def test_cluster_template_delete_by_id(self): self.logger.clear_log() ctx = context.ctx() t = self.api.cluster_template_create(ctx, c.SAMPLE_CLT) option_values = {"tenant_id": t["tenant_id"], "id": t["id"]} template_api.set_conf(Config(option_values)) template_api.do_cluster_template_delete_by_id() msg = 'Deleted cluster template {info}'.format(info=u.name_and_id(t)) self.assertIn(msg, self.logger.infos) t = self.api.cluster_template_get(ctx, t["id"]) self.assertIsNone(t)
def test_node_group_template_delete_by_name(self): self.logger.clear_log() ctx = context.ctx() t = self.api.node_group_template_create(ctx, c.SAMPLE_NGT) option_values = {"tenant_id": t["tenant_id"], "template_name": t["name"]} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete() msg = 'Deleted node group template {info}'.format( info=u.name_and_id(t)) self.assertIn(msg, self.logger.infos) t = self.api.node_group_template_get(ctx, t["id"]) self.assertIsNone(t)
def test_cluster_template_delete_by_id_skipped(self): self.logger.clear_log() ctx = context.ctx() template_values = copy.copy(c.SAMPLE_CLT) template_values["is_default"] = False t = self.api.cluster_template_create(ctx, template_values) option_values = {"tenant_id": t["tenant_id"], "id": t["id"]} template_api.set_conf(Config(option_values)) template_api.do_cluster_template_delete_by_id() msg = ("Deletion of cluster template {info} skipped, " "not a default template".format(info=u.name_and_id(t))) self.assertIn(msg, self.logger.warnings) t = self.api.cluster_template_get(ctx, t["id"]) self.assertIsNotNone(t)
def test_cluster_template_delete_by_id(self): self.logger.clear_log() self.setup_context(tenant_id=None) ctx = context.ctx() t = self.api.cluster_template_create(ctx, c.SAMPLE_CLT) option_values = {"tenant_id": t["tenant_id"], "id": t["id"]} template_api.set_conf(Config(option_values)) template_api.do_cluster_template_delete_by_id() msg = 'Deleted cluster template {info}'.format( info=u.name_and_id(t)) self.assertIn(msg, self.logger.infos) t = self.api.cluster_template_get(ctx, t["id"]) self.assertIsNone(t)
def test_add_config_section_for_template(self, add_config_section): conf = mock.Mock() conf.list_all_sections = mock.Mock() template_api.set_conf(conf) # No config sections conf.list_all_sections.return_value = [] ngt = c.SAMPLE_NGT template_api.add_config_section_for_template(ngt) add_config_section.assert_called_with(None, template_api.all_template_opts) add_config_section.reset_mock() # Add config section matching plugin conf.list_all_sections.return_value += [ngt["plugin_name"]] template_api.add_config_section_for_template(ngt) add_config_section.assert_called_with(ngt["plugin_name"], template_api.all_template_opts) add_config_section.reset_mock() # Add config section matching plugin and version section = "{plugin_name}_{hadoop_version}".format(**ngt) conf.list_all_sections.return_value += [section] template_api.add_config_section_for_template(ngt) add_config_section.assert_called_with(section, template_api.all_template_opts) add_config_section.reset_mock() # Add config section matching plugin, version and name section = "{plugin_name}_{hadoop_version}_{name}".format(**ngt) conf.list_all_sections.return_value += [section] template_api.add_config_section_for_template(ngt) add_config_section.assert_called_with( section, template_api.node_group_template_opts) add_config_section.reset_mock() # Add config section matching name section = "{name}".format(**ngt) conf.list_all_sections.return_value += [section] template_api.add_config_section_for_template(ngt) add_config_section.assert_called_with( section, template_api.node_group_template_opts) add_config_section.reset_mock()
def test_node_group_template_delete_by_name_skipped(self): self.logger.clear_log() ctx = context.ctx() template_values = copy.copy(c.SAMPLE_NGT) template_values["is_default"] = False t = self.api.node_group_template_create(ctx, template_values) option_values = {"tenant_id": t["tenant_id"], "template_name": t["name"]} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete() msg = ("Deletion of node group template {name} failed, " "no such template".format(name=t["name"])) self.assertIn(msg, self.logger.warnings) t = self.api.node_group_template_get(ctx, t["id"]) self.assertIsNotNone(t)
def test_cluster_template_delete_by_name_skipped(self): self.logger.clear_log() ctx = context.ctx() template_values = copy.copy(c.SAMPLE_NGT) template_values["is_default"] = False t = self.api.cluster_template_create(ctx, template_values) option_values = {"tenant_id": t["tenant_id"], "template_name": t["name"]} template_api.set_conf(Config(option_values)) template_api.do_cluster_template_delete() msg = ("Deletion of cluster template {name} failed, " "no such template".format(name=t["name"])) self.assertIn(msg, self.logger.warnings) t = self.api.cluster_template_get(ctx, t["id"]) self.assertIsNotNone(t)
def test_node_group_template_delete_in_use(self): self.logger.clear_log() ctx = context.ctx() t = self.api.node_group_template_create(ctx, c.SAMPLE_NGT) # Make a cluster that references the node group template cluster_values = copy.deepcopy(c.SAMPLE_CLUSTER) cluster_values["node_groups"][0]["node_group_template_id"] = t["id"] cl = self.api.cluster_create(ctx, cluster_values) # Make a cluster template that references the node group template cluster_temp_values = copy.deepcopy(c.SAMPLE_CLT) cluster_temp_values["node_groups"] = cluster_values["node_groups"] clt = self.api.cluster_template_create(ctx, cluster_temp_values) # Set up the expected messages msgs = ["Node group template {info} in use " "by clusters {clusters}".format( info=u.name_and_id(t), clusters=[cl["name"]])] msgs += ["Node group template {info} in use " "by cluster templates {cluster_temps}".format( info=u.name_and_id(t), cluster_temps=[clt["name"]])] msgs += ["Deletion of node group template {info} failed".format( info=u.name_and_id(t))] # Check delete by name option_values = {"tenant_id": t["tenant_id"], "template_name": t["name"]} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete() for msg in msgs: self.assertIn(msg, self.logger.warnings) self.logger.clear_log() # Check again with delete by id option_values = {"tenant_id": t["tenant_id"], "id": t["id"]} template_api.set_conf(Config(option_values)) template_api.do_node_group_template_delete_by_id() for msg in msgs: self.assertIn(msg, self.logger.warnings) self.logger.clear_log()
def test_process_files(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() # This should be ignored by process files some_other_json = {"name": "fred", "description": "not a template"} files = self._write_files(tempdir, [cluster_json, master_json, worker_json, some_other_json]) get_configs.return_value = {"flavor_id": "2", "security_groups": [], "auto_security_group": False} option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Check that cluster and ng templates are read and returned ng_templates, cl_templates = template_api.process_files(tempdir, files) cl_temp_names = [f["template"]["name"] for f in cl_templates] ng_temp_names = [f["template"]["name"] for f in ng_templates] self.assertEqual([cluster_json["name"]], cl_temp_names) self.assertEqual([master_json["name"], worker_json["name"]], ng_temp_names) # Plugin name/version filtering applied option_values = {"plugin_name": "vanilla", "plugin_version": "2.7.1"} template_api.set_conf(Config(option_values)) ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(1, len(cl_templates)) self.assertEqual(2, len(ng_templates)) option_values = {"plugin_name": "hdp", "plugin_version": "2.7.1"} template_api.set_conf(Config(option_values)) ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(0, len(cl_templates)) self.assertEqual(0, len(ng_templates))
def test_process_files(self, add_config_section, get_configs): self.logger.clear_log() tempdir = tempfile.mkdtemp() # This should be ignored by process files some_other_json = {"name": "fred", "description": "not a template"} files = self._write_files( tempdir, [cluster_json, master_json, worker_json, some_other_json]) get_configs.return_value = { "flavor_id": '2', 'security_groups': [], 'auto_security_group': False } option_values = {"plugin_name": None, "plugin_version": None} template_api.set_conf(Config(option_values)) # Check that cluster and ng templates are read and returned ng_templates, cl_templates = template_api.process_files(tempdir, files) cl_temp_names = [f["template"]["name"] for f in cl_templates] ng_temp_names = [f["template"]["name"] for f in ng_templates] self.assertEqual([cluster_json["name"]], cl_temp_names) self.assertEqual([master_json["name"], worker_json["name"]], ng_temp_names) # Plugin name/version filtering applied option_values = {"plugin_name": "vanilla", "plugin_version": "2.6.0"} template_api.set_conf(Config(option_values)) ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(1, len(cl_templates)) self.assertEqual(2, len(ng_templates)) option_values = {"plugin_name": "vanilla", "plugin_version": "1.2.1"} template_api.set_conf(Config(option_values)) ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(0, len(cl_templates)) self.assertEqual(0, len(ng_templates)) option_values = {"plugin_name": "hdp", "plugin_version": "2.6.0"} template_api.set_conf(Config(option_values)) ng_templates, cl_templates = template_api.process_files(tempdir, files) self.assertEqual(0, len(cl_templates)) self.assertEqual(0, len(ng_templates))
def test_do_delete(self): self.logger.clear_log() ctx = context.ctx() # Make some plugins to delete ngt, clt = self._make_templates(ctx, "first", "plugin", "v1") # Make some more for the same plugin, different version ngt2, clt2 = self._make_templates(ctx, "second", "plugin", "v2") # Make another set for a different plugin, overlapping version safe_ngt, safe_clt = self._make_templates(ctx, "third", "plugin2", "v1") # Run a delete by plugin name/version for the first set option_values = {"tenant_id": ngt["tenant_id"], "plugin_name": [ngt["plugin_name"]], "plugin_version": [ngt["hadoop_version"]]} template_api.set_conf(Config(option_values)) # Should delete clt and then ngt, check for messages in order template_api.do_delete() msgs = ["Deleted cluster template {info}".format( info=u.name_and_id(clt))] msgs += ["Deleted node group template {info}".format( info=u.name_and_id(ngt))] self.assertEqual(msgs, self.logger.infos) self.assertIsNone(self.api.node_group_template_get(ctx, ngt["id"])) self.assertIsNone(self.api.cluster_template_get(ctx, clt["id"])) # Make sure the other templates are still there self.assertIsNotNone(self.api.node_group_template_get(ctx, ngt2["id"])) self.assertIsNotNone(self.api.cluster_template_get(ctx, clt2["id"])) self.assertIsNotNone(self.api.node_group_template_get(ctx, safe_ngt["id"])) self.assertIsNotNone(self.api.cluster_template_get(ctx, safe_clt["id"])) # Run delete again for the plugin but with no version specified self.logger.clear_log() option_values = {"tenant_id": ngt2["tenant_id"], "plugin_name": [ngt2["plugin_name"]], "plugin_version": None} template_api.set_conf(Config(option_values)) # Should delete clt2 and then ngt2, check for messages in order template_api.do_delete() msgs = ["Deleted cluster template {info}".format( info=u.name_and_id(clt2))] msgs += ["Deleted node group template {info}".format( info=u.name_and_id(ngt2))] self.assertEqual(msgs, self.logger.infos) self.assertIsNone(self.api.node_group_template_get(ctx, ngt2["id"])) self.assertIsNone(self.api.cluster_template_get(ctx, clt2["id"])) # Make sure the other templates are still there self.assertIsNotNone(self.api.node_group_template_get(ctx, safe_ngt["id"])) self.assertIsNotNone(self.api.cluster_template_get(ctx, safe_clt["id"]))