780 lines
30 KiB
Python
780 lines
30 KiB
Python
# Copyright 2015 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import copy
|
|
import json
|
|
import tempfile
|
|
import uuid
|
|
|
|
import jsonschema
|
|
import mock
|
|
from oslo_utils import uuidutils
|
|
|
|
from sahara import context
|
|
from sahara.db.templates import api as template_api
|
|
from sahara.db.templates import utils as u
|
|
from sahara.tests.unit.conductor import base
|
|
from sahara.tests.unit.db.templates import common as c
|
|
|
|
cluster_json = {
|
|
"plugin_name": "vanilla",
|
|
"hadoop_version": "2.6.0",
|
|
"node_groups": [
|
|
{
|
|
"name": "worker",
|
|
"count": 3,
|
|
"node_group_template_id": "{vanilla-260-default-worker}"
|
|
},
|
|
{
|
|
"name": "master",
|
|
"count": 1,
|
|
"node_group_template_id": "{vanilla-260-default-master}"
|
|
}
|
|
],
|
|
"name": "vanilla-260-default-cluster",
|
|
"neutron_management_network": "{neutron_management_network}",
|
|
"cluster_configs": {}
|
|
}
|
|
|
|
master_json = {
|
|
"plugin_name": "vanilla",
|
|
"hadoop_version": "2.6.0",
|
|
"node_processes": [
|
|
"namenode",
|
|
"resourcemanager",
|
|
"hiveserver"
|
|
],
|
|
"name": "vanilla-260-default-master",
|
|
"floating_ip_pool": "{floating_ip_pool}",
|
|
"flavor_id": "{flavor_id}"
|
|
}
|
|
|
|
worker_json = {
|
|
"plugin_name": "vanilla",
|
|
"hadoop_version": "2.6.0",
|
|
"node_processes": [
|
|
"nodemanager",
|
|
"datanode"
|
|
],
|
|
"name": "vanilla-260-default-worker",
|
|
"floating_ip_pool": "{floating_ip_pool}",
|
|
"flavor_id": "{flavor_id}"
|
|
}
|
|
|
|
|
|
class Config(c.Config):
|
|
def __init__(self, option_values={}):
|
|
if "name" not in option_values:
|
|
option_values["name"] = "update"
|
|
super(Config, self).__init__(option_values)
|
|
|
|
|
|
class TemplateUpdateTestCase(base.ConductorManagerTestCase):
|
|
def setUp(self):
|
|
super(TemplateUpdateTestCase, self).setUp()
|
|
self.logger = c.Logger()
|
|
template_api.set_logger(self.logger)
|
|
|
|
@mock.patch("sahara.utils.api_validator.ApiValidator.validate")
|
|
def test_check_cluster_templates_valid(self, validate):
|
|
self.logger.clear_log()
|
|
ng_templates = [{"template": c.SAMPLE_NGT,
|
|
"path": "/foo"}]
|
|
|
|
# Reference the node group template by name
|
|
clt = copy.copy(c.SAMPLE_CLT)
|
|
clt["node_groups"] = [
|
|
{"name": "test",
|
|
"count": 1,
|
|
"node_group_template_id": "{%s}" % c.SAMPLE_NGT["name"]}
|
|
]
|
|
cl_templates = [{"template": clt,
|
|
"path": "/bar"}]
|
|
|
|
# Test failed validation
|
|
validate.side_effect = jsonschema.ValidationError("mistake")
|
|
res = template_api.check_cluster_templates_valid(ng_templates,
|
|
cl_templates)
|
|
self.assertTrue(res)
|
|
msg = "Validation for /bar failed, mistake"
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
# Validation passes, name replaced
|
|
validate.side_effect = None
|
|
self.logger.clear_log()
|
|
res = template_api.check_cluster_templates_valid(ng_templates,
|
|
cl_templates)
|
|
self.assertFalse(res)
|
|
node_groups = validate.call_args[0][0]["node_groups"]
|
|
self.assertTrue(uuidutils.is_uuid_like(
|
|
node_groups[0]["node_group_template_id"]))
|
|
|
|
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)
|
|
|
|
@mock.patch("sahara.db.templates.api.add_config_section")
|
|
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_substitute_config_values_ngt(self):
|
|
ngt = copy.copy(c.SAMPLE_NGT)
|
|
ngt["flavor_id"] = "{flavor_id}"
|
|
ngt["floating_ip_pool"] = "{floating_ip_pool}"
|
|
configs = {"flavor_id": "2",
|
|
"floating_ip_pool": None}
|
|
template_api.substitute_config_values(configs, ngt, "/path")
|
|
self.assertEqual("2", ngt["flavor_id"])
|
|
self.assertNotIn("floating_ip_pool", ngt)
|
|
|
|
def test_substitute_config_values_clt(self):
|
|
clt = copy.copy(c.SAMPLE_CLT)
|
|
clt["neutron_management_network"] = "{neutron_management_network}"
|
|
clt["default_image_id"] = "{default_image_id}"
|
|
|
|
netid = str(uuid.uuid4())
|
|
configs = {"neutron_management_network": netid,
|
|
"default_image_id": None}
|
|
template_api.substitute_config_values(configs, clt, "/path")
|
|
self.assertEqual(netid, clt["neutron_management_network"])
|
|
self.assertNotIn("default_image_id", clt)
|
|
|
|
def _write_files(self, tempdir, templates):
|
|
files = []
|
|
for template in templates:
|
|
fp = tempfile.NamedTemporaryFile(suffix=".json",
|
|
dir=tempdir, delete=False)
|
|
json.dump(template, fp)
|
|
files.append(fp.name)
|
|
fp.close()
|
|
return files
|
|
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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}
|
|
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))
|
|
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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}
|
|
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))
|
|
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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_add_node_group_templates(self):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_NGT)
|
|
existing = self.api.node_group_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_NGT)
|
|
update["flavor_id"] = "6"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_NGT)
|
|
new["name"] = "new_name"
|
|
|
|
ngts = [{"template": update, "path": "foo"},
|
|
{"template": new, "path": "bar"}]
|
|
|
|
ng_info, error = template_api.add_node_group_templates(ctx, ngts)
|
|
self.assertFalse(error)
|
|
|
|
new = self.api.node_group_template_get_all(ctx, name=new["name"])[0]
|
|
self.assertIsNotNone(new)
|
|
|
|
# ng_info["created"] is a list of templates that were created
|
|
self.assertEqual(1, len(ng_info["created"]))
|
|
self.assertEqual(new["id"], ng_info["created"][0]["id"])
|
|
|
|
# ng_info["updated"] is a list of tuples for templates that
|
|
# were updated. First element in the tuple is the template,
|
|
# second is a dictionary of fields that were updated.
|
|
self.assertEqual(1, len(ng_info["updated"]))
|
|
self.assertEqual(existing["id"], ng_info["updated"][0][0]["id"])
|
|
self.assertEqual({"flavor_id": "42"}, ng_info["updated"][0][1])
|
|
|
|
# ng_info["dict"] is a dictionary of name/id pairs
|
|
self.assertEqual({new["name"]: new["id"],
|
|
existing["name"]: existing["id"]}, ng_info["ids"])
|
|
|
|
msg = ("Created node group template {info} from bar".format(
|
|
info=u.name_and_id(new)))
|
|
self.assertIn(msg, self.logger.infos)
|
|
|
|
msg = ("Updated node group template {info} from foo".format(
|
|
info=u.name_and_id(existing)))
|
|
self.assertIn(msg, self.logger.infos)
|
|
|
|
self.api.node_group_template_destroy(ctx, new["id"],
|
|
ignore_default=True)
|
|
self.api.node_group_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
@mock.patch("sahara.conductor.API.node_group_template_update")
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_creates")
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_updates")
|
|
def test_add_node_group_templates_update_failed(self,
|
|
reverse_updates,
|
|
reverse_creates,
|
|
ng_update):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
ng_update.side_effect = Exception("mistake")
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_NGT)
|
|
existing = self.api.node_group_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_NGT)
|
|
update["flavor_id"] = "6"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_NGT)
|
|
new["name"] = "new_name"
|
|
|
|
ngts = [{"template": new, "path": "bar"},
|
|
{"template": update, "path": "foo"}]
|
|
|
|
ng_info, error = template_api.add_node_group_templates(ctx, ngts)
|
|
new = self.api.node_group_template_get_all(ctx, name=new["name"])[0]
|
|
self.assertTrue(error)
|
|
self.assertEqual(1, reverse_creates.call_count)
|
|
|
|
# call should have been (ctx, [new])
|
|
self.assertEqual(new["id"], reverse_creates.call_args[0][1][0]["id"])
|
|
|
|
self.assertEqual(1, reverse_updates.call_count)
|
|
msg = ("Update of node group template {info} failed, mistake".format(
|
|
info=u.name_and_id(existing)))
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
self.api.node_group_template_destroy(ctx, new["id"],
|
|
ignore_default=True)
|
|
self.api.node_group_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
@mock.patch("sahara.conductor.API.node_group_template_create")
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_creates")
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_updates")
|
|
def test_add_node_group_templates_create_failed(self,
|
|
reverse_updates,
|
|
reverse_creates,
|
|
ng_create):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
ng_create.side_effect = Exception("mistake")
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_NGT)
|
|
existing = self.api.node_group_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_NGT)
|
|
update["flavor_id"] = "6"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_NGT)
|
|
new["name"] = "new_name"
|
|
|
|
ngts = [{"template": update, "path": "foo"},
|
|
{"template": new, "path": "bar"}]
|
|
|
|
ng_info, error = template_api.add_node_group_templates(ctx, ngts)
|
|
self.assertTrue(error)
|
|
self.assertEqual(1, reverse_creates.call_count)
|
|
self.assertEqual(1, reverse_updates.call_count)
|
|
|
|
# call should have been (ctx, [(existing, updated_fields)])
|
|
self.assertEqual({"flavor_id": existing["flavor_id"]},
|
|
reverse_updates.call_args[0][1][0][1])
|
|
|
|
msg = "Creation of node group template from bar failed, mistake"
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
self.api.node_group_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
def test_add_cluster_templates(self):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_CLT)
|
|
existing = self.api.cluster_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_CLT)
|
|
update["hadoop_version"] = "1"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_CLT)
|
|
new["name"] = "new_name"
|
|
|
|
clts = [{"template": update, "path": "foo"},
|
|
{"template": new, "path": "bar"}]
|
|
|
|
error = template_api.add_cluster_templates(ctx, clts, {})
|
|
self.assertFalse(error)
|
|
|
|
new = self.api.cluster_template_get_all(ctx, name=new["name"])[0]
|
|
self.assertIsNotNone(new)
|
|
|
|
msg = ("Created cluster template {info} from bar".format(
|
|
info=u.name_and_id(new)))
|
|
self.assertIn(msg, self.logger.infos)
|
|
|
|
msg = ("Updated cluster template {info} from foo".format(
|
|
info=u.name_and_id(existing)))
|
|
self.assertIn(msg, self.logger.infos)
|
|
|
|
self.api.cluster_template_destroy(ctx, new["id"],
|
|
ignore_default=True)
|
|
self.api.cluster_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
@mock.patch("sahara.conductor.API.cluster_template_update")
|
|
@mock.patch("sahara.db.templates.api.reverse_cluster_template_creates")
|
|
@mock.patch("sahara.db.templates.api.reverse_cluster_template_updates")
|
|
def test_add_cluster_templates_update_failed(self,
|
|
reverse_updates,
|
|
reverse_creates,
|
|
cl_update):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
cl_update.side_effect = Exception("mistake")
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_CLT)
|
|
existing = self.api.cluster_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_CLT)
|
|
update["hadoop_version"] = "1"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_CLT)
|
|
new["name"] = "new_name"
|
|
|
|
clts = [{"template": new, "path": "bar"},
|
|
{"template": update, "path": "foo"}]
|
|
|
|
error = template_api.add_cluster_templates(ctx, clts, {})
|
|
new = self.api.cluster_template_get_all(ctx, name=new["name"])[0]
|
|
self.assertTrue(error)
|
|
self.assertEqual(1, reverse_creates.call_count)
|
|
|
|
# call should have been (ctx, [new])
|
|
self.assertEqual(new["id"], reverse_creates.call_args[0][1][0]["id"])
|
|
|
|
self.assertEqual(1, reverse_updates.call_count)
|
|
msg = ("Update of cluster template {info} failed, mistake".format(
|
|
info=u.name_and_id(existing)))
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
self.api.cluster_template_destroy(ctx, new["id"],
|
|
ignore_default=True)
|
|
self.api.cluster_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
@mock.patch("sahara.conductor.API.cluster_template_create")
|
|
@mock.patch("sahara.db.templates.api.reverse_cluster_template_creates")
|
|
@mock.patch("sahara.db.templates.api.reverse_cluster_template_updates")
|
|
def test_add_cluster_templates_create_failed(self,
|
|
reverse_updates,
|
|
reverse_creates,
|
|
cl_create):
|
|
self.logger.clear_log()
|
|
ctx = context.ctx()
|
|
|
|
cl_create.side_effect = Exception("mistake")
|
|
|
|
# Create a record that will be updated in the db
|
|
existing = copy.copy(c.SAMPLE_CLT)
|
|
existing = self.api.cluster_template_create(ctx, existing)
|
|
|
|
# Create the update
|
|
update = copy.copy(c.SAMPLE_CLT)
|
|
update["hadoop_version"] = "1"
|
|
|
|
# Create a record that will be new in the db
|
|
new = copy.copy(c.SAMPLE_CLT)
|
|
new["name"] = "new_name"
|
|
|
|
clts = [{"template": update, "path": "foo"},
|
|
{"template": new, "path": "bar"}]
|
|
|
|
error = template_api.add_cluster_templates(ctx, clts, {})
|
|
self.assertTrue(error)
|
|
self.assertEqual(1, reverse_creates.call_count)
|
|
self.assertEqual(1, reverse_updates.call_count)
|
|
|
|
# call should have been (ctx, [(existing, updated_fields)])
|
|
# updated fields will contain hadoop_version and node_groups,
|
|
# since node_groups is modified by the conductor
|
|
updated_fields = reverse_updates.call_args[0][1][0][1]
|
|
self.assertEqual(updated_fields["hadoop_version"],
|
|
existing["hadoop_version"])
|
|
self.assertIn("node_groups", updated_fields)
|
|
|
|
msg = "Creation of cluster template from bar failed, mistake"
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
self.api.cluster_template_destroy(ctx, existing["id"],
|
|
ignore_default=True)
|
|
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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)
|
|
|
|
@mock.patch("sahara.db.templates.api.check_cluster_templates_valid")
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
def test_do_update_cluster_invalid(self, add_config,
|
|
get_configs, clt_valid):
|
|
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))
|
|
|
|
clt_valid.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}, "
|
|
"error processing cluster templates".format(dir=tempdir))
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
@mock.patch("sahara.db.templates.api.check_usage_of_existing")
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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)
|
|
|
|
@mock.patch("sahara.db.templates.api.add_node_group_templates")
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
def test_do_update_add_ngts_fails(self, add_config,
|
|
get_configs, add_ngts):
|
|
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_ngts.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}, "
|
|
"error processing node group templates".format(dir=tempdir))
|
|
self.assertIn(msg, self.logger.warnings)
|
|
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_creates")
|
|
@mock.patch("sahara.db.templates.api.reverse_node_group_template_updates")
|
|
@mock.patch("sahara.db.templates.api.add_cluster_templates")
|
|
@mock.patch("sahara.db.templates.api.get_configs")
|
|
@mock.patch("sahara.db.templates.api.add_config_section_for_template")
|
|
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": 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)
|