~moon127/charms/trusty/ceilometer/add-execd-preinstall

« back to all changes in this revision

Viewing changes to hooks/utils.py

  • Committer: yolanda.robla at canonical
  • Date: 2013-02-06 11:06:34 UTC
  • Revision ID: yolanda.robla@canonical.com-20130206110634-qc7gqbxnsn7cijrk
new version of charm

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
 
1
#
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# Authors:
 
5
#  James Page <james.page@ubuntu.com>
 
6
#  Paul Collins <paul.collins@canonical.com>
 
7
#
 
8
 
 
9
import os
2
10
import subprocess
 
11
import socket
3
12
import sys
4
 
import json
5
 
import os
6
 
import time
7
 
 
8
 
from lib.openstack_common import *
9
 
 
10
 
ceilometer_conf = "/etc/ceilometer/ceilometer.conf"
11
 
 
12
 
def execute(cmd, die=False, echo=False):
13
 
    """ Executes a command 
14
 
 
15
 
    if die=True, script will exit(1) if command does not return 0
16
 
    if echo=True, output of command will be printed to stdout
17
 
 
18
 
    returns a tuple: (stdout, stderr, return code)
19
 
    """
20
 
    p = subprocess.Popen(cmd.split(" "),
21
 
                         stdout=subprocess.PIPE,
22
 
                         stdin=subprocess.PIPE,
23
 
                         stderr=subprocess.PIPE)
24
 
    stdout=""
25
 
    stderr=""
26
 
 
27
 
    def print_line(l):
28
 
        if echo:
29
 
            print l.strip('\n')
30
 
            sys.stdout.flush()
31
 
 
32
 
    for l in iter(p.stdout.readline, ''):
33
 
        print_line(l)
34
 
        stdout += l
35
 
    for l in iter(p.stderr.readline, ''):
36
 
        print_line(l)
37
 
        stderr += l
38
 
 
39
 
    p.communicate()
40
 
    rc = p.returncode
41
 
 
42
 
    if die and rc != 0:
43
 
        error_out("ERROR: command %s return non-zero.\n" % cmd)
44
 
    return (stdout, stderr, rc)
45
 
 
46
 
 
47
 
def config_get():
48
 
    """ Obtain the units config via 'config-get' 
49
 
    Returns a dict representing current config.
50
 
    private-address and IP of the unit is also tacked on for
51
 
    convienence
52
 
    """
53
 
    output = execute("config-get --format json")[0]
54
 
    if output:
55
 
        config = json.loads(output)
56
 
        # make sure no config element is blank after config-get
57
 
        for c in config.keys():
58
 
            if not config[c]:
59
 
                error_out("ERROR: Config option has no paramter: %s" % c)
60
 
        # tack on our private address and ip
61
 
        hostname = execute("unit-get private-address")[0].strip()
62
 
        config["hostname"] = execute("unit-get private-address")[0].strip()
 
13
import apt_pkg as apt
 
14
 
 
15
 
 
16
def do_hooks(hooks):
 
17
    hook = os.path.basename(sys.argv[0])
 
18
 
 
19
    try:
 
20
        hook_func = hooks[hook]
 
21
    except KeyError:
 
22
        juju_log('INFO',
 
23
                 "This charm doesn't know how to handle '{}'.".format(hook))
63
24
    else:
64
 
        config = {}
65
 
    return config
66
 
 
67
 
def relation_ids(relation_name=None):
68
 
    j = execute('relation-ids --format=json %s' % relation_name)[0]
69
 
    return json.loads(j)
70
 
 
71
 
def relation_list(relation_id=None):
72
 
    cmd = 'relation-list --format=json'
73
 
    if relation_id:
74
 
        cmd += ' -r %s' % relation_id
75
 
    j = execute(cmd)[0]
76
 
    return json.loads(j)
77
 
 
78
 
def relation_set(relation_data):
79
 
    """ calls relation-set for all key=values in dict """
80
 
    for k in relation_data:
81
 
        execute("relation-set %s=%s" % (k, relation_data[k]), die=True)
82
 
 
83
 
def relation_get(relation_data):
84
 
    """ Obtain all current relation data
85
 
    relation_data is a list of options to query from the relation
86
 
    Returns a k,v dict of the results. 
87
 
    Leave empty responses out of the results as they haven't yet been
88
 
    set on the other end. 
89
 
    Caller can then "len(results.keys()) == len(relation_data)" to find out if
90
 
    all relation values have been set on the other side
91
 
    """
