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 515dc7390c..a7d4fc8d24 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -137,10 +137,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 @@ -637,7 +639,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' @@ -1051,6 +1060,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 958438a997..b6f950c1be 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' @@ -789,6 +791,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}}