Implement OS::Sahara::ClusterTemplate resource
Change-Id: I19ffbc19340342f716aa0c23e2b9dd4332503cdc Implements: blueprint sahara-as-heat-resource
This commit is contained in:
parent
11018c8cd2
commit
065ff9baf8
@ -170,7 +170,168 @@ class SaharaNodeGroupTemplate(resource.Resource):
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
|
||||
class SaharaClusterTemplate(resource.Resource):
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, PLUGIN_NAME, HADOOP_VERSION, DESCRIPTION,
|
||||
ANTI_AFFINITY, MANAGEMENT_NETWORK,
|
||||
CLUSTER_CONFIGS, NODE_GROUPS, IMAGE_ID,
|
||||
) = (
|
||||
'name', 'plugin_name', 'hadoop_version', 'description',
|
||||
'anti_affinity', 'neutron_management_network',
|
||||
'cluster_configs', 'node_groups', 'default_image_id',
|
||||
)
|
||||
|
||||
_NODE_GROUP_KEYS = (
|
||||
NG_NAME, COUNT, NG_TEMPLATE_ID,
|
||||
) = (
|
||||
'name', 'count', 'node_group_template_id',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("Name for the Sahara Cluster Template."),
|
||||
constraints=[
|
||||
constraints.Length(min=1, max=50),
|
||||
constraints.AllowedPattern(SAHARA_NAME_REGEX),
|
||||
],
|
||||
),
|
||||
DESCRIPTION: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Description of the Sahara Group Template.'),
|
||||
default="",
|
||||
),
|
||||
PLUGIN_NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Plugin name.'),
|
||||
required=True,
|
||||
),
|
||||
HADOOP_VERSION: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Version of Hadoop running on instances.'),
|
||||
required=True,
|
||||
),
|
||||
IMAGE_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("ID of the default image to use for the template."),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('glance.image')
|
||||
],
|
||||
),
|
||||
MANAGEMENT_NETWORK: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name or UUID of Neutron network.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('neutron.network')
|
||||
],
|
||||
),
|
||||
ANTI_AFFINITY: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_("List of processes to enable anti-affinity for."),
|
||||
schema=properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
),
|
||||
),
|
||||
CLUSTER_CONFIGS: properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
_('Cluster configs dictionary.'),
|
||||
),
|
||||
NODE_GROUPS: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('Node groups.'),
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
NG_NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of the Node group.'),
|
||||
required=True
|
||||
),
|
||||
COUNT: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
_("Number of instances in the Node group."),
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.Range(min=1)
|
||||
]
|
||||
),
|
||||
NG_TEMPLATE_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("ID of the Node Group Template."),
|
||||
required=True
|
||||
),
|
||||
}
|
||||
),
|
||||
|
||||
),
|
||||
}
|
||||
|
||||
default_client_name = 'sahara'
|
||||
|
||||
physical_resource_name_limit = 50
|
||||
|
||||
def _cluster_template_name(self):
|
||||
name = self.properties.get(self.NAME)
|
||||
if name:
|
||||
return name
|
||||
return self.physical_resource_name()
|
||||
|
||||
def handle_create(self):
|
||||
plugin_name = self.properties[self.PLUGIN_NAME]
|
||||
hadoop_version = self.properties[self.HADOOP_VERSION]
|
||||
description = self.properties.get(self.DESCRIPTION)
|
||||
image_id = self.properties.get(self.IMAGE_ID)
|
||||
net_id = self.properties.get(self.MANAGEMENT_NETWORK)
|
||||
if net_id:
|
||||
net_id = self.client_plugin('neutron').find_neutron_resource(
|
||||
self.properties, self.MANAGEMENT_NETWORK, 'network')
|
||||
anti_affinity = self.properties.get(self.ANTI_AFFINITY)
|
||||
cluster_configs = self.properties.get(self.CLUSTER_CONFIGS)
|
||||
node_groups = self.properties.get(self.NODE_GROUPS)
|
||||
cluster_template = self.client().cluster_templates.create(
|
||||
self._cluster_template_name(),
|
||||
plugin_name, hadoop_version,
|
||||
description=description,
|
||||
default_image_id=image_id,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
cluster_configs=cluster_configs,
|
||||
node_groups=node_groups
|
||||
)
|
||||
LOG.info(_("Cluster Template '%s' has been created"
|
||||
) % cluster_template.name)
|
||||
self.resource_id_set(cluster_template.id)
|
||||
return self.resource_id
|
||||
|
||||
def handle_delete(self):
|
||||
if not self.resource_id:
|
||||
return
|
||||
try:
|
||||
self.client().cluster_templates.delete(
|
||||
self.resource_id)
|
||||
except Exception as ex:
|
||||
self.client_plugin().ignore_not_found(ex)
|
||||
LOG.info(_("Cluster Template '%s' has been deleted."
|
||||
) % self._cluster_template_name())
|
||||
|
||||
def validate(self):
|
||||
res = super(SaharaClusterTemplate, self).validate()
|
||||
if res:
|
||||
return res
|
||||
# check if running on neutron and MANAGEMENT_NETWORK missing
|
||||
#NOTE(pshchelo): on nova-network with MANAGEMENT_NETWORK present
|
||||
# overall stack validation will fail due to neutron.network constraint,
|
||||
# although the message will be not really relevant.
|
||||
if (self.is_using_neutron() and
|
||||
not self.properties.get(self.MANAGEMENT_NETWORK)):
|
||||
msg = _("%s must be provided"
|
||||
) % self.MANAGEMENT_NETWORK
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Sahara::NodeGroupTemplate': SaharaNodeGroupTemplate,
|
||||
'OS::Sahara::ClusterTemplate': SaharaClusterTemplate,
|
||||
}
|
||||
|
@ -43,6 +43,19 @@ resources:
|
||||
- jobtracker
|
||||
"""
|
||||
|
||||
cluster_template = """
|
||||
heat_template_version: 2013-05-23
|
||||
description: Sahara Cluster Template
|
||||
resources:
|
||||
cluster-template:
|
||||
type: OS::Sahara::ClusterTemplate
|
||||
properties:
|
||||
name: test-cluster-template
|
||||
plugin_name: vanilla
|
||||
hadoop_version: 2.3.0
|
||||
neutron_management_network: some_network
|
||||
"""
|
||||
|
||||
|
||||
class FakeNodeGroupTemplate(object):
|
||||
def __init__(self):
|
||||
@ -50,6 +63,12 @@ class FakeNodeGroupTemplate(object):
|
||||
self.name = "test-cluster-template"
|
||||
|
||||
|
||||
class FakeClusterTemplate(object):
|
||||
def __init__(self):
|
||||
self.id = "some_ct_id"
|
||||
self.name = "node-group-template"
|
||||
|
||||
|
||||
class SaharaNodeGroupTemplateTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
super(SaharaNodeGroupTemplateTest, self).setUp()
|
||||
@ -131,3 +150,86 @@ class SaharaNodeGroupTemplateTest(HeatTestCase):
|
||||
ex = self.assertRaises(exception.StackValidationFailed, ngt.validate)
|
||||
self.assertEqual('floating_ip_pool must be provided.',
|
||||
six.text_type(ex))
|
||||
|
||||
|
||||
class SaharaClusterTemplateTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
super(SaharaClusterTemplateTest, self).setUp()
|
||||
self.patchobject(st.constraints.CustomConstraint, '_is_valid'
|
||||
).return_value = True
|
||||
self.patchobject(neutron.NeutronClientPlugin, '_create')
|
||||
self.patchobject(neutron.NeutronClientPlugin, 'find_neutron_resource'
|
||||
).return_value = 'some_network_id'
|
||||
sahara_mock = mock.MagicMock()
|
||||
self.ct_mgr = sahara_mock.cluster_templates
|
||||
self.patchobject(sahara.SaharaClientPlugin,
|
||||
'_create').return_value = sahara_mock
|
||||
self.fake_ct = FakeClusterTemplate()
|
||||
|
||||
self.t = template_format.parse(cluster_template)
|
||||
|
||||
def _init_ct(self, template):
|
||||
stack = utils.parse_stack(template)
|
||||
return stack['cluster-template']
|
||||
|
||||
def test_ct_resource_mapping(self):
|
||||
ct = self._init_ct(self.t)
|
||||
mapping = st.resource_mapping()
|
||||
self.assertEqual(st.SaharaClusterTemplate,
|
||||
mapping['OS::Sahara::ClusterTemplate'])
|
||||
self.assertIsInstance(ct,
|
||||
st.SaharaClusterTemplate)
|
||||
|
||||
def _create_ct(self, template):
|
||||
ct = self._init_ct(template)
|
||||
self.ct_mgr.create.return_value = self.fake_ct
|
||||
scheduler.TaskRunner(ct.create)()
|
||||
self.assertEqual((ct.CREATE, ct.COMPLETE), ct.state)
|
||||
self.assertEqual(self.fake_ct.id, ct.resource_id)
|
||||
return ct
|
||||
|
||||
def test_ct_create(self):
|
||||
self._create_ct(self.t)
|
||||
expected_args = ('test-cluster-template', 'vanilla',
|
||||
'2.3.0')
|
||||
expected_kwargs = {'description': '',
|
||||
'default_image_id': None,
|
||||
'net_id': 'some_network_id',
|
||||
'anti_affinity': None,
|
||||
'node_groups': None,
|
||||
'cluster_configs': None
|
||||
}
|
||||
self.ct_mgr.create.assert_called_once_with(*expected_args,
|
||||
**expected_kwargs)
|
||||
|
||||
def test_ct_delete(self):
|
||||
ct = self._create_ct(self.t)
|
||||
scheduler.TaskRunner(ct.delete)()
|
||||
self.ct_mgr.delete.assert_called_once_with(self.fake_ct.id)
|
||||
self.assertEqual((ct.DELETE, ct.COMPLETE), ct.state)
|
||||
|
||||
def test_ngt_delete_ignores_not_found(self):
|
||||
ct = self._create_ct(self.t)
|
||||
self.ct_mgr.delete.side_effect = sahara.sahara_base.APIException(
|
||||
error_code=404)
|
||||
scheduler.TaskRunner(ct.delete)()
|
||||
self.ct_mgr.delete.assert_called_once_with(self.fake_ct.id)
|
||||
|
||||
def test_ngt_delete_fails(self):
|
||||
ct = self._create_ct(self.t)
|
||||
self.ct_mgr.delete.side_effect = sahara.sahara_base.APIException()
|
||||
delete_task = scheduler.TaskRunner(ct.delete)
|
||||
ex = self.assertRaises(exception.ResourceFailure, delete_task)
|
||||
expected = "APIException: None"
|
||||
self.assertEqual(expected, six.text_type(ex))
|
||||
self.ct_mgr.delete.assert_called_once_with(self.fake_ct.id)
|
||||
|
||||
def test_ct_validate_no_network_on_neutron_fails(self):
|
||||
self.t['resources']['cluster-template']['properties'].pop(
|
||||
'neutron_management_network')
|
||||
ct = self._init_ct(self.t)
|
||||
self.patchobject(ct, 'is_using_neutron', return_value=True)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
ct.validate)
|
||||
self.assertEqual("neutron_management_network must be provided",
|
||||
six.text_type(ex))
|
||||
|
Loading…
Reference in New Issue
Block a user