~bloodearnest/charms/precise/squid-reverseproxy/trunk

« back to all changes in this revision

Viewing changes to hooks/hooks.py

  • Committer: Marco Ceppi
  • Date: 2013-11-07 01:40:09 UTC
  • mfrom: (28.1.85 master)
  • Revision ID: marco@ceppi.net-20131107014009-lqqg63wkyt6ot2ou
[sidnei] Greatly improve test coverage
[sidnei] Allow the use of an X-Balancer-Name header to select which cache_peer backend will be used for a specific request.
[sidnei] Support 'all-services' being set in the relation, in the way that the haproxy sets it, in addition to the previously supported 'sitenames' setting. Makes it compatible with the haproxy charm.
[sidnei] When the list of supported 'sitenames' (computed from dstdomain acls) changes, notify services related via the 'cached-website' relation. This allows to add new services in the haproxy service (or really, any other service related), which notifies the squid service, which then bubbles up to services related via cached-website.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
 
3
 
import glob
4
 
import grp
5
 
import json
6
3
import math
7
4
import os
8
 
import pwd
9
5
import random
10
6
import re
11
7
import shutil
12
8
import string
13
9
import subprocess
14
10
import sys
 
11
import yaml
 
12
import collections
 
13
import socket
 
14
 
 
15
from charmhelpers.core.hookenv import (
 
16
    config as config_get,
 
17
    log,
 
18
    relation_set,
 
19
    relation_get,
 
20
    relation_ids as get_relation_ids,
 
21
    relations_of_type,
 
22
    related_units,
 
23
    open_port,
 
24
    close_port,
 
25
)
 
26
from charmhelpers.fetch import apt_install
 
27
from charmhelpers.contrib.charmsupport.nrpe import NRPE
15
28
 
16
29
###############################################################################
17
30
# Global variables
19
32
default_squid3_config_dir = "/etc/squid3"
20
33
default_squid3_config = "%s/squid.conf" % default_squid3_config_dir
21
34
default_squid3_config_cache_dir = "/var/run/squid3"
22
 
hook_name = os.path.basename(sys.argv[0])
 
35
default_nagios_plugin_dir = "/usr/lib/nagios/plugins"
 
36
Server = collections.namedtuple("Server", "name address port options")
 
37
service_affecting_packages = ['squid3']
23
38
 
24
39
###############################################################################
25
40
# Supporting functions
26
41
###############################################################################
27
42
 
28
43
 
29
 
#------------------------------------------------------------------------------
30
 
# config_get:  Returns a dictionary containing all of the config information
31
 
#              Optional parameter: scope
32
 
#              scope: limits the scope of the returned configuration to the
33
 
#                     desired config item.
34
 
#------------------------------------------------------------------------------
35
 
def config_get(scope=None):
36
 
    try:
37
 
        config_cmd_line = ['config-get']
38
 
        if scope is not None:
39
 
            config_cmd_line.append(scope)
40
 
        config_cmd_line.append('--format=json')
41
 
        config_data = json.loads(subprocess.check_output(config_cmd_line))
42
 
    except Exception, e:
43
 
        subprocess.call(['juju-log', str(e)])
44
 
        config_data = None
45
 
    finally:
46
 
        return(config_data)
47
 
 
48
 
 
49
 
#------------------------------------------------------------------------------
50
 
# relation_json:  Returns json-formatted relation data
51
 
#                Optional parameters: scope, relation_id
52
 
#                scope:        limits the scope of the returned data to the
53
 
#                              desired item.
54
 
#                unit_name:    limits the data ( and optionally the scope )
55
 
#                              to the specified unit
56
 
#                relation_id:  specify relation id for out of context usage.
57
 
#------------------------------------------------------------------------------
58
 
def relation_json(scope=None, unit_name=None, relation_id=None):
59
 
    try:
60
 
        relation_cmd_line = ['relation-get', '--format=json']
61
 
        if relation_id is not None:
