67
67
"""Pause a system service.
69
69
Stop it, and prevent it from starting again at boot."""
70
stopped = service_stop(service_name)
71
if service_running(service_name):
72
stopped = service_stop(service_name)
71
73
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
72
74
sysv_file = os.path.join(initd_dir, service_name)
73
if os.path.exists(upstart_file):
76
service('disable', service_name)
77
elif os.path.exists(upstart_file):
74
78
override_path = os.path.join(
75
79
init_dir, '{}.override'.format(service_name))
76
80
with open(override_path, 'w') as fh:
78
82
elif os.path.exists(sysv_file):
79
83
subprocess.check_call(["update-rc.d", service_name, "disable"])
81
# XXX: Support SystemD too
83
"Unable to detect {0} as either Upstart {1} or SysV {2}".format(
86
"Unable to detect {0} as SystemD, Upstart {1} or"
84
88
service_name, upstart_file, sysv_file))
92
96
Reenable starting again at boot. Start the service"""
93
97
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
94
98
sysv_file = os.path.join(initd_dir, service_name)
95
if os.path.exists(upstart_file):
100
service('enable', service_name)
101
elif os.path.exists(upstart_file):
96
102
override_path = os.path.join(
97
103
init_dir, '{}.override'.format(service_name))
98
104
if os.path.exists(override_path):
100
106
elif os.path.exists(sysv_file):
101
107
subprocess.check_call(["update-rc.d", service_name, "enable"])
103
# XXX: Support SystemD too
104
109
raise ValueError(
105
"Unable to detect {0} as either Upstart {1} or SysV {2}".format(
110
"Unable to detect {0} as SystemD, Upstart {1} or"
106
112
service_name, upstart_file, sysv_file))
108
started = service_start(service_name)
114
started = service_running(service_name)
116
started = service_start(service_name)
112
120
def service(action, service_name):
113
121
"""Control a system service"""
114
cmd = ['service', service_name, action]
122
if init_is_systemd():
123
cmd = ['systemctl', action, service_name]
125
cmd = ['service', service_name, action]
115
126
return subprocess.call(cmd) == 0
118
def service_running(service):
129
def service_running(service_name):
119
130
"""Determine whether a system service is running"""
121
output = subprocess.check_output(
122
['service', service, 'status'],
123
stderr=subprocess.STDOUT).decode('UTF-8')
124
except subprocess.CalledProcessError:
131
if init_is_systemd():
132
return service('is-active', service_name)
127
if ("start/running" in output or "is running" in output):
135
output = subprocess.check_output(
136
['service', service_name, 'status'],
137
stderr=subprocess.STDOUT).decode('UTF-8')
138
except subprocess.CalledProcessError:
141
if ("start/running" in output or "is running" in output or
142
"up and running" in output):
133
148
def service_available(service_name):
145
def adduser(username, password=None, shell='/bin/bash', system_user=False):
146
"""Add a user to the system"""
160
SYSTEMD_SYSTEM = '/run/systemd/system'
163
def init_is_systemd():
164
"""Return True if the host system uses systemd, False otherwise."""
165
return os.path.isdir(SYSTEMD_SYSTEM)
168
def adduser(username, password=None, shell='/bin/bash', system_user=False,
169
primary_group=None, secondary_groups=None):
170
"""Add a user to the system.
172
Will log but otherwise succeed if the user already exists.
174
:param str username: Username to create
175
:param str password: Password for user; if ``None``, create a system user
176
:param str shell: The default shell for the user
177
:param bool system_user: Whether to create a login or system user
178
:param str primary_group: Primary group for user; defaults to username
179
:param list secondary_groups: Optional list of additional groups
181
:returns: The password database entry struct, as returned by `pwd.getpwnam`
148
184
user_info = pwd.getpwnam(username)
149
185
log('user {0} already exists!'.format(username))
158
194
'--shell', shell,
159
195
'--password', password,
197
if not primary_group:
199
grp.getgrnam(username)
200
primary_group = username # avoid "group exists" error
204
cmd.extend(['-g', primary_group])
206
cmd.extend(['-G', ','.join(secondary_groups)])
161
207
cmd.append(username)
162
208
subprocess.check_call(cmd)
163
209
user_info = pwd.getpwnam(username)
257
303
def fstab_remove(mp):
258
"""Remove the given mountpoint entry from /etc/fstab
304
"""Remove the given mountpoint entry from /etc/fstab"""
260
305
return Fstab.remove_by_mountpoint(mp)
263
308
def fstab_add(dev, mp, fs, options=None):
264
"""Adds the given device entry to the /etc/fstab file
309
"""Adds the given device entry to the /etc/fstab file"""
266
310
return Fstab.add(dev, mp, fs, options=options)
320
364
def file_hash(path, hash_type='md5'):
322
Generate a hash checksum of the contents of 'path' or None if not found.
365
"""Generate a hash checksum of the contents of 'path' or None if not found.
324
367
:param str hash_type: Any hash alrgorithm supported by :mod:`hashlib`,
325
368
such as md5, sha1, sha256, sha512, etc.
336
379
def path_hash(path):
338
Generate a hash checksum of all files matching 'path'. Standard wildcards
339
like '*' and '?' are supported, see documentation for the 'glob' module for
380
"""Generate a hash checksum of all files matching 'path'. Standard
381
wildcards like '*' and '?' are supported, see documentation for the 'glob'
382
module for more information.
342
384
:return: dict: A { filename: hash } dictionary for all matched files.
343
385
Empty if none found.
351
393
def check_hash(path, checksum, hash_type='md5'):
353
Validate a file using a cryptographic checksum.
394
"""Validate a file using a cryptographic checksum.
355
396
:param str checksum: Value of the checksum used to validate the file.
356
397
:param str hash_type: Hash algorithm used to generate `checksum`.
514
556
def set_nic_mtu(nic, mtu):
515
'''Set MTU on a network interface'''
557
"""Set the Maximum Transmission Unit (MTU) on a network interface."""
516
558
cmd = ['ip', 'link', 'set', nic, 'mtu', mtu]
517
559
subprocess.check_call(cmd)
520
562
def get_nic_mtu(nic):
563
"""Return the Maximum Transmission Unit (MTU) for a network interface."""
521
564
cmd = ['ip', 'addr', 'show', nic]
522
565
ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
531
574
def get_nic_hwaddr(nic):
575
"""Return the Media Access Control (MAC) for a network interface."""
532
576
cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
533
577
ip_output = subprocess.check_output(cmd).decode('UTF-8')
541
585
def cmp_pkgrevno(package, revno, pkgcache=None):
542
'''Compare supplied revno with the revno of the installed package
586
"""Compare supplied revno with the revno of the installed package
544
588
* 1 => Installed revno is greater than supplied arg
545
589
* 0 => Installed revno is the same as supplied arg
548
592
This function imports apt_cache function from charmhelpers.fetch if
549
593
the pkgcache argument is None. Be sure to add charmhelpers.fetch if
550
594
you call this function, or pass an apt_pkg.Cache() instance.
554
598
from charmhelpers.fetch import apt_cache
605
def chdir(directory):
606
"""Change the current working directory to a different directory for a code
607
block and return the previous directory after the block exits. Useful to
608
run commands from a specificed directory.
610
:param str directory: The directory path to change to for this context.
562
612
cur = os.getcwd()
614
yield os.chdir(directory)
569
619
def chownr(path, owner, group, follow_links=True, chowntopdir=False):
571
Recursively change user and group ownership of files and directories
620
"""Recursively change user and group ownership of files and directories
572
621
in given path. Doesn't chown path itself by default, only its children.
623
:param str path: The string path to start changing ownership.
624
:param str owner: The owner string to use when looking up the uid.
625
:param str group: The group string to use when looking up the gid.
574
626
:param bool follow_links: Also Chown links if True
575
627
:param bool chowntopdir: Also chown path itself if True
596
648
def lchownr(path, owner, group):
649
"""Recursively change user and group ownership of files and directories
650
in a given path, not following symbolic links. See the documentation for
651
'os.lchown' for more information.
653
:param str path: The string path to start changing ownership.
654
:param str owner: The owner string to use when looking up the uid.
655
:param str group: The group string to use when looking up the gid.
597
657
chownr(path, owner, group, follow_links=False)
600
660
def get_total_ram():
601
'''The total amount of system RAM in bytes.
661
"""The total amount of system RAM in bytes.
603
663
This is what is reported by the OS, and may be overcommitted when
604
664
there are multiple containers hosted on the same machine.
606
666
with open('/proc/meminfo', 'r') as f:
607
667
for line in f.readlines():