diff --git a/hooks/heat-plugin-subordinate-relation-changed b/hooks/heat-plugin-subordinate-relation-changed new file mode 120000 index 0000000..ab98840 --- /dev/null +++ b/hooks/heat-plugin-subordinate-relation-changed @@ -0,0 +1 @@ +heat_relations.py \ No newline at end of file diff --git a/hooks/heat-plugin-subordinate-relation-joined b/hooks/heat-plugin-subordinate-relation-joined new file mode 120000 index 0000000..ab98840 --- /dev/null +++ b/hooks/heat-plugin-subordinate-relation-joined @@ -0,0 +1 @@ +heat_relations.py \ No newline at end of file diff --git a/hooks/heat_context.py b/hooks/heat_context.py index 6b003da..fdb4de8 100644 --- a/hooks/heat_context.py +++ b/hooks/heat_context.py @@ -13,7 +13,13 @@ # limitations under the License. from charmhelpers.contrib.openstack import context -from charmhelpers.core.hookenv import config, leader_get +from charmhelpers.core.hookenv import ( + config, + relation_ids, + related_units, + relation_get, + leader_get, +) from charmhelpers.contrib.hahelpers.cluster import ( determine_apache_port, determine_api_port, @@ -99,6 +105,37 @@ class HeatApacheSSLContext(context.ApacheSSLContext): service_namespace = 'heat' +class HeatPluginContext(context.SubordinateConfigContext): + interfaces = 'heat-plugin-subordinate' + + def __init__(self): + super(HeatPluginContext, self).__init__( + interface='heat-plugin-subordinate', + service='heat', + config_file='/etc/heat/heat.conf') + + def __call__(self): + ctxt = super(HeatPluginContext, self).__call__() + defaults = { + 'plugin-dirs': { + 'templ_key': 'plugin_dirs', + 'value': '/usr/lib64/heat,/usr/lib/heat', + }, + } + for rid in relation_ids('heat-plugin-subordinate'): + for unit in related_units(rid): + rdata = relation_get(rid=rid, unit=unit) + for key in defaults.keys(): + remote_value = rdata.get(key) + ctxt_key = defaults[key]['templ_key'] + if remote_value: + ctxt[ctxt_key] = remote_value + else: + ctxt[ctxt_key] = defaults[key]['value'] + return ctxt + return ctxt + + class InstanceUserContext(context.OSContextGenerator): def __call__(self): diff --git a/hooks/heat_relations.py b/hooks/heat_relations.py index 9be770b..fc9bbca 100755 --- a/hooks/heat_relations.py +++ b/hooks/heat_relations.py @@ -429,6 +429,13 @@ def ha_changed(): identity_joined(rid=rid) +@hooks.hook('heat-plugin-subordinate-relation-joined', + 'heat-plugin-subordinate-relation-changed') +@restart_on_change(restart_map(), stopstart=True) +def heat_plugin_subordinate_relation_joined(relid=None): + CONFIGS.write_all() + + @hooks.hook('update-status') @harden() def update_status(): diff --git a/hooks/heat_utils.py b/hooks/heat_utils.py index b3b79c9..d791219 100644 --- a/hooks/heat_utils.py +++ b/hooks/heat_utils.py @@ -54,6 +54,7 @@ from heat_context import ( InstanceUserContext, HeatApacheSSLContext, HeatHAProxyContext, + HeatPluginContext, ) TEMPLATES = 'templates/' @@ -108,6 +109,7 @@ CONFIG_FILES = OrderedDict([ HeatHAProxyContext(), HeatSecurityContext(), InstanceUserContext(), + HeatPluginContext(), context.SyslogContext(), context.LogLevelContext(), context.WorkerConfigContext(), diff --git a/metadata.yaml b/metadata.yaml index b85206d..da8e7eb 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -28,6 +28,9 @@ requires: scope: container certificates: interface: tls-certificates + heat-plugin-subordinate: + interface: heat-plugin-subordinate + scope: container peers: cluster: interface: heat-ha diff --git a/templates/heat.conf b/templates/heat.conf index 43c3e30..e993c92 100644 --- a/templates/heat.conf +++ b/templates/heat.conf @@ -5,11 +5,20 @@ debug = {{ debug }} log_dir = /var/log/heat instance_user=ec2-user instance_driver=heat.engine.nova -plugin_dirs=/usr/lib64/heat,/usr/lib/heat +{% if plugin_dirs -%} +plugin_dirs = {{ plugin_dirs }} +{% else -%} +plugin_dirs = /usr/lib64/heat,/usr/lib/heat +{% endif -%} environment_dir=/etc/heat/environment.d deferred_auth_method=password host=heat auth_encryption_key={{ encryption_key }} +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} {% if database_host -%} # < Icehouse db config @@ -75,3 +84,12 @@ bind_port={{ api_cfn_listen_port }} {% else -%} bind_port=8000 {% endif %} + +{% if sections -%} +{% for section in sections if section != 'DEFAULT' -%} +[{{ section }}] +{% for key, value in sections[section] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/templates/icehouse/heat.conf b/templates/icehouse/heat.conf index 59380b8..af65654 100644 --- a/templates/icehouse/heat.conf +++ b/templates/icehouse/heat.conf @@ -7,12 +7,22 @@ log_dir = /var/log/heat # Not including instance_user at all results in 'ec2-user' being used instance_user={{ instance_user }} instance_driver=heat.engine.nova -plugin_dirs=/usr/lib64/heat,/usr/lib/heat +{% if plugin_dirs -%} +plugin_dirs = {{ plugin_dirs }} +{% else -%} +plugin_dirs = /usr/lib64/heat,/usr/lib/heat +{% endif -%} environment_dir=/etc/heat/environment.d deferred_auth_method=password host=heat auth_encryption_key={{ encryption_key }} num_engine_workers = {{ workers }} +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} + {% if user_config_flags -%} {% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} @@ -95,3 +105,12 @@ endpoint_type = internalURL endpoint_type = publicURL {%- endif %} + +{% if sections -%} +{% for section in sections if section != 'DEFAULT' -%} +[{{ section }}] +{% for key, value in sections[section] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/templates/kilo/heat.conf b/templates/kilo/heat.conf index c6ed283..5880943 100644 --- a/templates/kilo/heat.conf +++ b/templates/kilo/heat.conf @@ -5,7 +5,11 @@ verbose = {{ verbose }} log_dir = /var/log/heat instance_user = {{ instance_user }} instance_driver = heat.engine.nova +{% if plugin_dirs -%} +plugin_dirs = {{ plugin_dirs }} +{% else -%} plugin_dirs = /usr/lib64/heat,/usr/lib/heat +{% endif -%} environment_dir = /etc/heat/environment.d host = heat auth_encryption_key = {{ encryption_key }} @@ -14,6 +18,12 @@ stack_domain_admin = heat_domain_admin stack_domain_admin_password = {{ heat_domain_admin_passwd }} stack_user_domain_name = heat num_engine_workers = {{ workers }} +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} + {% if user_config_flags -%} {% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} @@ -66,3 +76,12 @@ workers = {{ workers }} [clients] endpoint_type = internalURL {%- endif %} + +{% if sections -%} +{% for section in sections if section != 'DEFAULT' -%} +[{{ section }}] +{% for key, value in sections[section] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/templates/liberty/heat.conf b/templates/liberty/heat.conf index 1a767de..f3b8152 100644 --- a/templates/liberty/heat.conf +++ b/templates/liberty/heat.conf @@ -5,7 +5,11 @@ verbose = {{ verbose }} log_dir = /var/log/heat instance_user = {{ instance_user }} instance_driver = heat.engine.nova +{% if plugin_dirs -%} +plugin_dirs = {{ plugin_dirs }} +{% else -%} plugin_dirs = /usr/lib64/heat,/usr/lib/heat +{% endif -%} environment_dir = /etc/heat/environment.d host = heat auth_encryption_key = {{ encryption_key }} @@ -14,6 +18,12 @@ stack_domain_admin = heat_domain_admin stack_domain_admin_password = {{ heat_domain_admin_passwd }} stack_user_domain_name = heat num_engine_workers = {{ workers }} +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} + {% if user_config_flags -%} {% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} @@ -81,3 +91,12 @@ endpoint_type = internalURL {%- endif %} {% include "section-oslo-middleware" %} + +{% if sections -%} +{% for section in sections if section != 'DEFAULT' -%} +[{{ section }}] +{% for key, value in sections[section] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/templates/mitaka/heat.conf b/templates/mitaka/heat.conf index 33e1432..6617acf 100644 --- a/templates/mitaka/heat.conf +++ b/templates/mitaka/heat.conf @@ -5,7 +5,11 @@ verbose = {{ verbose }} log_dir = /var/log/heat instance_user = {{ instance_user }} instance_driver = heat.engine.nova +{% if plugin_dirs -%} +plugin_dirs = {{ plugin_dirs }} +{% else -%} plugin_dirs = /usr/lib64/heat,/usr/lib/heat +{% endif -%} environment_dir = /etc/heat/environment.d host = heat auth_encryption_key = {{ encryption_key }} @@ -14,6 +18,12 @@ stack_domain_admin = heat_domain_admin stack_domain_admin_password = {{ heat_domain_admin_passwd }} stack_user_domain_name = heat num_engine_workers = {{ workers }} +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} + {% if user_config_flags -%} {% for key, value in user_config_flags.items() -%} {{ key }} = {{ value }} @@ -77,3 +87,12 @@ endpoint_type = publicURL {%- endif %} {% include "section-oslo-middleware" %} + +{% if sections -%} +{% for section in sections if section != 'DEFAULT' -%} +[{{ section }}] +{% for key, value in sections[section] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endfor -%} +{% endif %} diff --git a/unit_tests/test_heat_context.py b/unit_tests/test_heat_context.py index 7eef616..3b620da 100644 --- a/unit_tests/test_heat_context.py +++ b/unit_tests/test_heat_context.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import charmhelpers import heat_context +import json from mock import patch from test_utils import CharmTestCase @@ -21,6 +23,9 @@ TO_PATCH = [ 'generate_ec2_tokens', 'config', 'leader_get', + 'relation_get', + 'relation_ids', + 'related_units', ] @@ -66,3 +71,84 @@ class TestHeatContext(CharmTestCase): self.assertEqual( heat_context.HeatIdentityServiceContext()(), final_result) + + +class HeatPluginContextTest(CharmTestCase): + + def setUp(self): + super(HeatPluginContextTest, self).setUp(heat_context, TO_PATCH) + self.relation_get.side_effect = self.test_relation.get + + def tearDown(self): + super(HeatPluginContextTest, self).tearDown() + + def test_init(self): + heatp_ctxt = heat_context.HeatPluginContext() + self.assertEqual( + heatp_ctxt.interfaces, + ['heat-plugin-subordinate'] + ) + self.assertEqual(heatp_ctxt.services, ['heat']) + self.assertEqual( + heatp_ctxt.config_file, + '/etc/heat/heat.conf' + ) + + @patch.object(charmhelpers.contrib.openstack.context, 'log') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_get') + @patch.object(charmhelpers.contrib.openstack.context, 'related_units') + @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids') + def ctxt_check(self, rel_settings, expect, _rids, _runits, _rget, _log): + self.test_relation.set(rel_settings) + _runits.return_value = ['unit1'] + _rids.return_value = ['rid2'] + _rget.side_effect = self.test_relation.get + self.relation_ids.return_value = ['rid2'] + self.related_units.return_value = ['unit1'] + heatp_ctxt = heat_context.HeatPluginContext()() + self.assertEqual(heatp_ctxt, expect) + + def test_defaults(self): + self.ctxt_check( + {}, + { + 'plugin_dirs': '/usr/lib64/heat,/usr/lib/heat', + 'sections': {}, + } + ) + + def test_overrides(self): + self.ctxt_check( + {'plugin-dirs': '/usr/lib64/heat,/usr/lib/heat,/usr/local/lib'}, + { + 'plugin_dirs': '/usr/lib64/heat,/usr/lib/heat,/usr/local/lib', + 'sections': {}, + } + ) + + def test_subordinateconfig(self): + principle_config = { + "heat": { + "/etc/heat/heat.conf": { + "sections": { + 'DEFAULT': [ + ('heatboost', True) + ], + 'heatplugin': [ + ('superkey', 'supervalue') + ], + } + } + } + } + self.ctxt_check( + { + 'plugin-dirs': '/usr/lib64/heat,/usr/lib/heat,/usr/local/lib', + 'subordinate_configuration': json.dumps(principle_config) + }, + { + 'plugin_dirs': '/usr/lib64/heat,/usr/lib/heat,/usr/local/lib', + 'sections': {u'DEFAULT': [[u'heatboost', True]], + u'heatplugin': [[u'superkey', u'supervalue']]}, + } + )