62
 
            relation_cmd_line.extend(('-r', relation_id))
63
 
        if scope is not None:
64
 
            relation_cmd_line.append(scope)
65
 
        else:
66
 
            relation_cmd_line.append('-')
67
 
        relation_cmd_line.append(unit_name)
68
 
        relation_data = subprocess.check_output(relation_cmd_line)
69
 
    except Exception, e:
70
 
        subprocess.call(['juju-log', str(e)])
71
 
        relation_data = None
72
 
    finally:
73
 
        return(relation_data)
74
 
 
75
 
 
76
 
#------------------------------------------------------------------------------
77
 
# relation_get:  Returns a dictionary containing the relation information
78
 
#                Optional parameters: scope, relation_id
79
 
#                scope:        limits the scope of the returned data to the
80
 
#                              desired item.
81
 
#                unit_name:    limits the data ( and optionally the scope )
82
 
#                              to the specified unit
83
 
#                relation_id:  specify relation id for out of context usage.
84
 
#------------------------------------------------------------------------------
85
 
def relation_get(scope=None, unit_name=None, relation_id=None):
86
 
    try:
87
 
        relation_data = json.loads(relation_json())
88
 
    except Exception, e:
89
 
        subprocess.call(['juju-log', str(e)])
90
 
        relation_data = None
91
 
    finally:
92
 
        return(relation_data)
93
 
 
94
 
 
95
 
#------------------------------------------------------------------------------
96
 
# relation_ids:  Returns a list of relation ids
97
 
#                optional parameters: relation_type
98
 
#                relation_type: return relations only of this type
99
 
#------------------------------------------------------------------------------
100
 
def relation_ids(relation_types=['website']):
101
 
    # accept strings or iterators
102
 
    if isinstance(relation_types, basestring):
103
 
        reltypes = [relation_types]
104
 
    else:
105
 
        reltypes = relation_types
106
 
    relids = []
107
 
    for reltype in reltypes:
108
 
        relid_cmd_line = ['relation-ids', '--format=json', reltype]
109
 
        relids.extend(json.loads(subprocess.check_output(relid_cmd_line)))
110
 
    return relids
111
 
 
112
 
 
113
 
#------------------------------------------------------------------------------
114
 
# relation_get_all:  Returns a dictionary containing the relation information
115
 
#                optional parameters: relation_type
116
 
#                relation_type: limits the scope of the returned data to the
117
 
#                               desired item.
118
 
#------------------------------------------------------------------------------
119
 
def relation_get_all():
120
 
    reldata = {}
121
 
    try:
122
 
        relids = relation_ids()
123
 
        for relid in relids:
124
 
            units_cmd_line = ['relation-list', '--format=json', '-r', relid]
125
 
            units = json.loads(subprocess.check_output(units_cmd_line))
126
 
            for unit in units:
127
 
                reldata[unit] = \
128
 
                    json.loads(relation_json(
129
 
                        relation_id=relid, unit_name=unit))
130
 
                if 'sitenames' in reldata[unit]:
131
 
                    reldata[unit]['sitenames'] = \
132
 
                        reldata[unit]['sitenames'].split()
133
 
                reldata[unit]['relation-id'] = relid
134
 
                reldata[unit]['name'] = unit.replace("/", "_")
135
 
    except Exception, e:
136
 
        subprocess.call(['juju-log', str(e)])
137
 
        reldata = []
138
 
    finally:
139
 
        return(reldata)
140
 
 
141
 
 
142
 
#------------------------------------------------------------------------------
143
 
# apt_get_install( packages ):  Installs a package
144
 
#------------------------------------------------------------------------------
145
 
def apt_get_install(packages=None):
146
 
    if packages is None:
147
 
        return(False)
148
 
    cmd_line = ['apt-get', '-y', 'install', '-qq']
149
 
    try:
150
 
        cmd_line.extend(packages.split())
151
 
    except AttributeError:
