~thedac/charms/precise/gunicorn/fix-config-changed

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/core/host.py

  • Committer: Marco Ceppi
  • Date: 2014-03-04 16:20:11 UTC
  • mfrom: (28.1.7 gunicorn)
  • Revision ID: marco@ceppi.net-20140304162011-kpulghowq5j4kpyj
[bloodearnest] Clean up of gunicorn charm, adding charmhelpers, tests and re-add env_extra param

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Tools for working with the host system"""
 
2
# Copyright 2012 Canonical Ltd.
 
3
#
 
4
# Authors:
 
5
#  Nick Moffitt <nick.moffitt@canonical.com>
 
6
#  Matthew Wedgwood <matthew.wedgwood@canonical.com>
 
7
 
 
8
import os
 
9
import pwd
 
10
import grp
 
11
import random
 
12
import string
 
13
import subprocess
 
14
import hashlib
 
15
 
 
16
from collections import OrderedDict
 
17
 
 
18
from hookenv import log
 
19
 
 
20
 
 
21
def service_start(service_name):
 
22
    """Start a system service"""
 
23
    return service('start', service_name)
 
24
 
 
25
 
 
26
def service_stop(service_name):
 
27
    """Stop a system service"""
 
28
    return service('stop', service_name)
 
29
 
 
30
 
 
31
def service_restart(service_name):
 
32
    """Restart a system service"""
 
33
    return service('restart', service_name)
 
34
 
 
35
 
 
36
def service_reload(service_name, restart_on_failure=False):
 
37
    """Reload a system service, optionally falling back to restart if reload fails"""
 
38
    service_result = service('reload', service_name)
 
39
    if not service_result and restart_on_failure:
 
40
        service_result = service('restart', service_name)
 
41
    return service_result
 
42
 
 
43
 
 
44
def service(action, service_name):
 
45
    """Control a system service"""
 
46
    cmd = ['service', service_name, action]
 
47
    return subprocess.call(cmd) == 0
 
48
 
 
49
 
 
50
def service_running(service):
 
51
    """Determine whether a system service is running"""
 
52
    try:
 
53
        output = subprocess.check_output(['service', service, 'status'])
 
54
    except subprocess.CalledProcessError:
 
55
        return False
 
56
    else:
 
57
        if ("start/running" in output or "is running" in output):
 
58
            return True
 
59
        else:
 
60
            return False
 
61
 
 
62
 
 
63
def adduser(username, password=None, shell='/bin/bash', system_user=False):
 
64
    """Add a user to the system"""
 
65
    try:
 
66
        user_info = pwd.getpwnam(username)
 
67
        log('user {0} already exists!'.format(username))
 
68
    except KeyError:
 
69
        log('creating user {0}'.format(username))
 
70
        cmd = ['useradd']
 
71
        if system_user or password is None:
 
72
            cmd.append('--system')
 
73
        else:
 
74
            cmd.extend([
 
75
                '--create-home',
 
76
                '--shell', shell,
 
77
                '--password', password,
 
78
            ])
 
79
        cmd.append(username)
 
80
        subprocess.check_call(cmd)
 
81
        user_info = pwd.getpwnam(username)
 
82
    return user_info
 
83
 
 
84
 
 
85
def add_user_to_group(username, group):
 
86
    """Add a user to a group"""
 
87
    cmd = [
 
88
        'gpasswd', '-a',
 
89
        username,
 
90
        group
 
91
    ]
 
92
    log("Adding user {} to group {}".format(username, group))
 
93
    subprocess.check_call(cmd)
 
94
 
 
95
 
 
96
def rsync(from_path, to_path, flags='-r', options=None):
 
97
    """Replicate the contents of a path"""
 
98
    options = options or ['--delete', '--executability']
 
99
    cmd = ['/usr/bin/rsync', flags]
 
100
    cmd.extend(options)
 
101
    cmd.append(from_path)
 
102
    cmd.append(to_path)
 
103
    log(" ".join(cmd))
 
104
    return subprocess.check_output(cmd).strip()
 
105
 
 
106
 
 
107
def symlink(source, destination):
 
108
    """Create a symbolic link"""
 
109
    log("Symlinking {} as {}".format(source, destination))
 
110
    cmd = [
 
111
        'ln',
 
112
        '-sf',
 
113
        source,
 
114
        destination,
 
115
    ]
 
116
    subprocess.check_call(cmd)
 
117
 
 
118
 
 
119
def mkdir(path, owner='root', group='root', perms=0555, force=False):
 
120
    """Create a directory"""
 
121
    log("Making dir {} {}:{} {:o}".format(path, owner, group,
 
122
                                          perms))
 
123
    uid = pwd.getpwnam(owner).pw_uid
 
124
    gid = grp.getgrnam(group).gr_gid
 
125
    realpath = os.path.abspath(path)
 
126
    if os.path.exists(realpath):
 
127
        if force and not os.path.isdir(realpath):
 
128
            log("Removing non-directory file {} prior to mkdir()".format(path))
 
129
            os.unlink(realpath)
 
130
    else:
 
131
        os.makedirs(realpath, perms)
 
132
    os.chown(realpath, uid, gid)
 
133
 
 
134
 
 
135
def write_file(path, content, owner='root', group='root', perms=0444):
 
136
    """Create or overwrite a file with the contents of a string"""
 
137
    log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
 
138
    uid = pwd.getpwnam(owner).pw_uid
 
139
    gid = grp.getgrnam(group).gr_gid
 
140
    with open(path, 'w') as target:
 
141
        os.fchown(target.fileno(), uid, gid)
 
142
        os.fchmod(target.fileno(), perms)
 
143
        target.write(content)
 
144
 
 
145
 
 
146
def mount(device, mountpoint, options=None, persist=False):
 
147
    """Mount a filesystem at a particular mountpoint"""
 
148
    cmd_args = ['mount']
 
149
    if options is not None:
 
150
        cmd_args.extend(['-o', options])
 
151
    cmd_args.extend([device, mountpoint])
 
152
    try:
 
153
        subprocess.check_output(cmd_args)
 
154
    except subprocess.CalledProcessError, e:
 
155
        log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
 
156
        return False
 
157
    if persist:
 
158
        # TODO: update fstab
 
159
        pass
 
160
    return True
 
161
 
 
162
 
 
163
def umount(mountpoint, persist=False):
 
164
    """Unmount a filesystem"""
 
165
    cmd_args = ['umount', mountpoint]
 
166
    try:
 
167
        subprocess.check_output(cmd_args)
 
168
    except subprocess.CalledProcessError, e:
 
169
        log('Error unmounting {}\n{}'.format(mountpoint, e.output))
 
170
        return False
 
171
    if persist:
 
172
        # TODO: update fstab
 
173
        pass
 
174
    return True
 
175
 
 
176
 
 
177
def mounts():
 
178
    """Get a list of all mounted volumes as [[mountpoint,device],[...]]"""
 
179
    with open('/proc/mounts') as f:
 
180
        # [['/mount/point','/dev/path'],[...]]
 
181
        system_mounts = [m[1::-1] for m in [l.strip().split()
 
182
                                            for l in f.readlines()]]
 
183
    return system_mounts
 
184
 
 
185
 
 
186
def file_hash(path):
 
187
    """Generate a md5 hash of the contents of 'path' or None if not found """
 
188
    if os.path.exists(path):
 
189
        h = hashlib.md5()
 
190
        with open(path, 'r') as source:
 
191
            h.update(source.read())  # IGNORE:E1101 - it does have update
 
192
        return h.hexdigest()
 
193
    else:
 
194
        return None
 
195
 
 
196
 
 
197
def restart_on_change(restart_map):
 
198
    """Restart services based on configuration files changing
 
