~chris-gondolin/charms/trusty/keystone/ldap-ca-cert

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/peerstorage/__init__.py

  • Committer: billy.olsen at canonical
  • Date: 2015-08-31 17:35:57 UTC
  • mfrom: (170.1.39 stable.remote)
  • Revision ID: billy.olsen@canonical.com-20150831173557-0r0ftkapbitq0s20
[ack,r=billy-olsen,1chb1n,tealeg,adam-collard] Add pause/resume actions to keystone.

This changes introduces the pause and resume action set to the keystone charm. These
actions can be used to pause keystone services on a unit for maintenance activities.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2014-2015 Canonical Limited.
2
 
#
3
 
# This file is part of charm-helpers.
4
 
#
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.
8
 
#
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.
13
 
#
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/>.
16
 
 
17
 
import json
18
 
import six
19
 
 
20
 
from charmhelpers.core.hookenv import relation_id as current_relation_id
21
 
from charmhelpers.core.hookenv import (
22
 
    is_relation_made,
23
 
    relation_ids,
24
 
    relation_get as _relation_get,
25
 
    local_unit,
26
 
    relation_set as _relation_set,
27
 
    leader_get as _leader_get,
28
 
    leader_set,
29
 
    is_leader,
30
 
)
31
 
 
32
 
 
33
 
"""
34
 
This helper provides functions to support use of a peer relation
35
 
for basic key/value storage, with the added benefit that all storage
36
 
can be replicated across peer units.
37
 
 
38
 
Requirement to use:
39
 
 
40
 
To use this, the "peer_echo()" method has to be called form the peer
41
 
relation's relation-changed hook:
42
 
 
43
 
@hooks.hook("cluster-relation-changed") # Adapt the to your peer relation name
44
 
def cluster_relation_changed():
45
 
    peer_echo()
46
 
 
47
 
Once this is done, you can use peer storage from anywhere:
48
 
 
49
 
@hooks.hook("some-hook")
50
 
def some_hook():
51
 
    # You can store and retrieve key/values this way:
52
 
    if is_relation_made("cluster"):  # from charmhelpers.core.hookenv
53
 
        # There are peers available so we can work with peer storage
54
 
        peer_store("mykey", "myvalue")
55
 
        value = peer_retrieve("mykey")
56
 
        print value
57
 
    else:
58
 
        print "No peers joind the relation, cannot share key/values :("
59
 
"""
60
 
 
61
 
 
62
 
def leader_get(attribute=None, rid=None):
63
 
    """Wrapper to ensure that settings are migrated from the peer relation.
64
 
 
65
 
    This is to support upgrading an environment that does not support
66
 
    Juju leadership election to one that does.
67
 
 
68
 
    If a setting is not extant in the leader-get but is on the relation-get
69
 
    peer rel, it is migrated and marked as such so that it is not re-migrated.
70
 
    """
71
 
    migration_key = '__leader_get_migrated_settings__'
72
 
    if not is_leader():
73
 
        return _leader_get(attribute=attribute)
74
 
 
75
 
    settings_migrated = False
76
 
    leader_settings = _leader_get(attribute=attribute)
77
 
    previously_migrated = _leader_get(attribute=migration_key)
78
 
 
79
 
    if previously_migrated:
80
 
        migrated = set(json.loads(previously_migrated))
81
 
    else:
82
 
        migrated = set([])
83
 
 
84
 
    try:
85
 
        if migration_key in leader_settings:
86
 
            del leader_settings[migration_key]
87
 
    except TypeError:
88
 
        pass
89
 
 
90
 
    if attribute:
91
 
        if attribute in migrated:
92
 
            return leader_settings
93
 
 
94
 
        # If attribute not present in leader db, check if this unit has set
95
 
        # the attribute in the peer relation
96
 
        if not leader_settings:
97
 
            peer_setting = _relation_get(attribute=attribute, unit=local_unit(),
98
 
                                         rid=rid)
99
 
            if peer_setting:
100
 
                leader_set(settings={attribute: peer_setting})
101
 
                leader_settings = peer_setting
102
 
 
103
 
        if leader_settings:
104
 
            settings_migrated = True
105
 
            migrated.add(attribute)
106
 
    else:
107
 
        r_settings = _relation_get(unit=local_unit(), rid=rid)
108
 
        if r_settings:
109
 
            for key in set(r_settings.keys()).difference(migrated):
110
 
                # Leader setting wins
111
 
                if not leader_settings.get(key):
112
 
                    leader_settings[key] = r_settings[key]
113
 
 
114
 
                settings_migrated = True
115
 
                migrated.add(key)
116
 
 
117
 
            if settings_migrated:
118
 
                leader_set(**leader_settings)
119
 
 
120
 
    if migrated and settings_migrated:
121
 
        migrated = json.dumps(list(migrated))
122
 
        leader_set(settings={migration_key: migrated})
123
 
 
124
 
    return leader_settings
125
 
 
126
 
 
127
 
def relation_set(relation_id=None, relation_settings=None, **kwargs):
128
 
    """Attempt to use leader-set if supported in the current version of Juju,
129
 
    otherwise falls back on relation-set.
130
 
 
131
 
    Note that we only attempt to use leader-set if the provided relation_id is
132
 
    a peer relation id or no relation id is provided (in which case we assume
133
 
    we are within the peer relation context).
134
 
    """
135
 
    try:
136
 
        if relation_id in relation_ids('cluster'):
137
 
            return leader_set(settings=relation_settings, **kwargs)
138
 
        else:
139
 
            raise NotImplementedError
140
 
    except NotImplementedError:
141
 
        return _relation_set(relation_id=relation_id,
142
 
                             relation_settings=relation_settings, **kwargs)
143
 
 
144
 
 
145
 
def relation_get(attribute=None, unit=None, rid=None):
146
 
    """Attempt to use leader-get if supported in the current version of Juju,
147
 
    otherwise falls back on relation-get.
148
 
 
149
 
    Note that we only attempt to use leader-get if the provided rid is a peer
150
 
    relation id or no relation id is provided (in which case we assume we are
151
 
    within the peer relation context).
152
 
    """
153
 
    try:
154
 
        if rid in relation_ids('cluster'):
155
 
            return leader_get(attribute, rid)
156
 
        else:
157
 
            raise NotImplementedError
158
 
    except NotImplementedError:
159
 
        return _relation_get(attribute=attribute, rid=rid, unit=unit)
160
 
 
161
 
 
162
 
def peer_retrieve(key, relation_name='cluster'):
163
 
    """Retrieve a named key from peer relation `relation_name`."""
164
 
    cluster_rels = relation_ids(relation_name)
165
 
    if len(cluster_rels) > 0:
166
 
        cluster_rid = cluster_rels[0]
167
 
        return relation_get(attribute=key, rid=cluster_rid,
168
 
                            unit=local_unit())
169
 
    else:
170
 
        raise ValueError('Unable to detect'
171
 
                         'peer relation {}'.format(relation_name))
172
 
 
173
 
 
174
 
def peer_retrieve_by_prefix(prefix, relation_name='cluster', delimiter='_',
175
 
                            inc_list=None, exc_list=None):
176
 
    """ Retrieve k/v pairs given a prefix and filter using {inc,exc}_list """
177
 
    inc_list = inc_list if inc_list else []
178
 
    exc_list = exc_list if exc_list else []
179
 
    peerdb_settings = peer_retrieve('-', relation_name=relation_name)
180
 
    matched = {}
181
 
    if peerdb_settings is None:
182
 
        return matched
183
 
    for k, v in peerdb_settings.items():
184
 
        full_prefix = prefix + delimiter
185
 
        if k.startswith(full_prefix):
186
 
            new_key = k.replace(full_prefix, '')
187
 
            if new_key in exc_list:
188
 
                continue
189
 
            if new_key in inc_list or len(inc_list) == 0:
190
 
                matched[new_key] = v
191
 
    return matched
192
 
 
193
 
 
194
 
def peer_store(key, value, relation_name='cluster'):
195
 
    """Store the key/value pair on the named peer relation `relation_name`."""
196
 
    cluster_rels = relation_ids(relation_name)
197
 
    if len(cluster_rels) > 0:
198
 
        cluster_rid = cluster_rels[0]
199
 
        relation_set(relation_id=cluster_rid,
200
 
                     relation_settings={key: value})
201
 
    else:
202
 
        raise ValueError('Unable to detect '
203
 
                         'peer relation {}'.format(relation_name))
204
 
 
205
 
 
206
 
def peer_echo(includes=None, force=False):
207
 
    """Echo filtered attributes back onto the same relation for storage.
208
 
 
209
 
    This is a requirement to use the peerstorage module - it needs to be called
210
 
    from the peer relation's changed hook.
211
 
 
212
 
    If Juju leader support exists this will be a noop unless force is True.
213
 
    """
214
 
    try:
215
 
        is_leader()
216
 
    except NotImplementedError:
217
 
        pass
218
 
    else:
219
 
        if not force:
220
 
            return  # NOOP if leader-election is supported
221
 
 
222
 
    # Use original non-leader calls
223
 
    relation_get = _relation_get
224
 
    relation_set = _relation_set
225
 
 
226
 
    rdata = relation_get()
227
 
    echo_data = {}
228
 
    if includes is None:
229
 
        echo_data = rdata.copy()
230
 
        for ex in ['private-address', 'public-address']:
231
 
            if ex in echo_data:
232
 
                echo_data.pop(ex)
233
 
    else:
234
 
        for attribute, value in six.iteritems(rdata):
235
 
            for include in includes:
236
 
                if include in attribute:
237
 
                    echo_data[attribute] = value
238
 
    if len(echo_data) > 0:
239
 
        relation_set(relation_settings=echo_data)
240
 
 
241
 
 
242
 
def peer_store_and_set(relation_id=None, peer_relation_name='cluster',
243
 
                       peer_store_fatal=False, relation_settings=None,
244
 
                       delimiter='_', **kwargs):
245
 
    """Store passed-in arguments both in argument relation and in peer storage.
246
 
 
247
 
    It functions like doing relation_set() and peer_store() at the same time,
248
 
    with the same data.
249
 
 
250
 
    @param relation_id: the id of the relation to store the data on. Defaults
251
 
                        to the current relation.
252
 
    @param peer_store_fatal: Set to True, the function will raise an exception
253
 
                             should the peer sotrage not be avialable."""
254
 
 
255
 
    relation_settings = relation_settings if relation_settings else {}
256
 
    relation_set(relation_id=relation_id,
257
 
                 relation_settings=relation_settings,
258
 
                 **kwargs)
259
 
    if is_relation_made(peer_relation_name):
260
 
        for key, value in six.iteritems(dict(list(kwargs.items()) +
261
 
                                             list(relation_settings.items()))):
262
 
            key_prefix = relation_id or current_relation_id()
263
 
            peer_store(key_prefix + delimiter + key,
264
 
                       value,
265
 
                       relation_name=peer_relation_name)
266
 
    else:
267
 
        if peer_store_fatal:
268
 
            raise ValueError('Unable to detect '
269
 
                             'peer relation {}'.format(peer_relation_name))