152
 
        cmd_line.extend(packages)
153
 
    return(subprocess.call(cmd_line))
154
 
 
155
 
 
156
 
#------------------------------------------------------------------------------
157
 
# enable_squid3:  Enable squid3 at boot time
158
 
#------------------------------------------------------------------------------
159
 
def enable_squid3():
160
 
    # squid is enabled at boot time
161
 
    return True
 
44
def get_id(sitename, relation_id, unit_name):
 
45
    unit_name = unit_name.replace('/', '_').replace('.', '_')
 
46
    relation_id = relation_id.replace(':', '_').replace('.', '_')
 
47
    if sitename is None:
 
48
        return relation_id + '__' + unit_name
 
49
    return sitename.replace('.', '_') + '__' + relation_id + '__' + unit_name
 
50
 
 
51
 
 
52
def get_forward_sites():
 
53
    reldata = []
 
54
    for relid in get_relation_ids('cached-website'):
 
55
        units = related_units(relid)
 
56
        for unit in units:
 
57
            data = relation_get(rid=relid, unit=unit)
 
58
            if 'sitenames' in data:
 
59
                data['sitenames'] = data['sitenames'].split()
 
60
            data['relation-id'] = relid
 
61
            data['name'] = unit.replace("/", "_")
 
62
            reldata.append(data)
 
63
    return reldata
 
64
 
 
65
 
 
66
def get_reverse_sites():
 
67
    all_sites = {}
 
68
 
 
69
    config_data = config_get()
 
70
    config_services = yaml.safe_load(config_data.get("services", "")) or ()
 
71
    server_options_by_site = {}
 
72
 
 
73
    for service_item in config_services:
 
74
        site = service_item["service_name"]
 
75
        servers = all_sites.setdefault(site, [])
 
76
        options = " ".join(service_item.get("server_options", []))
 
77
        server_options_by_site[site] = options
 
78
        for host, port in service_item["servers"]:
 
79
            servers.append(
 
80
                Server(name=get_id(None, site, host),
 
81
                       address=host, port=port,
 
82
                       options=options))
 
83
 
 
84
    relations = relations_of_type("website")
 
85
 
 
86
    for relation_data in relations:
 
87
        unit = relation_data["__unit__"]
 
88
        relation_id = relation_data["__relid__"]
 
89
        if not ("port" in relation_data or "all_services" in relation_data):
 
90
            log("No port in relation data for '%s', skipping." % unit)
 
91
            continue
 
92
 
 
93
        if not "private-address" in relation_data:
 
94
            log("No private-address in relation data "
 
95
                "for '%s', skipping." % unit)
 
96
            continue
 
97
 
 
98
        for site in relation_data.get("sitenames", "").split():
 
99
            servers = all_sites.setdefault(site, [])
 
100
            servers.append(
 
101
                Server(name=get_id(site, relation_id, unit),
 
102
                       address=relation_data["private-address"],
 
103
                       port=relation_data["port"],
 
104
                       options=server_options_by_site.get(site, '')))
 
105
 
 
106
        services = yaml.safe_load(relation_data.get("all_services", "")) or ()
 
107
        for service_item in services:
 
108
            site = service_item["service_name"]
 
109
            servers = all_sites.setdefault(site, [])
 
110
            servers.append(
 
111
                Server(name=get_id(site, relation_id, unit),
 
112
                       address=relation_data["private-address"],
 
113
                       port=service_item["service_port"],
 
114
                       options=server_options_by_site.get(site, '')))
 
115
 
 
116
        if not ("sitenames" in relation_data or
 
117
                "all_services" in relation_data):
 
118
            servers = all_sites.setdefault(None, [])
 
119
            servers.append(
 
120
                Server(name=get_id(None, relation_id, unit),
 
121
                       address=relation_data["private-address"],
 
122
                       port=relation_data["port"],
 
123
                       options=server_options_by_site.get(None, '')))
 