199
 
 
200
    This function is used a decorator, for example
 
201
 
 
202
        @restart_on_change({
 
203
            '/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
 
204
            })
 
205
        def ceph_client_changed():
 
206
            ...
 
207
 
 
208
    In this example, the cinder-api and cinder-volume services
 
209
    would be restarted if /etc/ceph/ceph.conf is changed by the
 
210
    ceph_client_changed function.
 
211
    """
 
212
    def wrap(f):
 
213
        def wrapped_f(*args):
 
214
            checksums = {}
 
215
            for path in restart_map:
 
216
                checksums[path] = file_hash(path)
 
217
            f(*args)
 
218
            restarts = []
 
219
            for path in restart_map:
 
220
                if checksums[path] != file_hash(path):
 
221
                    restarts += restart_map[path]
 
222
            for service_name in list(OrderedDict.fromkeys(restarts)):
 
223
                service('restart', service_name)
 
224
        return wrapped_f
 
225
    return wrap
 
226
 
 
227
 
 
228
def lsb_release():
 
229
    """Return /etc/lsb-release in a dict"""
 
230
    d = {}
 
231
    with open('/etc/lsb-release', 'r') as lsb:
 
232
        for l in lsb:
 
233
            k, v = l.split('=')
 
234
            d[k.strip()] = v.strip()
 
235
    return d
 
236
 
 
237
 
 
238
def pwgen(length=None):
 
239
    """Generate a random pasword."""
 
240
    if length is None:
 
241
        length = random.choice(range(35, 45))
 
242
    alphanumeric_chars = [
 
243
        l for l in (string.letters + string.digits)
 
244
        if l not in 'l0QD1vAEIOUaeiou']
 
245
    random_chars = [
 
246
        random.choice(alphanumeric_chars) for _ in range(length)]
 
247
    return(''.join(random_chars))
 
248
 
 
249
 
 
250
def list_nics(nic_type):
 
251
    '''Return a list of nics of given type(s)'''
 
252
    if isinstance(nic_type, basestring):
 
253
        int_types = [nic_type]
 
254
    else:
 
255
        int_types = nic_type
 
256
    interfaces = []
 
257
    for int_type in int_types:
 
258
        cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
 
259
        ip_output = subprocess.check_output(cmd).split('\n')
 
260
        ip_output = (line for line in ip_output if line)
 
261
        for line in ip_output:
 
262
            if line.split()[1].startswith(int_type):
 
263
                interfaces.append(line.split()[1].replace(":", ""))
 
264
    return interfaces
 
265
 
 
266
 
 
267
def set_nic_mtu(nic, mtu):
 
268
    '''Set MTU on a network interface'''
 
269
    cmd = ['ip', 'link', 'set', nic, 'mtu', mtu]
 
270
    subprocess.check_call(cmd)
 
271
 
 
272
 
 
273
def get_nic_mtu(nic):
 
274
    cmd = ['ip', 'addr', 'show', nic]
 
275
    ip_output = subprocess.check_output(cmd).split('\n')
 
276
    mtu = ""
 
277
    for line in ip_output:
 
278
        words = line.split()
 
279
        if 'mtu' in words:
 
280
            mtu = words[words.index("mtu") + 1]
 
281
    return mtu
 
282
 
 
283
 
 
284
def get_nic_hwaddr(nic):
 
285
    cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
 
286
    ip_output = subprocess.check_output(cmd)
 
287
    hwaddr = ""
 
288
    words = ip_output.split()
 
289
    if 'link/ether' in words:
 
290
        hwaddr = words[words.index('link/ether') + 1]
 
291
    return hwaddr