92
 
    results = {}
93
 
    for r in relation_data:
94
 
        result = execute("relation-get %s" % r, die=True)[0].strip('\n')
95
 
        if result != "":
96
 
           results[r] = result
97
 
    return results
98
 
 
99
 
def relation_get_dict(relation_id=None, remote_unit=None):
100
 
    """Obtain all relation data as dict by way of JSON"""
101
 
    cmd = 'relation-get --format=json'
102
 
    if relation_id:
103
 
        cmd += ' -r %s' % relation_id
104
 
    if remote_unit:
105
 
        remote_unit_orig = os.getenv('JUJU_REMOTE_UNIT', None)
106
 
        os.environ['JUJU_REMOTE_UNIT'] = remote_unit
107
 
    j = execute(cmd, die=True)[0]
108
 
    if remote_unit and remote_unit_orig:
109
 
        os.environ['JUJU_REMOTE_UNIT'] = remote_unit_orig
110
 
    d = json.loads(j)
111
 
    settings = {}
112
 
    # convert unicode to strings
113
 
    for k, v in d.iteritems():
114
 
        settings[str(k)] = str(v)
115
 
    return settings
116
 
 
117
 
def update_config_block(block, **kwargs):
118
 
    """ Updates ceilometer.conf blocks given kwargs.
119
 
    Can be used to update driver settings for a particular backend,
120
 
    setting the sql connection, etc.
121
 
 
122
 
    Parses block heading as '[block]'
123
 
 
124
 
    If block does not exist, a new block will be created at end of file with
125
 
    given kwargs
126
 
    """
127
 
    f = open(ceilometer_conf, "r+")
128
 
    orig = f.readlines()
129
 
    new = []
130
 
    found_block = ""
131
 
    heading = "[%s]\n" % block
132
 
 
133
 
    lines = len(orig)
134
 
    ln = 0
135
 
 
136
 
    def update_block(block):
137
 
        for k, v in kwargs.iteritems():
138
 
            for l in block:
139
 
                if l.strip().split(" ")[0] == k:
140
 
                    block[block.index(l)] = "%s = %s\n" % (k, v)
141
 
                    return
142
 
            block.append('%s = %s\n' % (k, v))
143
 
            block.append('\n')
144
 
 
145
 
    try:
146
 
        found = False
147
 
        while ln < lines:
148
 
            if orig[ln] != heading:
149
 
                new.append(orig[ln])
150
 
                ln += 1
151
 
            else:
152
 
                new.append(orig[ln])
153
 
                ln += 1
154
 
                block = []
155
 
                while orig[ln].strip() != '':
156
 
                    block.append(orig[ln])
157
 
                    ln += 1
158
 
                update_block(block)
159
 
                new += block
160
 
                found = True
161
 
 
162
 
        if not found:
163
 
            if new[(len(new) - 1)].strip() != '':
164
 
                new.append('\n')
165
 
            new.append('%s' % heading)
166
 
            for k, v in kwargs.iteritems():
167
 
                new.append('%s = %s\n' % (k, v))
168
 
            new.append('\n')
169
 
    except:
170
 
        error_out('Error while attempting to update config block. '\
171
 
                  'Refusing to overwite existing config.')
172
 
 
 
25
        hook_func()
 
26
 
 
27
 
 
28
def install(*pkgs):
 
29
    cmd = [
 
30
        'apt-get',
 
31
        '-y',
 
32
        'install'
 
33
          ]
 
34
    for pkg in pkgs:
 
35
        cmd.append(pkg)
 
36
    subprocess.check_call(cmd)
 
37
 
 
38
TEMPLATES_DIR = 'templates'
 
39
 
 
40
try:
 
41
    import jinja2
 
42
except ImportError:
 
43
    install('python-jinja2')
 
44
    import jinja2
 
45
 
 
46
def render_template(template_name, context, template_dir=TEMPLATES_DIR):
 
47
    templates = jinja2.Environment(
 
48
                    loader=jinja2.FileSystemLoader(template_dir)
 
49
                    )
 
50
    template = templates.get_template(template_name)
 
51
    return template.render(context)
 
52
 
 
53
CLOUD_ARCHIVE = \
 
54
""" # Ubuntu Cloud Archive
 
55
deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
 
56
"""
 