124
 
 
125
    if len(all_sites) == 0:
 
126
        return
 
127
 
 
128
    for site, servers in all_sites.iteritems():
 
129
        servers.sort()
 
130
 
 
131
    return all_sites
 
132
 
 
133
 
 
134
def ensure_package_status(packages, status):
 
135
    if status in ['install', 'hold']:
 
136
        selections = ''.join(['{} {}\n'.format(package, status)
 
137
                              for package in packages])
 
138
        dpkg = subprocess.Popen(['dpkg', '--set-selections'],
 
139
                                stdin=subprocess.PIPE)
 
140
        dpkg.communicate(input=selections)
162
141
 
163
142
 
164
143
#------------------------------------------------------------------------------
169
148
#------------------------------------------------------------------------------
170
149
def load_squid3_config(squid3_config_file="/etc/squid3/squid.conf"):
171
150
    if os.path.isfile(squid3_config_file):
172
 
        return(open(squid3_config_file).read())
173
 
    else:
174
 
        return(None)
 
151
        return open(squid3_config_file).read()
 
152
    return
175
153
 
176
154
 
177
155
#------------------------------------------------------------------------------
183
161
def get_service_ports(squid3_config_file="/etc/squid3/squid.conf"):
184
162
    squid3_config = load_squid3_config(squid3_config_file)
185
163
    if squid3_config is None:
186
 
        return(None)
187
 
    return(re.findall("http_port ([0-9]+)", squid3_config))
 
164
        return
 
165
    return re.findall("http_port ([0-9]+)", squid3_config)
188
166
 
189
167
 
190
168
#------------------------------------------------------------------------------
195
173
def get_sitenames(squid3_config_file="/etc/squid3/squid.conf"):
196
174
    squid3_config = load_squid3_config(squid3_config_file)
197
175
    if squid3_config is None:
198
 
        return(None)
199
 
    sitenames = re.findall("cache_peer_domain \w+ ([^!].*)", squid3_config)
200
 
    return(list(set(sitenames)))
201
 
 
202
 
 
203
 
#------------------------------------------------------------------------------
204
 
# open_port:  Convenience function to open a port in juju to
205
 
#             expose a service
206
 
#------------------------------------------------------------------------------
207
 
def open_port(port=None, protocol="TCP"):
208
 
    if port is None:
209
 
        return(None)
210
 
    return(subprocess.call(['/usr/bin/open-port', "%d/%s" %
211
 
                            (int(port), protocol)]))
212
 
 
213
 
 
214
 
#------------------------------------------------------------------------------
215
 
# close_port:  Convenience function to close a port in juju to
216
 
#              unexpose a service
217
 
#------------------------------------------------------------------------------
218
 
def close_port(port=None, protocol="TCP"):
219
 
    if port is None:
220
 
        return(None)
221
 
    return(subprocess.call(['/usr/bin/close-port', "%d/%s" %
222
 
                            (int(port), protocol)]))
 
176
        return
 
177
    sitenames = re.findall("acl [\w_-]+ dstdomain ([^!].*)", squid3_config)
 
178
    return list(set(sitenames))
223
179
 
224
180
 
225
181
#------------------------------------------------------------------------------
229
185
#------------------------------------------------------------------------------
230
186
def update_service_ports(old_service_ports=None, new_service_ports=None):
231
187
    if old_service_ports is None or new_service_ports is None:
232
 
        return(None)
 
188
        return
233
189
    for port in old_service_ports:
234
190
        if port not in new_service_ports:
235
191
            close_port(port)
248
204
                          if l not in 'Iil0oO1']
249
205
    random_chars = [random.choice(alphanumeric_chars)
250
206
                    for i in range(pwd_length)]
251
 
    return(''.join(random_chars))
 
207
    return ''.join(random_chars)
252
208
 
253
209
 
254
210
#------------------------------------------------------------------------------
257
213
def construct_squid3_config():
258
214
    from jinja2 import Environment, FileSystemLoader
