Add attribute schemas to `OS::Magnum::Cluster`

Add attribute schemas to allow use get_attr
for `OS::Magnum::Cluster`.
Partial-Bug: #1625757

Change-Id: I714f6dbc3a612425128f18ef3cea0336fdc47090
This commit is contained in:
ricolin 2017-04-11 15:51:39 +08:00
parent 4992593baa
commit 3c06d0a349
2 changed files with 208 additions and 36 deletions

View File

@ -15,6 +15,7 @@ import six
from heat.common import exception
from heat.common.i18n import _
from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
@ -35,6 +36,85 @@ class Cluster(resource.Resource):
entity = 'clusters'
ATTRIBUTES = (
API_ADDRESS_ATTR, STACK_ID_ATTR, COE_VERSION_ATTR,
MASTER_ADDRESSES_ATTR, STATUS_ATTR, MASTER_COUNT_ATTR,
NODE_ADDRESSES_ATTR, STATUS_REASON_ATTR, NODE_COUNT_ATTR,
NAME_ATTR, CONTAINER_VERSION_ATTR, DISCOVERY_URL_ATTR,
CLUSTER_TEMPLATE_ID_ATTR, KEYPAIR_ATTR, CREATE_TIMEOUT_ATTR
) = (
'api_address', 'stack_id', 'coe_version',
'master_addresses', 'status', 'master_count',
'node_addresses', 'status_reason', 'node_count',
'name', 'container_version', 'discovery_url',
'cluster_template_id', 'keypair', 'create_timeout'
)
attributes_schema = {
API_ADDRESS_ATTR: attributes.Schema(
_('The endpoint URL of COE API exposed to end-users.'),
type=attributes.Schema.STRING
),
STACK_ID_ATTR: attributes.Schema(
_('The reference UUID of orchestration stack for this '
'COE cluster.'),
type=attributes.Schema.STRING
),
COE_VERSION_ATTR: attributes.Schema(
_('Version info of chosen COE in cluster for helping client '
'in picking the right version of client.'),
type=attributes.Schema.STRING
),
MASTER_ADDRESSES_ATTR: attributes.Schema(
_('List of floating IP of all master nodes.'),
type=attributes.Schema.LIST
),
STATUS_ATTR: attributes.Schema(
_('The status for this COE cluster.'),
type=attributes.Schema.STRING
),
MASTER_COUNT_ATTR: attributes.Schema(
_('The number of servers that will serve as master for the '
'cluster.'),
type=attributes.Schema.INTEGER
),
NODE_ADDRESSES_ATTR: attributes.Schema(
_('List of floating IP of all servers that serve as node.'),
type=attributes.Schema.LIST
),
STATUS_REASON_ATTR: attributes.Schema(
_('The reason of cluster current status.'),
type=attributes.Schema.STRING
),
NODE_COUNT_ATTR: attributes.Schema(
_('The number of servers that will serve as node in the cluster.'),
type=attributes.Schema.INTEGER
),
NAME_ATTR: attributes.Schema(
_('Name of the resource.'),
type=attributes.Schema.STRING
),
CONTAINER_VERSION_ATTR: attributes.Schema(
_('Version info of constainer engine in the chosen COE in cluster '
'for helping client in picking the right version of client.'),
type=attributes.Schema.STRING
),
DISCOVERY_URL_ATTR: attributes.Schema(
_('The custom discovery url for node discovery.'),
type=attributes.Schema.STRING
),
CLUSTER_TEMPLATE_ID_ATTR: attributes.Schema(
_('The UUID of the cluster template.'),
type=attributes.Schema.STRING
),
KEYPAIR_ATTR: attributes.Schema(
_('The name of the keypair.'),
type=attributes.Schema.STRING
),
CREATE_TIMEOUT_ATTR: attributes.Schema(
_('The timeout for cluster creation in minutes.'),
type=attributes.Schema.INTEGER
)}
PROPERTIES = (
NAME, CLUSTER_TEMPLATE, KEYPAIR, NODE_COUNT, MASTER_COUNT,
DISCOVERY_URL, CREATE_TIMEOUT
@ -103,6 +183,12 @@ class Cluster(resource.Resource):
finder='get_cluster_template')
]
def _resolve_attribute(self, name):
if self.resource_id is None:
return
cluster = self.client().clusters.get(self.resource_id)
return getattr(cluster, name, None)
def handle_create(self):
args = dict(self.properties.items())

View File

