63
65
return service_result
66
def service_pause(service_name, init_dir=None):
68
def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d"):
67
69
"""Pause a system service.
69
71
Stop it, and prevent it from starting again at boot."""
71
init_dir = "/etc/init"
72
stopped = service_stop(service_name)
73
# XXX: Support systemd too
74
override_path = os.path.join(
75
init_dir, '{}.conf.override'.format(service_name))
76
with open(override_path, 'w') as fh:
73
if service_running(service_name):
74
stopped = service_stop(service_name)
75
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
76
sysv_file = os.path.join(initd_dir, service_name)
78
service('disable', service_name)
79
elif os.path.exists(upstart_file):
80
override_path = os.path.join(
81
init_dir, '{}.override'.format(service_name))
82
with open(override_path, 'w') as fh:
84
elif os.path.exists(sysv_file):
85
subprocess.check_call(["update-rc.d", service_name, "disable"])
88
"Unable to detect {0} as SystemD, Upstart {1} or"
90
service_name, upstart_file, sysv_file))
81
def service_resume(service_name, init_dir=None):
94
def service_resume(service_name, init_dir="/etc/init",
95
initd_dir="/etc/init.d"):
82
96
"""Resume a system service.
84
98
Reenable starting again at boot. Start the service"""
85
# XXX: Support systemd too
87
init_dir = "/etc/init"
88
override_path = os.path.join(
89
init_dir, '{}.conf.override'.format(service_name))
90
if os.path.exists(override_path):
91
os.unlink(override_path)
92
started = service_start(service_name)
99
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
100
sysv_file = os.path.join(initd_dir, service_name)
101
if init_is_systemd():
102
service('enable', service_name)
103
elif os.path.exists(upstart_file):
104
override_path = os.path.join(
105
init_dir, '{}.override'.format(service_name))
106
if os.path.exists(override_path):
107
os.unlink(override_path)
108
elif os.path.exists(sysv_file):
109
subprocess.check_call(["update-rc.d", service_name, "enable"])
112
"Unable to detect {0} as SystemD, Upstart {1} or"
114
service_name, upstart_file, sysv_file))
116
started = service_running(service_name)
118
started = service_start(service_name)
96
122
def service(action, service_name):
97
123
"""Control a system service"""
98
cmd = ['service', service_name, action]
124
if init_is_systemd():
125
cmd = ['systemctl', action, service_name]
127
cmd = ['service', service_name, action]
99
128
return subprocess.call(cmd) == 0
102
def service_running(service):
131
def service_running(service_name):
103
132
"""Determine whether a system service is running"""
105
output = subprocess.check_output(
106
['service', service, 'status'],
107
stderr=subprocess.STDOUT).decode('UTF-8')
108
except subprocess.CalledProcessError:
133
if init_is_systemd():
134
return service('is-active', service_name)
111
if ("start/running" in output or "is running" in output):
137
output = subprocess.check_output(
138
['service', service_name, 'status'],
139
stderr=subprocess.STDOUT).decode('UTF-8')
140
except subprocess.CalledProcessError:
143
if ("start/running" in output or "is running" in output or
144
"up and running" in output):
117
150
def service_available(service_name):
129
def adduser(username, password=None, shell='/bin/bash', system_user=False):
130
"""Add a user to the system"""
162
SYSTEMD_SYSTEM = '/run/systemd/system'
165
def init_is_systemd():
166
"""Return True if the host system uses systemd, False otherwise."""
167
return os.path.isdir(SYSTEMD_SYSTEM)
170
def adduser(username, password=None, shell='/bin/bash', system_user=False,
171
primary_group=None, secondary_groups=None):
172
"""Add a user to the system.
174
Will log but otherwise succeed if the user already exists.
176
:param str username: Username to create
177
:param str password: Password for user; if ``None``, create a system user
178
:param str shell: The default shell for the user
179
:param bool system_user: Whether to create a login or system user
180
:param str primary_group: Primary group for user; defaults to username
181
:param list secondary_groups: Optional list of additional groups
183
:returns: The password database entry struct, as returned by `pwd.getpwnam`
132
186
user_info = pwd.getpwnam(username)
133
187
log('user {0} already exists!'.format(username))
280
352
return system_mounts
355
def fstab_mount(mountpoint):
356
"""Mount filesystem using fstab"""
357
cmd_args = ['mount', mountpoint]
359
subprocess.check_output(cmd_args)
360
except subprocess.CalledProcessError as e:
361
log('Error unmounting {}\n{}'.format(mountpoint, e.output))
283
366
def file_hash(path, hash_type='md5'):
285
Generate a hash checksum of the contents of 'path' or None if not found.
367
"""Generate a hash checksum of the contents of 'path' or None if not found.
287
369
:param str hash_type: Any hash alrgorithm supported by :mod:`hashlib`,
288
370
such as md5, sha1, sha256, sha512, etc.
349
430
restarted if any file matching the pattern got changed, created
350
431
or removed. Standard wildcards are supported, see documentation
351
432
for the 'glob' module for more information.
434
@param restart_map: {path_file_name: [service_name, ...]
435
@param stopstart: DEFAULT false; whether to stop, start OR restart
436
@returns result from decorated function
354
440
def wrapped_f(*args, **kwargs):
355
checksums = {path: path_hash(path) for path in restart_map}
358
for path in restart_map:
359
if path_hash(path) != checksums[path]:
360
restarts += restart_map[path]
361
services_list = list(OrderedDict.fromkeys(restarts))
363
for service_name in services_list:
364
service('restart', service_name)
366
for action in ['stop', 'start']:
367
for service_name in services_list:
368
service(action, service_name)
441
return restart_on_change_helper(
442
(lambda: f(*args, **kwargs)), restart_map, stopstart)
447
def restart_on_change_helper(lambda_f, restart_map, stopstart=False):
448
"""Helper function to perform the restart_on_change function.
450
This is provided for decorators to restart services if files described
451
in the restart_map have changed after an invocation of lambda_f().
453
@param lambda_f: function to call.
454
@param restart_map: {file: [service, ...]}
455
@param stopstart: whether to stop, start or restart a service
456
@returns result of lambda_f()
458
checksums = {path: path_hash(path) for path in restart_map}
460
# create a list of lists of the services to restart
461
restarts = [restart_map[path]
462
for path in restart_map
463
if path_hash(path) != checksums[path]]
464
# create a flat list of ordered services without duplicates from lists
465
services_list = list(OrderedDict.fromkeys(itertools.chain(*restarts)))
467
actions = ('stop', 'start') if stopstart else ('restart',)
468
for action in actions:
469
for service_name in services_list:
470
service(action, service_name)
373
474
def lsb_release():
374
475
"""Return /etc/lsb-release in a dict"""
396
497
return(''.join(random_chars))
399
def list_nics(nic_type):
400
'''Return a list of nics of given type(s)'''
500
def is_phy_iface(interface):
501
"""Returns True if interface is not virtual, otherwise False."""
503
sys_net = '/sys/class/net'
504
if os.path.isdir(sys_net):
505
for iface in glob.glob(os.path.join(sys_net, '*')):
506
if '/virtual/' in os.path.realpath(iface):
509
if interface == os.path.basename(iface):
515
def get_bond_master(interface):
516
"""Returns bond master if interface is bond slave otherwise None.
518
NOTE: the provided interface is expected to be physical
521
iface_path = '/sys/class/net/%s' % (interface)
522
if os.path.exists(iface_path):
523
if '/virtual/' in os.path.realpath(iface_path):
526
master = os.path.join(iface_path, 'master')
527
if os.path.exists(master):
528
master = os.path.realpath(master)
529
# make sure it is a bond master
530
if os.path.exists(os.path.join(master, 'bonding')):
531
return os.path.basename(master)
536
def list_nics(nic_type=None):
537
"""Return a list of nics of given type(s)"""
401
538
if isinstance(nic_type, six.string_types):
402
539
int_types = [nic_type]
404
541
int_types = nic_type
406
for int_type in int_types:
407
cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
545
for int_type in int_types:
546
cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
547
ip_output = subprocess.check_output(cmd).decode('UTF-8')
548
ip_output = ip_output.split('\n')
549
ip_output = (line for line in ip_output if line)
550
for line in ip_output:
551
if line.split()[1].startswith(int_type):
552
matched = re.search('.*: (' + int_type +
553
r'[0-9]+\.[0-9]+)@.*', line)
555
iface = matched.groups()[0]
557
iface = line.split()[1].replace(":", "")
559
if iface not in interfaces:
560
interfaces.append(iface)
408
563
ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
409
ip_output = (line for line in ip_output if line)
564
ip_output = (line.strip() for line in ip_output if line)
566
key = re.compile('^[0-9]+:\s+(.+):')
410
567
for line in ip_output:
411
if line.split()[1].startswith(int_type):
412
matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line)
414
interface = matched.groups()[0]
416
interface = line.split()[1].replace(":", "")
417
interfaces.append(interface)
568
matched = re.search(key, line)
570
iface = matched.group(1)
571
iface = iface.partition("@")[0]
572
if iface not in interfaces:
573
interfaces.append(iface)
419
575
return interfaces
422
578
def set_nic_mtu(nic, mtu):
423
'''Set MTU on a network interface'''
579
"""Set the Maximum Transmission Unit (MTU) on a network interface."""
424
580
cmd = ['ip', 'link', 'set', nic, 'mtu', mtu]
425
581
subprocess.check_call(cmd)
428
584
def get_nic_mtu(nic):
585
"""Return the Maximum Transmission Unit (MTU) for a network interface."""
429
586
cmd = ['ip', 'addr', 'show', nic]
430
587
ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
627
def chdir(directory):
628
"""Change the current working directory to a different directory for a code
629
block and return the previous directory after the block exits. Useful to
630
run commands from a specificed directory.
632
:param str directory: The directory path to change to for this context.
470
634
cur = os.getcwd()
636
yield os.chdir(directory)
477
def chownr(path, owner, group, follow_links=True):
641
def chownr(path, owner, group, follow_links=True, chowntopdir=False):
642
"""Recursively change user and group ownership of files and directories
643
in given path. Doesn't chown path itself by default, only its children.
645
:param str path: The string path to start changing ownership.
646
:param str owner: The owner string to use when looking up the uid.
647
:param str group: The group string to use when looking up the gid.
648
:param bool follow_links: Also Chown links if True
649
:param bool chowntopdir: Also chown path itself if True
478
651
uid = pwd.getpwnam(owner).pw_uid
479
652
gid = grp.getgrnam(group).gr_gid
493
670
def lchownr(path, owner, group):
671
"""Recursively change user and group ownership of files and directories
672
in a given path, not following symbolic links. See the documentation for
673
'os.lchown' for more information.
675
:param str path: The string path to start changing ownership.
676
:param str owner: The owner string to use when looking up the uid.
677
:param str group: The group string to use when looking up the gid.
494
679
chownr(path, owner, group, follow_links=False)
683
"""The total amount of system RAM in bytes.
685
This is what is reported by the OS, and may be overcommitted when
686
there are multiple containers hosted on the same machine.
688
with open('/proc/meminfo', 'r') as f:
689
for line in f.readlines():
691
key, value, unit = line.split()
692
if key == 'MemTotal:':
693
assert unit == 'kB', 'Unknown unit'
694
return int(value) * 1024 # Classic, not KiB.
695
raise NotImplementedError()