259
215
    config_data = config_get()
260
 
    relations = relation_get_all()
 
216
    reverse_sites = get_reverse_sites()
 
217
    only_direct = set()
 
218
    if reverse_sites is not None:
 
219
        for site, servers in reverse_sites.iteritems():
 
220
            if not servers:
 
221
                only_direct.add(site)
 
222
 
261
223
    if config_data['refresh_patterns']:
262
 
        refresh_patterns = json.loads(config_data['refresh_patterns'])
 
224
        refresh_patterns = yaml.safe_load(config_data['refresh_patterns'])
263
225
    else:
264
226
        refresh_patterns = {}
265
 
    template_env = Environment(loader=FileSystemLoader(
266
 
        os.path.join(os.environ['CHARM_DIR'], 'templates')))
 
227
    # Use default refresh pattern if specified.
 
228
    if '.' in refresh_patterns:
 
229
        default_refresh_pattern = refresh_patterns.pop('.')
 
230
    else:
 
231
        default_refresh_pattern = {
 
232
            'min': 30,
 
233
            'percent': 20,
 
234
            'max': 4320,
 
235
            'options': [],
 
236
            }
 
237
 
 
238
    templates_dir = os.path.join(os.environ['CHARM_DIR'], 'templates')
 
239
    template_env = Environment(loader=FileSystemLoader(templates_dir))
 
240
 
267
241
    config_data['cache_l1'] = int(math.ceil(math.sqrt(
268
 
        int(config_data['cache_size_mb']) * 1024 / (16 *
269
 
        int(config_data['target_objs_per_dir']) * int(
270
 
            config_data['avg_obj_size_kb'])))))
 
242
        int(config_data['cache_size_mb']) * 1024 / (
 
243
            16 * int(config_data['target_objs_per_dir']) * int(
 
244
                config_data['avg_obj_size_kb'])))))
271
245
    config_data['cache_l2'] = config_data['cache_l1'] * 16
272
246
    templ_vars = {
273
247
        'config': config_data,
274
 
        'relations': relations,
 
248
        'sites': reverse_sites,
 
249
        'forward_relations': get_forward_sites(),
 
250
        'only_direct': only_direct,
275
251
        'refresh_patterns': refresh_patterns,
 
252
        'default_refresh_pattern': default_refresh_pattern,
276
253
    }
277
254
    template = template_env.get_template('main_config.template').\
278
255
        render(templ_vars)
 
256
    write_squid3_config('\n'.join(
 
257
        (l.strip() for l in str(template).splitlines())))
 
258
 
 
259
 
 
260
def write_squid3_config(contents):
279
261
    with open(default_squid3_config, 'w') as squid3_config:
280
 
        squid3_config.write(str(template))
 
262
        squid3_config.write(contents)
281
263
 
282
264
 
283
265
#------------------------------------------------------------------------------
286
268
#------------------------------------------------------------------------------
287
269
def service_squid3(action=None, squid3_config=default_squid3_config):
288
270
    if action is None or squid3_config is None:
289
 
        return(None)
 
271
        return
290
272
    elif action == "check":
291
273
        check_cmd = ['/usr/sbin/squid3', '-f', squid3_config, '-k', 'parse']
292
274
        retVal = subprocess.call(check_cmd)
293
275
        if retVal == 1:
294
 
            return(False)
 
276
            return False
295
277
        elif retVal == 0:
296
 
            return(True)
 
278
            return True
297
279
        else:
298
 
            return(False)
 
280
            return False
299
281
    elif action == 'status':
300
282
        status = subprocess.check_output(['status', 'squid3'])
301
283
        if re.search('running', status) is not None:
302
 
            return(True)
 
284
            return True
303
285
        else:
304
 
            return(False)
 
286
            return False
305
287
    elif action in ('start', 'stop', 'reload', 'restart'):
