~brad-marshall/charms/trusty/ntp/add-nrpe-checks

« back to all changes in this revision

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

  • Committer: Brad Marshall
  • Date: 2015-03-17 04:10:30 UTC
  • Revision ID: brad.marshall@canonical.com-20150317041030-l2jq6ait6qtd642f
[bradm] Sync charmhelpers, add nrpe checks

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014-2015 Canonical Limited.
 
2
#
 
3
# This file is part of charm-helpers.
 
4
#
 
5
# charm-helpers is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU Lesser General Public License version 3 as
 
7
# published by the Free Software Foundation.
 
8
#
 
9
# charm-helpers is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public License
 
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
1
17
"""Tools for working with the host system"""
2
18
# Copyright 2012 Canonical Ltd.
3
19
#
6
22
#  Matthew Wedgwood <matthew.wedgwood@canonical.com>
7
23
 
8
24
import os
 
25
import re
9
26
import pwd
10
27
import grp
11
28
import random
12
29
import string
13
30
import subprocess
14
31
import hashlib
15
 
import shutil
16
32
from contextlib import contextmanager
17
 
 
18
33
from collections import OrderedDict
19
34
 
20
 
from hookenv import log
21
 
from fstab import Fstab
 
35
import six
 
36
 
 
37
from .hookenv import log
 
38
from .fstab import Fstab
22
39
 
23
40
 
24
41
def service_start(service_name):
54
71
def service_running(service):
55
72
    """Determine whether a system service is running"""
56
73
    try:
57
 
        output = subprocess.check_output(['service', service, 'status'], stderr=subprocess.STDOUT)
 
74
        output = subprocess.check_output(
 
75
            ['service', service, 'status'],
 
76
            stderr=subprocess.STDOUT).decode('UTF-8')
58
77
    except subprocess.CalledProcessError:
59
78
        return False
60
79
    else:
67
86
def service_available(service_name):
68
87
    """Determine whether a system service is available"""
69
88
    try:
70
 
        subprocess.check_output(['service', service_name, 'status'], stderr=subprocess.STDOUT)
71
 
    except subprocess.CalledProcessError:
72
 
        return False
 
89
        subprocess.check_output(
 
90
            ['service', service_name, 'status'],
 
91
            stderr=subprocess.STDOUT).decode('UTF-8')
 
92
    except subprocess.CalledProcessError as e:
 
93
        return 'unrecognized service' not in e.output
73
94
    else:
74
95
        return True
75
96
 
96
117
    return user_info
97
118
 
98
119
 
 
120
def add_group(group_name, system_group=False):
 
121
    """Add a group to the system"""
 
122
    try:
 
123
        group_info = grp.getgrnam(group_name)
 
124
        log('group {0} already exists!'.format(group_name))
 
125
    except KeyError:
 
126
        log('creating group {0}'.format(group_name))
 
127
        cmd = ['addgroup']
 
128
        if system_group:
 
129
            cmd.append('--system')
 
130
        else:
 
131
            cmd.extend([
 
132
                '--group',
 
133
            ])
 
134
        cmd.append(group_name)
 
135
        subprocess.check_call(cmd)
 
136
        group_info = grp.getgrnam(group_name)
 
137
    return group_info
 
138
 
 
139
 
99
140
def add_user_to_group(username, group):
100
141
    """Add a user to a group"""
