1
"""Tools for working with the host system"""
2
# Copyright 2012 Canonical Ltd.
5
# Nick Moffitt <nick.moffitt@canonical.com>
6
# Matthew Wedgwood <matthew.wedgwood@canonical.com>
16
from collections import OrderedDict
18
from hookenv import log
21
def service_start(service_name):
22
return service('start', service_name)
25
def service_stop(service_name):
26
return service('stop', service_name)
29
def service_restart(service_name):
30
return service('restart', service_name)
33
def service_reload(service_name, restart_on_failure=False):
34
service_result = service('reload', service_name)
35
if not service_result and restart_on_failure:
36
service_result = service('restart', service_name)
40
def service(action, service_name):
41
cmd = ['service', service_name, action]
42
return subprocess.call(cmd) == 0
45
def service_running(service):
47
output = subprocess.check_output(['service', service, 'status'])
48
except subprocess.CalledProcessError:
51
if ("start/running" in output or "is running" in output):
57
def adduser(username, password=None, shell='/bin/bash', system_user=False):
60
user_info = pwd.getpwnam(username)
61
log('user {0} already exists!'.format(username))
63
log('creating user {0}'.format(username))
65
if system_user or password is None:
66
cmd.append('--system')
71
'--password', password,
74
subprocess.check_call(cmd)
75
user_info = pwd.getpwnam(username)
79
def add_user_to_group(username, group):
80
"""Add a user to a group"""
86
log("Adding user {} to group {}".format(username, group))
87
subprocess.check_call(cmd)
90
def rsync(from_path, to_path, flags='-r', options=None):
91
"""Replicate the contents of a path"""
92
options = options or ['--delete', '--executability']
93
cmd = ['/usr/bin/rsync', flags]
98
return subprocess.check_output(cmd).strip()
101
def symlink(source, destination):
102
"""Create a symbolic link"""
103
log("Symlinking {} as {}".format(source, destination))
110
subprocess.check_call(cmd)
113
def mkdir(path, owner='root', group='root', perms=0555, force=False):
114
"""Create a directory"""
115
log("Making dir {} {}:{} {:o}".format(path, owner, group,
117
uid = pwd.getpwnam(owner).pw_uid
118
gid = grp.getgrnam(group).gr_gid
119
realpath = os.path.abspath(path)
120
if os.path.exists(realpath):
121
if force and not os.path.isdir(realpath):
122
log("Removing non-directory file {} prior to mkdir()".format(path))
125
os.makedirs(realpath, perms)
126
os.chown(realpath, uid, gid)
129
def write_file(path, content, owner='root', group='root', perms=0444):
130
"""Create or overwrite a file with the contents of a string"""
131
log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
132
uid = pwd.getpwnam(owner).pw_uid
133
gid = grp.getgrnam(group).gr_gid
134
with open(path, 'w') as target:
135
os.fchown(target.fileno(), uid, gid)
136
os.fchmod(target.fileno(), perms)
137
target.write(content)
140
def mount(device, mountpoint, options=None, persist=False):
141
'''Mount a filesystem'''
143
if options is not None:
144
cmd_args.extend(['-o', options])
145
cmd_args.extend([device, mountpoint])
147
subprocess.check_output(cmd_args)
148
except subprocess.CalledProcessError, e:
149
log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
157
def umount(mountpoint, persist=False):
158
'''Unmount a filesystem'''
159
cmd_args = ['umount', mountpoint]
161
subprocess.check_output(cmd_args)
162
except subprocess.CalledProcessError, e:
163
log('Error unmounting {}\n{}'.format(mountpoint, e.output))
172
'''List of all mounted volumes as [[mountpoint,device],[...]]'''
173
with open('/proc/mounts') as f:
174
# [['/mount/point','/dev/path'],[...]]
175
system_mounts = [m[1::-1] for m in [l.strip().split()
176
for l in f.readlines()]]
181
''' Generate a md5 hash of the contents of 'path' or None if not found '''
182
if os.path.exists(path):
184
with open(path, 'r') as source:
185
h.update(source.read()) # IGNORE:E1101 - it does have update
191
def restart_on_change(restart_map):
192
''' Restart services based on configuration files changing
194
This function is used a decorator, for example
197
'/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
199
def ceph_client_changed():
202
In this example, the cinder-api and cinder-volume services
203
would be restarted if /etc/ceph/ceph.conf is changed by the
204
ceph_client_changed function.
207
def wrapped_f(*args):
209
for path in restart_map:
210
checksums[path] = file_hash(path)
213
for path in restart_map:
214
if checksums[path] != file_hash(path):
215
restarts += restart_map[path]
216
for service_name in list(OrderedDict.fromkeys(restarts)):
217
service('restart', service_name)
223
'''Return /etc/lsb-release in a dict'''
225
with open('/etc/lsb-release', 'r') as lsb:
228
d[k.strip()] = v.strip()
232
def pwgen(length=None):
233
'''Generate a random pasword.'''
235
length = random.choice(range(35, 45))
236
alphanumeric_chars = [
237
l for l in (string.letters + string.digits)
238
if l not in 'l0QD1vAEIOUaeiou']
240
random.choice(alphanumeric_chars) for _ in range(length)]
241
return(''.join(random_chars))