1
# Copyright 2014-2015 Canonical Limited.
3
# This file is part of charm-helpers.
5
# charm-helpers is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU Lesser General Public License version 3 as
7
# published by the Free Software Foundation.
9
# charm-helpers is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU Lesser General Public License for more details.
14
# You should have received a copy of the GNU Lesser General Public License
15
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
17
# Copyright 2013 Canonical Ltd.
20
# Charm Helpers Developers <juju@lists.ubuntu.com>
21
"""A helper to create a yaml cache of config with namespaced relation data."""
27
import charmhelpers.core.hookenv
30
charm_dir = os.environ.get('CHARM_DIR', '')
33
def dict_keys_without_hyphens(a_dict):
34
"""Return the a new dict with underscores instead of hyphens in keys."""
36
(key.replace('-', '_'), val) for key, val in a_dict.items())
39
def update_relations(context, namespace_separator=':'):
40
"""Update the context with the relation data."""
41
# Add any relation data prefixed with the relation type.
42
relation_type = charmhelpers.core.hookenv.relation_type()
44
context['current_relation'] = {}
45
if relation_type is not None:
46
relation_data = charmhelpers.core.hookenv.relation_get()
47
context['current_relation'] = relation_data
48
# Deprecated: the following use of relation data as keys
49
# directly in the context will be removed.
51
("{relation_type}{namespace_separator}{key}".format(
52
relation_type=relation_type,
54
namespace_separator=namespace_separator), val)
55
for key, val in relation_data.items())
56
relation_data = dict_keys_without_hyphens(relation_data)
57
context.update(relation_data)
58
relations = charmhelpers.core.hookenv.relations_of_type(relation_type)
59
relations = [dict_keys_without_hyphens(rel) for rel in relations]
61
context['relations_full'] = charmhelpers.core.hookenv.relations()
63
# the hookenv.relations() data structure is effectively unusable in
64
# templates and other contexts when trying to access relation data other
65
# than the current relation. So provide a more useful structure that works
67
local_unit = charmhelpers.core.hookenv.local_unit()
69
for rname, rids in context['relations_full'].items():
71
for rid, rdata in rids.items():
73
if local_unit in rdata:
75
for unit_name, rel_data in data.items():
76
new_data = {'__relid__': rid, '__unit__': unit_name}
77
new_data.update(rel_data)
78
relations[rname].append(new_data)
79
context['relations'] = relations
82
def juju_state_to_yaml(yaml_path, namespace_separator=':',
83
allow_hyphens_in_keys=True):
84
"""Update the juju config and state in a yaml file.
86
This includes any current relation-get data, and the charm
89
This function was created for the ansible and saltstack
90
support, as those libraries can use a yaml file to supply
91
context to templates, but it may be useful generally to
92
create and update an on-disk cache of all the config, including
93
previous relation data.
95
By default, hyphens are allowed in keys as this is supported
96
by yaml, but for tools like ansible, hyphens are not valid [1].
98
[1] http://www.ansibleworks.com/docs/playbooks_variables.html#what-makes-a-valid-variable-name
100
config = charmhelpers.core.hookenv.config()
102
# Add the charm_dir which we will need to refer to charm
103
# file resources etc.
104
config['charm_dir'] = charm_dir
105
config['local_unit'] = charmhelpers.core.hookenv.local_unit()
106
config['unit_private_address'] = charmhelpers.core.hookenv.unit_private_ip()
107
config['unit_public_address'] = charmhelpers.core.hookenv.unit_get(
111
# Don't use non-standard tags for unicode which will not
112
# work when salt uses yaml.load_safe.
113
yaml.add_representer(six.text_type,
114
lambda dumper, value: dumper.represent_scalar(
115
six.u('tag:yaml.org,2002:str'), value))
117
yaml_dir = os.path.dirname(yaml_path)
118
if not os.path.exists(yaml_dir):
119
os.makedirs(yaml_dir)
121
if os.path.exists(yaml_path):
122
with open(yaml_path, "r") as existing_vars_file:
123
existing_vars = yaml.load(existing_vars_file.read())
127
if not allow_hyphens_in_keys:
128
config = dict_keys_without_hyphens(config)
129
existing_vars.update(config)
131
update_relations(existing_vars, namespace_separator)
133
with open(yaml_path, "w+") as fp:
134
fp.write(yaml.dump(existing_vars, default_flow_style=False))