~openstack-charmers-archive/charms/trusty/cinder-ceph/old-1501

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/hahelpers/cluster.py

  • Committer: James Page
  • Date: 2014-01-23 16:14:44 UTC
  • Revision ID: james.page@canonical.com-20140123161444-kuaebxd66qmeu94t
Initial version of charm

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# Authors:
 
5
#  James Page <james.page@ubuntu.com>
 
6
#  Adam Gandelman <adamg@ubuntu.com>
 
7
#
 
8
 
 
9
import subprocess
 
10
import os
 
11
 
 
12
from socket import gethostname as get_unit_hostname
 
13
 
 
14
from charmhelpers.core.hookenv import (
 
15
    log,
 
16
    relation_ids,
 
17
    related_units as relation_list,
 
18
    relation_get,
 
19
    config as config_get,
 
20
    INFO,
 
21
    ERROR,
 
22
    unit_get,
 
23
)
 
24
 
 
25
 
 
26
class HAIncompleteConfig(Exception):
 
27
    pass
 
28
 
 
29
 
 
30
def is_clustered():
 
31
    for r_id in (relation_ids('ha') or []):
 
32
        for unit in (relation_list(r_id) or []):
 
33
            clustered = relation_get('clustered',
 
34
                                     rid=r_id,
 
35
                                     unit=unit)
 
36
            if clustered:
 
37
                return True
 
38
    return False
 
39
 
 
40
 
 
41
def is_leader(resource):
 
42
    cmd = [
 
43
        "crm", "resource",
 
44
        "show", resource
 
45
    ]
 
46
    try:
 
47
        status = subprocess.check_output(cmd)
 
48
    except subprocess.CalledProcessError:
 
49
        return False
 
50
    else:
 
51
        if get_unit_hostname() in status:
 
52
            return True
 
53
        else:
 
54
            return False
 
55
 
 
56
 
 
57
def peer_units():
 
58
    peers = []
 
59
    for r_id in (relation_ids('cluster') or []):
 
60
        for unit in (relation_list(r_id) or []):
 
61
            peers.append(unit)
 
62
    return peers
 
63
 
 
64
 
 
65
def oldest_peer(peers):
 
66
    local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1])
 
67
    for peer in peers:
 
68
        remote_unit_no = int(peer.split('/')[1])
 
69
        if remote_unit_no < local_unit_no:
 
70
            return False
 
71
    return True
 
72
 
 
73
 
 
74
def eligible_leader(resource):
 
75
    if is_clustered():
 
76
        if not is_leader(resource):
 
77
            log('Deferring action to CRM leader.', level=INFO)
 
78
            return False
 
79
    else:
 
80
        peers = peer_units()
 
81
        if peers and not oldest_peer(peers):
 
82
            log('Deferring action to oldest service unit.', level=INFO)
 
83
            return False
 
84
    return True
 
85
 
 
86
 
 
87
def https():
 
88
    '''
 
89
    Determines whether enough data has been provided in configuration
 
90
    or relation data to configure HTTPS
 
91
    .
 
92
    returns: boolean
 
93
    '''
 
94
    if config_get('use-https') == "yes":
 
95
        return True
 
96
    if config_get('ssl_cert') and config_get('ssl_key'):
 
97
        return True
 
98
    for r_id in relation_ids('identity-service'):
 
99
        for unit in relation_list(r_id):
 
100
            rel_state = [
 
101
                relation_get('https_keystone', rid=r_id, unit=unit),
 
102
                relation_get('ssl_cert', rid=r_id, unit=unit),
 
103
                relation_get('ssl_key', rid=r_id, unit=unit),
 
104
                relation_get('ca_cert', rid=r_id, unit=unit),
 
105
            ]
 
106
            # NOTE: works around (LP: #1203241)
 
107
            if (None not in rel_state) and ('' not in rel_state):
 
108
                return True
 
109
    return False
 
110
 
 
111
 
 
112
def determine_api_port(public_port):
 
113
    '''
 
114
    Determine correct API server listening port based on
 
115
    existence of HTTPS reverse proxy and/or haproxy.
 
116
 
 
117
    public_port: int: standard public port for given service
 
118
 
 
119
    returns: int: the correct listening port for the API service
 
120
    '''
 
121
    i = 0
 
122
    if len(peer_units()) > 0 or is_clustered():
 
123
        i += 1
 
124
    if https():
 
125
        i += 1
 
126
    return public_port - (i * 10)
 
127
 
 
128
 
 
129
def determine_haproxy_port(public_port):
 
130
    '''
 
131
    Description: Determine correct proxy listening port based on public IP +
 
132
    existence of HTTPS reverse proxy.
 
133
 
 
134
    public_port: int: standard public port for given service
 
135
 
 
136
    returns: int: the correct listening port for the HAProxy service
 
137
    '''
 
138
    i = 0
 
139
    if https():
 
140
        i += 1
 
141
    return public_port - (i * 10)
 
142
 
 
143
 
 
144
def get_hacluster_config():
 
145
    '''
 
146
    Obtains all relevant configuration from charm configuration required
 
147
    for initiating a relation to hacluster:
 
148
 
 
149
        ha-bindiface, ha-mcastport, vip, vip_iface, vip_cidr
 
150
 
 
151
    returns: dict: A dict containing settings keyed by setting name.
 
152
    raises: HAIncompleteConfig if settings are missing.
 
153
    '''
 
154
    settings = ['ha-bindiface', 'ha-mcastport', 'vip', 'vip_iface', 'vip_cidr']
 
155
    conf = {}
 
156
    for setting in settings:
 
157
        conf[setting] = config_get(setting)
 
158
    missing = []
 
159
    [missing.append(s) for s, v in conf.iteritems() if v is None]
 
160
    if missing:
 
161
        log('Insufficient config data to configure hacluster.', level=ERROR)
 
162
        raise HAIncompleteConfig
 
163
    return conf
 
164
 
 
165
 
 
166
def canonical_url(configs, vip_setting='vip'):
 
167
    '''
 
168
    Returns the correct HTTP URL to this host given the state of HTTPS
 
169
    configuration and hacluster.
 
170
 
 
171
    :configs    : OSTemplateRenderer: A config tempating object to inspect for
 
172
                                      a complete https context.
 
173
    :vip_setting:                str: Setting in charm config that specifies
 
174
                                      VIP address.
 
175
    '''
 
176
    scheme = 'http'
 
177
    if 'https' in configs.complete_contexts():
 
178
        scheme = 'https'
 
179
    if is_clustered():
 
180
        addr = config_get(vip_setting)
 
181
    else:
 
182
        addr = unit_get('private-address')
 
183
    return '%s://%s' % (scheme, addr)