306
288
        retVal = subprocess.call([action, 'squid3'])
307
289
        if retVal == 0:
308
 
            return(True)
 
290
            return True
309
291
        else:
310
 
            return(False)
 
292
            return False
 
293
 
 
294
 
 
295
def install_nrpe_scripts():
 
296
    if not os.path.exists(default_nagios_plugin_dir):
 
297
        os.makedirs(default_nagios_plugin_dir)
 
298
    shutil.copy2('%s/files/check_squidpeers' % (
 
299
        os.environ['CHARM_DIR']),
 
300
        '{}/check_squidpeers'.format(default_nagios_plugin_dir))
311
301
 
312
302
 
313
303
def update_nrpe_checks():
314
 
    config_data = config_get()
315
 
    try:
316
 
        nagios_uid = pwd.getpwnam('nagios').pw_uid
317
 
        nagios_gid = grp.getgrnam('nagios').gr_gid
318
 
    except:
319
 
        subprocess.call(['juju-log', "Nagios user not setup, exiting"])
320
 
        return
321
 
 
322
 
    unit_name = os.environ['JUJU_UNIT_NAME'].replace('/', '-')
323
 
    nrpe_check_file = '/etc/nagios/nrpe.d/check_squidrp.cfg'
324
 
    nagios_hostname = "%s-%s" % (config_data['nagios_context'], unit_name)
325
 
    nagios_logdir = '/var/log/nagios'
326
 
    nagios_exportdir = '/var/lib/nagios/export'
327
 
    nrpe_service_file = \
328
 
        '/var/lib/nagios/export/service__%s_check_squidrp.cfg' % \
329
 
            (nagios_hostname)
330
 
    if not os.path.exists(nagios_logdir):
331
 
        os.mkdir(nagios_logdir)
332
 
        os.chown(nagios_logdir, nagios_uid, nagios_gid)
333
 
    if not os.path.exists(nagios_exportdir):
334
 
        subprocess.call(['juju-log', 'Exiting as %s is not accessible' %
335
 
            (nagios_exportdir)])
336
 
        return
337
 
    for f in os.listdir(nagios_exportdir):
338
 
        if re.search('.*check_squidrp.cfg', f):
339
 
            os.remove(os.path.join(nagios_exportdir, f))
340
 
    from jinja2 import Environment, FileSystemLoader
341
 
    template_env = Environment(
342
 
        loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], 'templates')))
343
 
    templ_vars = {
344
 
        'nagios_hostname': nagios_hostname,
345
 
        'nagios_servicegroup': config_data['nagios_context'],
346
 
    }
347
 
    template = template_env.get_template('nrpe_service.template').\
348
 
        render(templ_vars)
349
 
    with open(nrpe_service_file, 'w') as nrpe_service_config:
350
 
        nrpe_service_config.write(str(template))
351
 
    with open(nrpe_check_file, 'w') as nrpe_check_config:
352
 
        nrpe_check_config.write("# check squid\n")
353
 
        nrpe_check_config.write(
354
 
            "command[check_squidrp]=/usr/lib/nagios/plugins/check_http %s\n" %
355
 
                (config_data['nagios_check_http_params']))
356
 
    if os.path.isfile('/etc/init.d/nagios-nrpe-server'):
357
 
        subprocess.call(['service', 'nagios-nrpe-server', 'reload'])
 
304
    install_nrpe_scripts()
 
305
    nrpe_compat = NRPE()
 
306
    conf = nrpe_compat.config
 
307
    nrpe_compat.add_check(
 
308
        shortname='squidpeers',
 
309
        description='Check Squid Peers',
 
310
        check_cmd='check_squidpeers'
 
311
    )
 
312
    check_http_params = conf.get('nagios_check_http_params')
 
313
    if check_http_params:
 
314
        nrpe_compat.add_check(
 
315
            shortname='squidrp',
 
316
            description='Check Squid',
 
317
            check_cmd='check_http %s' % check_http_params
 
318
        )
 