101
142
    cmd = [
115
156
    cmd.append(from_path)
116
157
    cmd.append(to_path)
117
158
    log(" ".join(cmd))
118
 
    return subprocess.check_output(cmd).strip()
 
159
    return subprocess.check_output(cmd).decode('UTF-8').strip()
119
160
 
120
161
 
121
162
def symlink(source, destination):
130
171
    subprocess.check_call(cmd)
131
172
 
132
173
 
133
 
def mkdir(path, owner='root', group='root', perms=0555, force=False):
 
174
def mkdir(path, owner='root', group='root', perms=0o555, force=False):
134
175
    """Create a directory"""
135
176
    log("Making dir {} {}:{} {:o}".format(path, owner, group,
136
177
                                          perms))
137
178
    uid = pwd.getpwnam(owner).pw_uid
138
179
    gid = grp.getgrnam(group).gr_gid
139
180
    realpath = os.path.abspath(path)
140
 
    if os.path.exists(realpath):
141
 
        if force and not os.path.isdir(realpath):
 
181
    path_exists = os.path.exists(realpath)
 
182
    if path_exists and force:
 
183
        if not os.path.isdir(realpath):
142
184
            log("Removing non-directory file {} prior to mkdir()".format(path))
143
185
            os.unlink(realpath)
144
 
    else:
 
186
            os.makedirs(realpath, perms)
 
187
    elif not path_exists:
145
188
        os.makedirs(realpath, perms)
146
189
    os.chown(realpath, uid, gid)
147
 
 
148
 
 
149
 
def write_file(path, content, owner='root', group='root', perms=0444):
150
 
    """Create or overwrite a file with the contents of a string"""
 
190
    os.chmod(realpath, perms)
 
191
 
 
192
 
 
193
def write_file(path, content, owner='root', group='root', perms=0o444):
 
194
    """Create or overwrite a file with the contents of a byte string."""
151
195
    log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
152
196
    uid = pwd.getpwnam(owner).pw_uid
153
197
    gid = grp.getgrnam(group).gr_gid
154
 
    with open(path, 'w') as target:
 
198
    with open(path, 'wb') as target:
155
199
        os.fchown(target.fileno(), uid, gid)
156
200
        os.fchmod(target.fileno(), perms)
157
201
        target.write(content)
177
221
    cmd_args.extend([device, mountpoint])
178
222
    try:
179
223
        subprocess.check_output(cmd_args)
180
 
    except subprocess.CalledProcessError, e:
 
224
    except subprocess.CalledProcessError as e:
181
225
        log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
182
226
        return False
183
227
 
191
235
    cmd_args = ['umount', mountpoint]
192
236
    try:
193
237
        subprocess.check_output(cmd_args)
194
 
    except subprocess.CalledProcessError, e:
 
238
    except subprocess.CalledProcessError as e:
195
239
        log('Error unmounting {}\n{}'.format(mountpoint, e.output))
196
240
        return False
197
241
 
209
253
    return system_mounts
210
254
 
211
255
 
212
 
def file_hash(path):
213
 
    """Generate a md5 hash of the contents of 'path' or None if not found """
 
256
def file_hash(path, hash_type='md5'):
 
257
    """
 
258
    Generate a hash checksum of the contents of 'path' or None if not found.
 
259
 
 
260
    :param str hash_type: Any hash alrgorithm supported by :mod:`hashlib`,
 
261
                          such as md5, sha1, sha256, sha512, etc.
 
262
    """
214
263
    if os.path.exists(path):
215
 
        h = hashlib.md5()
216
 
        with open(path, 'r') as source:
217
 
            h.update(source.read())  # IGNORE:E1101 - it does have update
 
264
        h = getattr(hashlib, hash_type)()
 
265
        with open(path, 'rb') as source:
 
266
            h.update(source.read())
218
267
        return h.hexdigest()
219
268
    else:
220
269
        return None
221
270
 
222
271
 
 
272
def check_hash(path, checksum, hash_type='md5'):
 
273
    """
 
274
    Validate a file using a cryptographic checksum.
 
275
 
 
276
    :param str checksum: Value of the checksum used to validate the file.
 
277
    :param str hash_type: Hash algorithm used to generate `checksum`.
 
278
        Can be any hash alrgorithm supported by :mod:`hashlib`,
 
279
        such as md5, sha1, sha256, sha512, etc.
 
280
    :raises ChecksumError: If the file fails the checksum
 
281
 
 
282
    """
 
283
    actual_checksum = file_hash(path, hash_type)
 
284
    if checksum != actual_checksum:
 
285
        raise ChecksumError("'%s' != '%s'" % (checksum, actual_checksum))
 
286
 
 
287
 
 
288
class ChecksumError(ValueError):
 
289
    pass
 
290
 
 
291
 
223
292
def restart_on_change(restart_map, stopstart=False):
224
293
    """Restart services based on configuration files changing
225
294
 
236
305
    ceph_client_changed function.
237
306
    """
238
307
    def wrap(f):
239
 
        def wrapped_f(*args):
 
308
        def wrapped_f(*args, **kwargs):
240
309
            checksums = {}
241
310
            for path in restart_map:
242
311
                checksums[path] = file_hash(path)
243
 
            f(*args)
 
312
            f(*args, **kwargs)
244
313
            restarts = []
245
314
            for path in restart_map:
246
315
                if checksums[path] != file_hash(path):
270
339
def pwgen(length=None):
271
340
    """Generate a random pasword."""
272
341
    if length is None:
 
342
        # A random length is ok to use a weak PRNG
273
343
        length = random.choice(range(35, 45))
274
344
    alphanumeric_chars = [
275
 
        l for l in (string.letters + string.digits)
 
345
        l for l in (string.ascii_letters + string.digits)
276
346
        if l not in 'l0QD1vAEIOUaeiou']
 
347
    # Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the
 
348
    # actual password
 
349
    random_generator = random.SystemRandom()
277
350
    random_chars = [
278
 
        random.choice(alphanumeric_chars) for _ in range(length)]
 
351
        random_generator.choice(alphanumeric_chars) for _ in range(length)]