57
 
 
58
CLOUD_ARCHIVE_POCKETS = {
 
59
    'precise-folsom': 'precise-updates/folsom',
 
60
    'precise-folsom/updates': 'precise-updates/folsom',
 
61
    'precise-folsom/proposed': 'precise-proposed/folsom',
 
62
    'precise-grizzly': 'precise-updates/grizzly',
 
63
    'precise-grizzly/updates': 'precise-updates/grizzly',
 
64
    'precise-grizzly/proposed': 'precise-proposed/grizzly'
 
65
    }
 
66
 
 
67
def configure_source():
 
68
    source = str(config_get('openstack-origin'))
 
69
    if not source:
173
70
        return
174
 
 
175
 
    # backup original config
176
 
    backup = open(ceilometer_conf + '.juju-back', 'w+')
177
 
    for l in orig:
178
 
        backup.write(l)
179
 
    backup.close()
180
 
 
181
 
    # update config
182
 
    f.seek(0)
183
 
    f.truncate()
184
 
    for l in new:
185
 
        f.write(l)
186
 
 
187
 
 
188
 
def ceilometer_conf_update(opt, val):
189
 
    """ Updates ceilometer.conf values 
190
 
    If option exists, it is reset to new value
191
 
    If it does not, it added to the top of the config file after the [DEFAULT]
192
 
    heading to keep it out of the paste deploy config
193
 
    """
194
 
    f = open(ceilometer_conf, "r+")
195
 
    orig = f.readlines()
196
 
    new = ""
197
 
    found = False
198
 
    for l in orig:
199
 
        if l.split(' ')[0] == opt:
200
 
            juju_log("Updating %s, setting %s = %s" % (keystone_conf, opt, val))
201
 
            new += "%s = %s\n" % (opt, val)
202
 
            found  = True
 
71
    if source.startswith('ppa:'):
 
72
        cmd = [
 
73
            'add-apt-repository',
 
74
            source
 
75
            ]
 
76
        subprocess.check_call(cmd)
 
77
    if source.startswith('cloud:'):
 
78
        install('ubuntu-cloud-keyring')
 
79
        pocket = source.split(':')[1]
 
80
        with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt:
 
81
            apt.write(CLOUD_ARCHIVE.format(CLOUD_ARCHIVE_POCKETS[pocket]))
 
82
    if source.startswith('deb'):
 
83
        l = len(source.split('|'))
 
84
        if l == 2:
 
85
            (apt_line, key) = source.split('|')
 
86
            cmd = [
 
87
                'apt-key',
 
88
                'adv', '--keyserver keyserver.ubuntu.com',
 
89
                '--recv-keys', key
 
90
                ]
 
91
            subprocess.check_call(cmd)
 
92
        elif l == 1:
 
93
            apt_line = source
 
94
 
 
95
        with open('/etc/apt/sources.list.d/ceilometer.list', 'w') as apt:
 
96
            apt.write(apt_line + "\n")
 
97
    cmd = [
 
98
        'apt-get',
 
99
        'update'
 
100
        ]
 
101
    subprocess.check_call(cmd)
 
102
 
 
103
# Protocols
 
104
TCP = 'TCP'
 
105
UDP = 'UDP'
 
106
 
 
107
 
 
108
def expose(port, protocol='TCP'):
 
109
    cmd = [
 
110
        'open-port',
 
111
        '{}/{}'.format(port, protocol)
 
112
        ]
 
113
    subprocess.check_call(cmd)
 
114
 
 
115
 
 
116
def juju_log(severity, message):
 
117
    cmd = [
 
118
        'juju-log',
 
119
        '--log-level', severity,
 
120
        message
 
121
        ]
 
122
    subprocess.check_call(cmd)
 
123
 
 
124
 
 
125
def relation_ids(relation):
 
126
    cmd = [
 
127
        'relation-ids',
 
128
        relation
 
129
        ]
 
130
    return subprocess.check_output(cmd).split()  # IGNORE:E1103
 
131
 
 
132
 
 
133
def relation_list(rid):
 
134
    cmd = [
 
135
        'relation-list',
 
136
        '-r', rid,
 
137
        ]
 
138
    return subprocess.check_output(cmd).split()  # IGNORE:E1103
 
139
 
 
140
 
 
141
def relation_get(attribute, unit=None, rid=None):
 
142
    cmd = [
 
143
        'relation-get',
 
144
        ]
 
145
    if rid:
 
146
        cmd.append('-r')
 
147
        cmd.append(rid)
 
148
    cmd.append(attribute)
 
149
    if unit:
 
150
        cmd.append(unit)
 
151
    value = subprocess.check_output(cmd).strip()  # IGNORE:E1103
 
