~arosales/charms/trusty/hdp-storm/updated-icon

« back to all changes in this revision

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

  • Committer: amir sanjar
  • Date: 2014-08-28 02:54:14 UTC
  • Revision ID: amir.sanjar@canonical.com-20140828025414-042djeamn4rl3nog
add install

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