279
352
    return(''.join(random_chars))
280
353
 
281
354
 
282
355
def list_nics(nic_type):
283
356
    '''Return a list of nics of given type(s)'''
284
 
    if isinstance(nic_type, basestring):
 
357
    if isinstance(nic_type, six.string_types):
285
358
        int_types = [nic_type]
286
359
    else:
287
360
        int_types = nic_type
288
361
    interfaces = []
289
362
    for int_type in int_types:
290
363
        cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
291
 
        ip_output = subprocess.check_output(cmd).split('\n')
 
364
        ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
292
365
        ip_output = (line for line in ip_output if line)
293
366
        for line in ip_output:
294
367
            if line.split()[1].startswith(int_type):
295
 
                interfaces.append(line.split()[1].replace(":", ""))
 
368
                matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line)
 
369
                if matched:
 
370
                    interface = matched.groups()[0]
 
371
                else:
 
372
                    interface = line.split()[1].replace(":", "")
 
373
                interfaces.append(interface)
 
374
 
296
375
    return interfaces
297
376
 
298
377
 
304
383
 
305
384
def get_nic_mtu(nic):
306
385
    cmd = ['ip', 'addr', 'show', nic]
307
 
    ip_output = subprocess.check_output(cmd).split('\n')
 
386
    ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
308
387
    mtu = ""
309
388
    for line in ip_output:
310
389
        words = line.split()
315
394
 
316
395
def get_nic_hwaddr(nic):
317
396
    cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
318
 
    ip_output = subprocess.check_output(cmd)
 
397
    ip_output = subprocess.check_output(cmd).decode('UTF-8')
319
398
    hwaddr = ""
320
399
    words = ip_output.split()
321
400
    if 'link/ether' in words:
330
409
    *  0 => Installed revno is the same as supplied arg
331
410
    * -1 => Installed revno is less than supplied arg
332
411
 
 
412
    This function imports apt_cache function from charmhelpers.fetch if
 
413
    the pkgcache argument is None. Be sure to add charmhelpers.fetch if
 
414
    you call this function, or pass an apt_pkg.Cache() instance.
333
415
    '''
334
416
    import apt_pkg
335
 
    from charmhelpers.fetch import apt_cache
336
417
    if not pkgcache:
 
418
        from charmhelpers.fetch import apt_cache
337
419
        pkgcache = apt_cache()
338
420
    pkg = pkgcache[package]
339
421
    return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
348
430
        os.chdir(cur)
349
431
 
350
432
 
351
 
def chownr(path, owner, group):
 
433
def chownr(path, owner, group, follow_links=True):
352
434
    uid = pwd.getpwnam(owner).pw_uid
353
435
    gid = grp.getgrnam(group).gr_gid
 
436
    if follow_links:
 
437
        chown = os.chown
 
438
    else:
 
439
        chown = os.lchown
354
440
 
355
441
    for root, dirs, files in os.walk(path):
356
442
        for name in dirs + files:
357
443
            full = os.path.join(root, name)
358
444
            broken_symlink = os.path.lexists(full) and not os.path.exists(full)
359
445
            if not broken_symlink:
360
 
                os.chown(full, uid, gid)
 
446
                chown(full, uid, gid)
 
447
 
 
448
 
 
449
def lchownr(path, owner, group):
 
450
    chownr(path, owner, group, follow_links=False)