152
    if value == "":
 
153
        return None
 
154
    else:
 
155
        return value
 
156
 
 
157
 
 
158
def relation_set(**kwargs):
 
159
    cmd = [
 
160
        'relation-set'
 
161
        ]
 
162
    args = []
 
163
    for k, v in kwargs.items():
 
164
        if k == 'rid':
 
165
            cmd.append('-r')
 
166
            cmd.append(v)
203
167
        else:
204
 
            new += l
205
 
    new = new.split('\n')
206
 
    # insert a new value at the top of the file, after the 'DEFAULT' header so
207
 
    # as not to muck up paste deploy configuration later in the file 
208
 
    if not found:
209
 
        juju_log("Adding new config option %s = %s" % (opt, val))
210
 
        header = new.index("[DEFAULT]")
211
 
        new.insert((header+1), "%s = %s" % (opt, val))
212
 
    f.seek(0)
213
 
    f.truncate()
214
 
    for l in new:
215
 
        f.write("%s\n" % l)
216
 
    f.close
 
168
            args.append('{}={}'.format(k, v))
 
169
    cmd += args
 
170
    subprocess.check_call(cmd)
 
171
 
 
172
 
 
173
def unit_get(attribute):
 
174
    cmd = [
 
175
        'unit-get',
 
176
        attribute
 
177
        ]
 
178
    value = subprocess.check_output(cmd).strip()  # IGNORE:E1103
 
179
    if value == "":
 
180
        return None
 
181
    else:
 
182
        return value
 
183
 
 
184
 
 
185
def config_get(attribute):
 
186
    cmd = [
 
187
        'config-get',
 
188
        attribute
 
189
        ]
 
190
    value = subprocess.check_output(cmd).strip()  # IGNORE:E1103
 
191
    if value == "":
 
192
        return None
 
193
    else:
 
194
        return value
 
195
 
 
196
 
 
197
def get_unit_hostname():
 
198
    return socket.gethostname()
 
199
 
 
200
 
 
201
def get_host_ip(hostname=unit_get('private-address')):
 
202
    try:
 
203
        # Test to see if already an IPv4 address
 
204
        socket.inet_aton(hostname)
 
205
        return hostname
 
206
    except socket.error:
 
207
        pass
 
208
    try:
 
209
        answers = dns.resolver.query(hostname, 'A')
 
210
        if answers:
 
211
            return answers[0].address
 
212
    except dns.resolver.NXDOMAIN:
 
213
        pass
 
214
    return None
 
215
 
 
216
 
 
217
CLUSTER_RESOURCES = {
 
218
    'quantum-dhcp-agent': 'res_quantum_dhcp_agent',
 
219
    'quantum-l3-agent': 'res_quantum_l3_agent'
 
220
    }
 
221
 
 
222
HAMARKER = '/var/lib/juju/haconfigured'
 
223
 
 
224
 
 
225
def _service_ctl(service, action):
 
226
    if (os.path.exists(HAMARKER) and
 
227
        os.path.exists(os.path.join('/etc/init/',
 
228
                                   '{}.override'.format(service))) and
 
229
        service in CLUSTER_RESOURCES):
 
230
        hostname = str(subprocess.check_output(['hostname'])).strip()
 
231
        service_status = \
 
232
            subprocess.check_output(['crm', 'resource', 'show',
 
233
                                     CLUSTER_RESOURCES[service]])
 
234
        # Only restart if we are the node that owns the service
 
235
        if hostname in service_status:
 
236
            subprocess.check_call(['crm', 'resource', action,
 
237
                                  CLUSTER_RESOURCES[service]])
 
238
    else:
 
239
        subprocess.check_call(['service', service, action])
 
240
 
 
241
 
 
242
def restart(*services):
 
243
    for service in services:
 
244
        _service_ctl(service, 'restart')
 
245
 
 
246
 
 
247
def stop(*services):
 
248
    for service in services:
 
249
        _service_ctl(service, 'stop')
 
250
 
 
251
 
 
252
def start(*services):
 
253
    for service in services:
 
254
        _service_ctl(service, 'start')
 
255
 
 
256
 
 
257
def get_os_version(package=None):
 
258
    apt.init()
 
259
    cache = apt.Cache()
 
260
    pkg = cache[package or 'quantum-common']
 
261
    if pkg.current_ver:
 
262
        return apt.upstream_version(pkg.current_ver.ver_str)
 
263
    else:
 
264
        return None