~james-page/charms/trusty/rabbitmq-server/network-splits

« back to all changes in this revision

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

  • Committer: james.page at ubuntu
  • Date: 2015-01-23 08:23:05 UTC
  • mfrom: (52.2.27 rabbitmq-server)
  • Revision ID: james.page@ubuntu.com-20150123082305-5uf1uk14iov78hl2
Rebase on next branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
#  Adam Gandelman <adamg@ubuntu.com>
7
7
#
8
8
 
 
9
"""
 
10
Helpers for clustering and determining "cluster leadership" and other
 
11
clustering-related helpers.
 
12
"""
 
13
 
9
14
import subprocess
10
15
import os
11
 
 
12
16
from socket import gethostname as get_unit_hostname
13
17
 
 
18
import six
 
19
 
14
20
from charmhelpers.core.hookenv import (
15
21
    log,
16
22
    relation_ids,
19
25
    config as config_get,
20
26
    INFO,
21
27
    ERROR,
 
28
    WARNING,
22
29
    unit_get,
23
30
)
24
31
 
27
34
    pass
28
35
 
29
36
 
 
37
def is_elected_leader(resource):
 
38
    """
 
39
    Returns True if the charm executing this is the elected cluster leader.
 
40
 
 
41
    It relies on two mechanisms to determine leadership:
 
42
        1. If the charm is part of a corosync cluster, call corosync to
 
43
        determine leadership.
 
44
        2. If the charm is not part of a corosync cluster, the leader is
 
45
        determined as being "the alive unit with the lowest unit numer". In
 
46
        other words, the oldest surviving unit.
 
47
    """
 
48
    if is_clustered():
 
49
        if not is_crm_leader(resource):
 
50
            log('Deferring action to CRM leader.', level=INFO)
 
51
            return False
 
52
    else:
 
53
        peers = peer_units()
 
54
        if peers and not oldest_peer(peers):
 
55
            log('Deferring action to oldest service unit.', level=INFO)
 
56
            return False
 
57
    return True
 
58
 
 
59
 
30
60
def is_clustered():
31
61
    for r_id in (relation_ids('ha') or []):
32
62
        for unit in (relation_list(r_id) or []):
38
68
    return False
39
69
 
40
70
 
41
 
def is_leader(resource):
 
71
def is_crm_leader(resource):
 
72
    """
 
73
    Returns True if the charm calling this is the elected corosync leader,
 
74
    as returned by calling the external "crm" command.
 
75
    """
42
76
    cmd = [
43
77
        "crm", "resource",
44
78
        "show", resource
45
79
    ]
46
80
    try:
47
 
        status = subprocess.check_output(cmd)
 
81
        status = subprocess.check_output(cmd).decode('UTF-8')
48
82
    except subprocess.CalledProcessError:
49
83
        return False
50
84
    else:
54
88
            return False
55
89
 
56
90
 
57
 
def peer_units():
 
91
def is_leader(resource):
 
92
    log("is_leader is deprecated. Please consider using is_crm_leader "
 
93
        "instead.", level=WARNING)
 
94
    return is_crm_leader(resource)
 
95
 
 
96
 
 
97
def peer_units(peer_relation="cluster"):
58
98
    peers = []
59
 
    for r_id in (relation_ids('cluster') or []):
 
99
    for r_id in (relation_ids(peer_relation) or []):
60
100
        for unit in (relation_list(r_id) or []):
61
101
            peers.append(unit)
62
102
    return peers
63
103
 
64
104
 
 
105
def peer_ips(peer_relation='cluster', addr_key='private-address'):
 
106
    '''Return a dict of peers and their private-address'''
 
107
    peers = {}
 
108
    for r_id in relation_ids(peer_relation):
 
109
        for unit in relation_list(r_id):
 
110
            peers[unit] = relation_get(addr_key, rid=r_id, unit=unit)
 
111
    return peers
 
112
 
 
113
 
65
114
def oldest_peer(peers):
 
115
    """Determines who the oldest peer is by comparing unit numbers."""
66
116
    local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1])
67
117
    for peer in peers:
68
118
        remote_unit_no = int(peer.split('/')[1])
72
122
 
73
123
 
74
124
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
 
125
    log("eligible_leader is deprecated. Please consider using "
 
126
        "is_elected_leader instead.", level=WARNING)
 
127
    return is_elected_leader(resource)
85
128
 
86
129
 
87
130
def https():
97
140
        return True
98
141
    for r_id in relation_ids('identity-service'):
99
142
        for unit in relation_list(r_id):
 
143
            # TODO - needs fixing for new helper as ssl_cert/key suffixes with CN
100
144
            rel_state = [
101
145
                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
146
                relation_get('ca_cert', rid=r_id, unit=unit),
105
147
            ]
106
148
            # NOTE: works around (LP: #1203241)
109
151
    return False
110
152
 
111
153
 
112
 
def determine_api_port(public_port):
 
154
def determine_api_port(public_port, singlenode_mode=False):
113
155
    '''
114
156
    Determine correct API server listening port based on
115
157
    existence of HTTPS reverse proxy and/or haproxy.
116
158
 
117
159
    public_port: int: standard public port for given service
118
160
 
 
161
    singlenode_mode: boolean: Shuffle ports when only a single unit is present
 
162
 
119
163
    returns: int: the correct listening port for the API service
120
164
    '''
121
165
    i = 0
122
 
    if len(peer_units()) > 0 or is_clustered():
 
166
    if singlenode_mode:
 
167
        i += 1
 
168
    elif len(peer_units()) > 0 or is_clustered():
123
169
        i += 1
124
170
    if https():
125
171
        i += 1
126
172
    return public_port - (i * 10)
127
173
 
128
174
 
129
 
def determine_apache_port(public_port):
 
175
def determine_apache_port(public_port, singlenode_mode=False):
130
176
    '''
131
177
    Description: Determine correct apache listening port based on public IP +
132
178
    state of the cluster.
133
179
 
134
180
    public_port: int: standard public port for given service
135
181
 
 
182
    singlenode_mode: boolean: Shuffle ports when only a single unit is present
 
183
 
136
184
    returns: int: the correct listening port for the HAProxy service
137
185
    '''
138
186
    i = 0
139
 
    if len(peer_units()) > 0 or is_clustered():
 
187
    if singlenode_mode:
 
188
        i += 1
 
189
    elif len(peer_units()) > 0 or is_clustered():
140
190
        i += 1
141
191
    return public_port - (i * 10)
142
192
 
156
206
    for setting in settings:
157
207
        conf[setting] = config_get(setting)
158
208
    missing = []
159
 
    [missing.append(s) for s, v in conf.iteritems() if v is None]
 
209
    [missing.append(s) for s, v in six.iteritems(conf) if v is None]
160
210
    if missing:
161
211
        log('Insufficient config data to configure hacluster.', level=ERROR)
162
212
        raise HAIncompleteConfig