319
    config_services_str = conf.get('services', '')
 
320
    config_services = yaml.safe_load(config_services_str) or ()
 
321
    for service in config_services:
 
322
        path = service.get('nrpe_check_path')
 
323
        if path is not None:
 
324
            command = 'check_http -I 127.0.0.1 -p 3128 --method=HEAD '
 
325
            service_name = service['service_name']
 
326
            if conf.get('x_balancer_name_allowed'):
 
327
                command += ("-u http://localhost%s "
 
328
                            "-k \\'X-Balancer-Name: %s\\'" % (
 
329
                                path, service_name))
 
330
            else:
 
331
                command += "-u http://%s%s" % (service_name, path)
 
332
            nrpe_compat.add_check(
 
333
                shortname='squid-%s' % service_name.replace(".", "_"),
 
334
                description='Check Squid for site %s' % service_name,
 
335
                check_cmd=command,
 
336
                )
 
337
    nrpe_compat.write()
 
338
 
 
339
 
 
340
def install_packages():
 
341
    apt_install("squid3 squidclient python-jinja2".split(), fatal=True)
 
342
    ensure_package_status(service_affecting_packages,
 
343
                          config_get('package_status'))
358
344
 
359
345
 
360
346
###############################################################################
361
347
# Hook functions
362
348
###############################################################################
363
349
def install_hook():
364
 
    for f in glob.glob('exec.d/*/charm-pre-install'):
365
 
        if os.path.isfile(f) and os.access(f, os.X_OK):
366
 
            subprocess.check_call(['sh', '-c', f])
367
350
    if not os.path.exists(default_squid3_config_dir):
368
351
        os.mkdir(default_squid3_config_dir, 0600)
369
352
    if not os.path.exists(default_squid3_config_cache_dir):
370
353
        os.mkdir(default_squid3_config_cache_dir, 0600)
371
 
    shutil.copy2('%s/files/default.squid3' % (os.environ['CHARM_DIR']), '/etc/default/squid3')
372
 
    return (apt_get_install(
373
 
        "squid3 python-jinja2") == enable_squid3() is not True)
 
354
    shutil.copy2('%s/files/default.squid3' % (
 
355
        os.environ['CHARM_DIR']), '/etc/default/squid3')
 
356
    install_packages()
 
357
    return True
374
358
 
375
359
 
376
360
def config_changed():
377
 
    current_service_ports = get_service_ports()
 
361
    old_service_ports = get_service_ports()
 
362
    old_sitenames = get_sitenames()
378
363
    construct_squid3_config()
379
364
    update_nrpe_checks()
 
365
    ensure_package_status(service_affecting_packages,
 
366
                          config_get('package_status'))
380
367
 
381
368
    if service_squid3("check"):
382
369
        updated_service_ports = get_service_ports()
383
 
        update_service_ports(current_service_ports, updated_service_ports)
 
370
        update_service_ports(old_service_ports, updated_service_ports)
384
371
        service_squid3("reload")
 
372
        if not (old_sitenames == get_sitenames()):
 
373
            notify_cached_website()
385
374
    else:
 
375
        # XXX Ideally the config should be restored to a working state if the
 
376
        # check fails, otherwise an inadvertent reload will cause the service
 
377
        # to be broken.
 
378
        log("squid configuration check failed, exiting")
386
379
        sys.exit(1)
387
380
 
388
381
 
389
382
def start_hook():
390
383
    if service_squid3("status"):
391
 
        return(service_squid3("restart"))
 
384
        return service_squid3("restart")
392
385
    else:
393
 
        return(service_squid3("start"))
 
386
        return service_squid3("start")
394
387
 
395
388
 
396
389
def stop_hook():
397
390
    if service_squid3("status"):
398
 
        return(service_squid3("stop"))
 
391
        return service_squid3("stop")
399
392
 
400
393
 
