From 64ddd071604bf4fbdeb97f8c8fa4e313dd611c3c Mon Sep 17 00:00:00 2001 From: James Slagle Date: Wed, 19 Apr 2017 15:14:29 -0400 Subject: [PATCH] Add os_collect_config attribute to server resources Add a new attribute, os_collect_config to the server resources. The attribute's value is a map representing the configuration for the os-collect-config agent on the actual servers. Adding this data as an attribute will allow it to be represented in a stack output and also queried directly in the templates. implements blueprint split-stack-default Change-Id: I8acfd67cd8138d587cc362184c84a08134bf3157 --- .../openstack/heat/deployed_server.py | 14 +++++++++++--- .../engine/resources/openstack/nova/server.py | 17 ++++++++++++++--- heat/engine/resources/server_base.py | 19 +++++++++++++++++++ .../openstack/heat/test_deployed_server.py | 15 +++++++++++++++ heat/tests/openstack/nova/test_server.py | 18 ++++++++++++++++++ 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/heat/engine/resources/openstack/heat/deployed_server.py b/heat/engine/resources/openstack/heat/deployed_server.py index c44c03538e..c984ba5a9c 100644 --- a/heat/engine/resources/openstack/heat/deployed_server.py +++ b/heat/engine/resources/openstack/heat/deployed_server.py @@ -19,6 +19,7 @@ from heat.engine import attributes from heat.engine import constraints from heat.engine import properties from heat.engine.resources import server_base +from heat.engine import support cfg.CONF.import_opt('default_software_config_transport', 'heat.common.config') cfg.CONF.import_opt('default_user_data_format', 'heat.common.config') @@ -81,16 +82,23 @@ class DeployedServer(server_base.BaseServer): } ATTRIBUTES = ( - NAME_ATTR + NAME_ATTR, OS_COLLECT_CONFIG ) = ( - 'name' + 'name', 'os_collect_config' ) attributes_schema = { NAME_ATTR: attributes.Schema( _('Name of the server.'), type=attributes.Schema.STRING - ) + ), + OS_COLLECT_CONFIG: attributes.Schema( + _('The os-collect-config configuration for the server''s local ' + 'agent to be configured to connect to Heat to retrieve ' + 'deployment data.'), + type=attributes.Schema.MAP, + support_status=support.SupportStatus(version='9.0.0') + ), } def __init__(self, name, json_snippet, stack): diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index 6a44d13c6b..9d4f307146 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -129,10 +129,12 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, ATTRIBUTES = ( NAME_ATTR, ADDRESSES, NETWORKS_ATTR, FIRST_ADDRESS, - INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS, TAGS_ATTR + INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS, TAGS_ATTR, + OS_COLLECT_CONFIG ) = ( 'name', 'addresses', 'networks', 'first_address', - 'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls', 'tags' + 'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls', 'tags', + 'os_collect_config' ) # valid image Status @@ -612,7 +614,14 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, _('Tags from the server. Supported since client version 2.26.'), support_status=support.SupportStatus(version='8.0.0'), type=attributes.Schema.LIST - ) + ), + OS_COLLECT_CONFIG: attributes.Schema( + _('The os-collect-config configuration for the server''s local ' + 'agent to be configured to connect to Heat to retrieve ' + 'deployment data.'), + support_status=support.SupportStatus(version='9.0.0'), + type=attributes.Schema.MAP + ), } default_client_name = 'nova' @@ -1038,6 +1047,8 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, if name == self.FIRST_ADDRESS: return self.client_plugin().server_to_ipaddress( self.resource_id) or '' + if name == self.OS_COLLECT_CONFIG: + return self.metadata_get().get('os-collect-config', {}) if name == self.NAME_ATTR: return self._server_name() try: diff --git a/heat/engine/resources/server_base.py b/heat/engine/resources/server_base.py index c24d9287d5..d87a76b268 100644 --- a/heat/engine/resources/server_base.py +++ b/heat/engine/resources/server_base.py @@ -18,6 +18,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils from heat.common import exception +from heat.engine import attributes from heat.engine.clients import progress from heat.engine.resources import stack_user @@ -129,6 +130,8 @@ class BaseServer(stack_user.StackUser): self.client('swift').put_object( container, object_name, jsonutils.dumps(meta)) + self.attributes.reset_resolved_values() + def _create_transport_credentials(self, props): if self.transport_poll_server_cfn(props): self._create_user() @@ -184,6 +187,8 @@ class BaseServer(stack_user.StackUser): return if name == self.NAME_ATTR: return self._server_name() + if name == self.OS_COLLECT_CONFIG: + return self.metadata_get().get('os-collect-config', {}) def handle_update(self, json_snippet, tmpl_diff, prop_diff): if tmpl_diff.metadata_changed(): @@ -307,3 +312,17 @@ class BaseServer(stack_user.StackUser): def check_delete_complete(self, prg): if not prg: return True + + def _show_resource(self): + rsrc_dict = super(BaseServer, self)._show_resource() + rsrc_dict.setdefault( + self.OS_COLLECT_CONFIG, + self.metadata_get().get('os-collect-config', {})) + return rsrc_dict + + def get_attribute(self, key, *path): + if key == self.OS_COLLECT_CONFIG: + occ = self.metadata_get().get('os-collect-config', {}) + return attributes.select_from_attribute(occ, path) + else: + return super(BaseServer, self).get_attribute(key, *path) diff --git a/heat/tests/openstack/heat/test_deployed_server.py b/heat/tests/openstack/heat/test_deployed_server.py index 9ce055b09c..5e236c7e9b 100644 --- a/heat/tests/openstack/heat/test_deployed_server.py +++ b/heat/tests/openstack/heat/test_deployed_server.py @@ -349,3 +349,18 @@ class DeployedServersTest(common.HeatTestCase): }, 'deployments': [] }, server.metadata_get()) + + def test_resolve_attribute_os_collect_config(self): + metadata_url, server = ( + self._server_create_software_config_poll_temp_url()) + + # FnGetAtt usage belows requires the resource to have a stack set + (tmpl, stack) = self._setup_test_stack('stack_name') + server.stack = stack + + self.assertEqual({ + 'request': { + 'metadata_url': metadata_url + }, + 'collectors': ['request', 'local'] + }, server.FnGetAtt('os_collect_config')) diff --git a/heat/tests/openstack/nova/test_server.py b/heat/tests/openstack/nova/test_server.py index 992925f50a..6eea2b4d2b 100644 --- a/heat/tests/openstack/nova/test_server.py +++ b/heat/tests/openstack/nova/test_server.py @@ -394,6 +394,7 @@ class ServersTest(common.HeatTestCase): def test_server_create(self): return_server = self.fc.servers.list()[1] return_server.id = '5678' + return_server._info['os_collect_config'] = {} server_name = 'test_server_create' stack_name = '%s_s' % server_name server = self._create_test_server(return_server, server_name) @@ -444,6 +445,7 @@ class ServersTest(common.HeatTestCase): if server.attributes._resolved_values.get('tags'): del server.attributes._resolved_values['tags'] self.assertIsNone(server.FnGetAtt('tags')) + self.assertEqual({}, server.FnGetAtt('os_collect_config')) def test_server_create_metadata(self): stack_name = 'create_metadata_test_stack' @@ -764,6 +766,22 @@ class ServersTest(common.HeatTestCase): 'deployments': [] }, server.metadata_get()) + @mock.patch.object(heat_plugin.HeatClientPlugin, 'url_for') + def test_resolve_attribute_os_collect_config(self, fake_url): + fake_url.return_value = 'the-cfn-url' + server = self._server_create_software_config() + + self.assertEqual({ + 'cfn': { + 'access_key_id': '4567', + 'metadata_url': 'the-cfn-url/v1/', + 'path': 'WebServer.Metadata', + 'secret_access_key': '8901', + 'stack_name': 'software_config_s' + }, + 'collectors': ['ec2', 'cfn', 'local'] + }, server.FnGetAtt('os_collect_config')) + @mock.patch.object(heat_plugin.HeatClientPlugin, 'url_for') def test_server_create_software_config_metadata(self, fake_url): md = {'os-collect-config': {'polling_interval': 10}}