@ -53,23 +53,65 @@ RESOURCE_TYPE = 'OS::Magnum::Cluster'
class TestMagnumCluster(common.HeatTestCase):
def setUp(self):
super(TestMagnumCluster, self).setUp()
self.resource_id = '12345'
self.fake_name = u'test_cluster'
self.fake_keypair = u'key'
self.fake_cluster_template = '123456'
self.fake_node_count = 5
self.fake_master_count = 1
self.fake_discovery_url = u'https://discovery.etcd.io'
self.fake_create_timeout = 15
self.fake_api_address = 'https://192.168.0.249:6443'
self.fake_coe_version = 'v1.5.2'
self.fake_master_addresses = ['192.168.0.2']
self.fake_status = 'bar'
self.fake_node_addresses = ['192.168.0.3', '192.168.0.4',
'192.168.0.5', '192.168.0.6',
'192.168.0.7']
self.fake_status_reason = 'foobar'
self.fake_stack_id = '22767a68-a7f2-45fe-bc08-335a83e2b919'
self.fake_container_version = '1.12.6'
resource._register_class(RESOURCE_TYPE, cluster.Cluster)
t = template_format.parse(magnum_template)
self.stack = utils.parse_stack(t)
resource_defns = self.stack.t.resource_definitions(self.stack)
self.min_rsrc_defn = resource_defns['test_cluster_min']
resource_defns = self.stack.t.resource_definitions(self.stack)
self.rsrc_defn = resource_defns['test_cluster']
self.rsrc_defn = resource_defns[self.fake_name]
self.client = mock.Mock()
self.patchobject(cluster.Cluster, 'client', return_value=self.client)
self.m_fct = self.patchobject(mc.MagnumClientPlugin,
'get_cluster_template')
self.m_fnk = self.patchobject(nova.NovaClientPlugin,
'get_keypair', return_value='key')
'get_keypair',
return_value=self.fake_keypair)
def _mock_get_client(self):
value = mock.MagicMock()
value.name = self.fake_name
value.cluster_template_id = self.fake_cluster_template
value.uuid = self.resource_id
value.coe_version = self.fake_coe_version
value.node_count = self.fake_node_count
value.master_count = self.fake_master_count
value.discovery_url = self.fake_discovery_url
value.create_timeout = self.fake_create_timeout
value.api_address = self.fake_api_address
value.master_addresses = self.fake_master_addresses
value.status = self.fake_status
value.node_addresses = self.fake_node_addresses
value.status_reason = self.fake_status_reason
value.stack_id = self.fake_stack_id
value.container_version = self.fake_container_version
value.keypair = self.fake_keypair
value.to_dict.return_value = value.__dict__
self.client.clusters.get.return_value = value
def _create_resource(self, name, snippet, stack, stat='CREATE_COMPLETE'):
self.resource_id = '12345'
self.m_fct.return_value = '123456'
self.m_fct.return_value = self.fake_cluster_template
value = mock.MagicMock(uuid=self.resource_id)
self.client.clusters.create.return_value = value
get_rv = mock.MagicMock(status=stat)
@ -81,37 +123,38 @@ class TestMagnumCluster(common.HeatTestCase):
b = self._create_resource('cluster', self.rsrc_defn, self.stack)
# validate the properties
self.assertEqual(
'test_cluster',
self.fake_name,
b.properties.get(cluster.Cluster.NAME))
self.assertEqual(
'123456',
self.fake_cluster_template,
b.properties.get(cluster.Cluster.CLUSTER_TEMPLATE))
self.assertEqual(
'key',
self.fake_keypair,
b.properties.get(cluster.Cluster.KEYPAIR))
self.assertEqual(
5,
self.fake_node_count,
b.properties.get(cluster.Cluster.NODE_COUNT))
self.assertEqual(
1,
self.fake_master_count,
b.properties.get(cluster.Cluster.MASTER_COUNT))
self.assertEqual(
'https://discovery.etcd.io',
self.fake_discovery_url,
b.properties.get(cluster.Cluster.DISCOVERY_URL))
self.assertEqual(
15,
self.fake_create_timeout,
b.properties.get(cluster.Cluster.CREATE_TIMEOUT))
scheduler.TaskRunner(b.create)()
self.assertEqual(self.resource_id, b.resource_id)
self.assertEqual((b.CREATE, b.COMPLETE), b.state)
self.client.clusters.create.assert_called_once_with(
name=u'test_cluster',
keypair=u'key',
cluster_template_id='123456',
node_count=5,
master_count=1,
discovery_url=u'https://discovery.etcd.io',
create_timeout=15)
name=self.fake_name,
keypair=self.fake_keypair,
cluster_template_id=self.fake_cluster_template,
node_count=self.fake_node_count,
master_count=self.fake_master_count,
discovery_url=self.fake_discovery_url,
create_timeout=self.fake_create_timeout
)
def test_cluster_create_with_default_value(self):
b = self._create_resource('cluster', self.min_rsrc_defn,
@ -121,7 +164,7 @@ class TestMagnumCluster(common.HeatTestCase):
None,
b.properties.get(cluster.Cluster.NAME))
self.assertEqual(
'123456',
self.fake_cluster_template,
b.properties.get(cluster.Cluster.CLUSTER_TEMPLATE))
self.assertEqual(
None,
@ -144,7 +187,7 @@ class TestMagnumCluster(common.HeatTestCase):
self.client.clusters.create.assert_called_once_with(
name=None,
keypair=None,
cluster_template_id='123456',
cluster_template_id=self.fake_cluster_template,
node_count=1,
master_count=1,
discovery_url=None,
@ -174,9 +217,9 @@ class TestMagnumCluster(common.HeatTestCase):
self.client.clusters.get.return_value = status
t = template_format.parse(magnum_template)
new_t = copy.deepcopy(t)
new_t['resources']['test_cluster']['properties']['node_count'] = 10
new_t['resources'][self.fake_name]['properties']['node_count'] = 10
rsrc_defns = template.Template(new_t).resource_definitions(self.stack)
new_bm = rsrc_defns['test_cluster']
new_bm = rsrc_defns[self.fake_name]
if update_status == 'UPDATE_COMPLETE':
scheduler.TaskRunner(b.update, new_bm)()
self.assertEqual((b.UPDATE, b.COMPLETE), b.state)
@ -208,19 +251,62 @@ class TestMagnumCluster(common.HeatTestCase):
def test_cluster_get_live_state(self):
b = self._create_resource('cluster', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
value = mock.MagicMock()
value.to_dict.return_value = {
'cluster_template': 123456,
'node_count': 5,
'master_count': 1,
'discovery_url': 'https://discovery.etcd.io',
'create_timeout': 15}
self.client.clusters.get.return_value = value
self._mock_get_client()
reality = b.get_live_state(b.properties)
self.assertEqual(
{'create_timeout': 15,
'discovery_url': 'https://discovery.etcd.io',
'master_count': 1,
'node_count': 5
},
reality)
{
cluster.Cluster.CREATE_TIMEOUT: self.fake_create_timeout,
cluster.Cluster.DISCOVERY_URL: self.fake_discovery_url,
cluster.Cluster.MASTER_COUNT: self.fake_master_count,
cluster.Cluster.NODE_COUNT: self.fake_node_count
}, reality)
def test_resolve_attributes(self):
b = self._create_resource('cluster', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
self._mock_get_client()
self.assertEqual(
self.fake_name,
b._resolve_attribute(cluster.Cluster.NAME_ATTR))
self.assertEqual(
self.fake_coe_version,
b._resolve_attribute(cluster.Cluster.COE_VERSION_ATTR))
self.assertEqual(
self.fake_stack_id,
b._resolve_attribute(cluster.Cluster.STACK_ID_ATTR))
self.assertEqual(
self.fake_api_address,
b._resolve_attribute(cluster.Cluster.API_ADDRESS_ATTR))
self.assertEqual(
self.fake_master_count,
b._resolve_attribute(cluster.Cluster.MASTER_COUNT_ATTR))
self.assertEqual(
self.fake_status,
b._resolve_attribute(cluster.Cluster.STATUS_ATTR))
self.assertEqual(
self.fake_master_addresses,
b._resolve_attribute(cluster.Cluster.MASTER_ADDRESSES_ATTR))
self.assertEqual(
self.fake_node_addresses,
b._resolve_attribute(cluster.Cluster.NODE_ADDRESSES_ATTR))
self.assertEqual(
self.fake_status_reason,
b._resolve_attribute(cluster.Cluster.STATUS_REASON_ATTR))
self.assertEqual(
self.fake_node_count,
b._resolve_attribute(cluster.Cluster.NODE_COUNT_ATTR))
self.assertEqual(
self.fake_container_version,
b._resolve_attribute(cluster.Cluster.CONTAINER_VERSION_ATTR))
self.assertEqual(
self.fake_discovery_url,
b._resolve_attribute(cluster.Cluster.DISCOVERY_URL_ATTR))
self.assertEqual(
self.fake_cluster_template,
b._resolve_attribute(cluster.Cluster.CLUSTER_TEMPLATE_ID_ATTR))
self.assertEqual(
self.fake_keypair,
b._resolve_attribute(cluster.Cluster.KEYPAIR_ATTR))
self.assertEqual(
self.fake_create_timeout,
b._resolve_attribute(cluster.Cluster.CREATE_TIMEOUT_ATTR))