401
394
def website_interface(hook_name=None):
402
395
    if hook_name is None:
403
 
        return(None)
404
 
    if hook_name in ["joined", "changed", "broken", "departed"]:
 
396
        return
 
397
    if hook_name in ("joined", "changed", "broken", "departed"):
405
398
        config_changed()
406
399
 
407
400
 
408
401
def cached_website_interface(hook_name=None):
409
402
    if hook_name is None:
410
 
        return(None)
411
 
    if hook_name in ["joined", "changed"]:
412
 
        sitenames = ' '.join(get_sitenames())
413
 
        # passing only one port - the first one defined
414
 
        subprocess.call(['relation-set', 'port=%s' % get_service_ports()[0],
415
 
                         'sitenames=%s' % sitenames])
 
403
        return
 
404
    if hook_name in ("joined", "changed"):
 
405
        notify_cached_website(relation_ids=(None,))
 
406
    config_data = config_get()
 
407
    if config_data['enable_forward_proxy']:
 
408
        config_changed()
 
409
 
 
410
 
 
411
def get_hostname(host=None):
 
412
    my_host = socket.gethostname()
 
413
    if host is None or host == "0.0.0.0":
 
414
        # If the listen ip has been set to 0.0.0.0 then pass back the hostname
 
415
        return socket.getfqdn(my_host)
 
416
    elif host == "localhost":
 
417
        # If the fqdn lookup has returned localhost (lxc setups) then return
 
418
        # hostname
 
419
        return my_host
 
420
    return host
 
421
 
 
422
 
 
423
def notify_cached_website(relation_ids=None):
 
424
    hostname = get_hostname()
 
425
    # passing only one port - the first one defined
 
426
    port = get_service_ports()[0]
 
427
    sitenames = ' '.join(get_sitenames())
 
428
 
 
429
    for rid in relation_ids or get_relation_ids("cached-website"):
 
430
        relation_set(relation_id=rid, port=port,
 
431
                     hostname=hostname,
 
432
                     sitenames=sitenames)
 
433
 
 
434
 
 
435
def upgrade_charm():
 
436
    # Ensure that all current dependencies are installed.
 
437
    install_packages()
416
438
 
417
439
 
418
440
###############################################################################
419
441
# Main section
420
442
###############################################################################
421
 
def main():
 
443
def main(hook_name):
422
444
    if hook_name == "install":
423
445
        install_hook()
424
446
    elif hook_name == "config-changed":
425
447
        config_changed()
 
448
        update_nrpe_checks()
426
449
    elif hook_name == "start":
427
450
        start_hook()
428
451
    elif hook_name == "stop":
429
452
        stop_hook()
 
453
    elif hook_name == "upgrade-charm":
 
454
        upgrade_charm()
 
455
        config_changed()
 
456
        update_nrpe_checks()
430
457
 
431
458
    elif hook_name == "website-relation-joined":
432
459
        website_interface("joined")
446
473
    elif hook_name == "cached-website-relation-departed":
447
474
        cached_website_interface("departed")
448
475
 
449
 
    elif hook_name == "nrpe-external-master-relation-changed":
 
476
    elif hook_name in ("nrpe-external-master-relation-joined",
 
477
                       "local-monitors-relation-joined"):
450
478
        update_nrpe_checks()
451
479
 
452
480
    elif hook_name == "env-dump":
453
 
        print relation_get_all()
 
481
        print relations_of_type()
454
482
    else:
455
483
        print "Unknown hook"
456
484
        sys.exit(1)
457
485
 
458
486
if __name__ == '__main__':
459
 
    main()
 
487
    hook_name = os.path.basename(sys.argv[0])
 
488
    # Also support being invoked directly with hook as argument name.
 
489
    if hook_name == "hooks.py":
 
490
        if len(sys.argv) < 2:
 
491
            sys.exit("Missing required hook name argument.")
 
492
        hook_name = sys.argv[1]
 
493
    main(hook_name)