Merge lp:~kos.tsakalozos/charm-helpers/merge-addusers into lp:~bigdata-dev/charm-helpers/framework

Proposed by Konstantinos Tsakalozos
Status: Merged
Merged at revision: 445
Proposed branch: lp:~kos.tsakalozos/charm-helpers/merge-addusers
Merge into: lp:~bigdata-dev/charm-helpers/framework
Diff against target: 224 lines (+141/-7)
1 file modified
charmhelpers/core/host.py (+141/-7)
To merge this branch: bzr merge lp:~kos.tsakalozos/charm-helpers/merge-addusers
Reviewer Review Type Date Requested Status
Kevin W Monroe Approve
Cory Johns Pending
Review via email: mp+279766@code.launchpad.net

Description of the change

Merge of host.py upstream with our bigdata-dev fork (host.py includes the adduser method).

To post a comment you must log in.
Revision history for this message
Kevin W Monroe (kwmonroe) wrote :

LGTM, merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/core/host.py'
2--- charmhelpers/core/host.py 2015-05-13 20:47:37 +0000
3+++ charmhelpers/core/host.py 2015-12-07 12:51:05 +0000
4@@ -24,6 +24,7 @@
5 import os
6 import re
7 import pwd
8+import glob
9 import grp
10 import random
11 import string
12@@ -62,6 +63,56 @@
13 return service_result
14
15
16+def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d"):
17+ """Pause a system service.
18+
19+ Stop it, and prevent it from starting again at boot."""
20+ stopped = True
21+ if service_running(service_name):
22+ stopped = service_stop(service_name)
23+ upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
24+ sysv_file = os.path.join(initd_dir, service_name)
25+ if os.path.exists(upstart_file):
26+ override_path = os.path.join(
27+ init_dir, '{}.override'.format(service_name))
28+ with open(override_path, 'w') as fh:
29+ fh.write("manual\n")
30+ elif os.path.exists(sysv_file):
31+ subprocess.check_call(["update-rc.d", service_name, "disable"])
32+ else:
33+ # XXX: Support SystemD too
34+ raise ValueError(
35+ "Unable to detect {0} as either Upstart {1} or SysV {2}".format(
36+ service_name, upstart_file, sysv_file))
37+ return stopped
38+
39+
40+def service_resume(service_name, init_dir="/etc/init",
41+ initd_dir="/etc/init.d"):
42+ """Resume a system service.
43+
44+ Reenable starting again at boot. Start the service"""
45+ upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
46+ sysv_file = os.path.join(initd_dir, service_name)
47+ if os.path.exists(upstart_file):
48+ override_path = os.path.join(
49+ init_dir, '{}.override'.format(service_name))
50+ if os.path.exists(override_path):
51+ os.unlink(override_path)
52+ elif os.path.exists(sysv_file):
53+ subprocess.check_call(["update-rc.d", service_name, "enable"])
54+ else:
55+ # XXX: Support SystemD too
56+ raise ValueError(
57+ "Unable to detect {0} as either Upstart {1} or SysV {2}".format(
58+ service_name, upstart_file, sysv_file))
59+
60+ started = service_running(service_name)
61+ if not started:
62+ started = service_start(service_name)
63+ return started
64+
65+
66 def service(action, service_name):
67 """Control a system service"""
68 cmd = ['service', service_name, action]
69@@ -95,8 +146,22 @@
70 return True
71
72
73-def adduser(username, password=None, shell='/bin/bash', system_user=False, group=None, groups=None):
74- """Add a user to the system"""
75+def adduser(username, password=None, shell='/bin/bash', system_user=False,
76+ primary_group=None, secondary_groups=None):
77+ """
78+ Add a user to the system.
79+
80+ Will log but otherwise succeed if the user already exists.
81+
82+ :param str username: Username to create
83+ :param str password: Password for user; if ``None``, create a system user
84+ :param str shell: The default shell for the user
85+ :param bool system_user: Whether to create a login or system user
86+ :param str primary_group: Primary group for user; defaults to their username
87+ :param list secondary_groups: Optional list of additional groups
88+
89+ :returns: The password database entry struct, as returned by `pwd.getpwnam`
90+ """
91 try:
92 user_info = pwd.getpwnam(username)
93 log('user {0} already exists!'.format(username))
94@@ -111,16 +176,32 @@
95 '--shell', shell,
96 '--password', password,
97 ])
98- if group:
99- cmd.extend(['-g', group])
100- if groups:
101- cmd.extend(['-G', ','.join(groups)])
102+ if not primary_group:
103+ try:
104+ grp.getgrnam(username)
105+ primary_group = username # avoid "group exists" error
106+ except KeyError:
107+ pass
108+ if primary_group:
109+ cmd.extend(['-g', primary_group])
110+ if secondary_groups:
111+ cmd.extend(['-G', ','.join(secondary_groups)])
112 cmd.append(username)
113 subprocess.check_call(cmd)
114 user_info = pwd.getpwnam(username)
115 return user_info
116
117
118+def user_exists(username):
119+ """Check if a user exists"""
120+ try:
121+ pwd.getpwnam(username)
122+ user_exists = True
123+ except KeyError:
124+ user_exists = False
125+ return user_exists
126+
127+
128 def add_group(group_name, system_group=False):
129 """Add a group to the system"""
130 try:
131@@ -262,6 +343,17 @@
132 return system_mounts
133
134
135+def fstab_mount(mountpoint):
136+ """Mount filesystem using fstab"""
137+ cmd_args = ['mount', mountpoint]
138+ try:
139+ subprocess.check_output(cmd_args)
140+ except subprocess.CalledProcessError as e:
141+ log('Error unmounting {}\n{}'.format(mountpoint, e.output))
142+ return False
143+ return True
144+
145+
146 def file_hash(path, hash_type='md5'):
147 """
148 Generate a hash checksum of the contents of 'path' or None if not found.
149@@ -278,6 +370,21 @@
150 return None
151
152
153+def path_hash(path):
154+ """
155+ Generate a hash checksum of all files matching 'path'. Standard wildcards
156+ like '*' and '?' are supported, see documentation for the 'glob' module for
157+ more information.
158+
159+ :return: dict: A { filename: hash } dictionary for all matched files.
160+ Empty if none found.
161+ """
162+ return {
163+ filename: file_hash(filename)
164+ for filename in glob.iglob(path)
165+ }
166+
167+
168 def check_hash(path, checksum, hash_type='md5'):
169 """
170 Validate a file using a cryptographic checksum.
171@@ -367,6 +474,7 @@
172 int_types = [nic_type]
173 else:
174 int_types = nic_type
175+
176 interfaces = []
177 for int_type in int_types:
178 cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
179@@ -439,7 +547,14 @@
180 os.chdir(cur)
181
182
183-def chownr(path, owner, group, follow_links=True):
184+def chownr(path, owner, group, follow_links=True, chowntopdir=False):
185+ """
186+ Recursively change user and group ownership of files and directories
187+ in given path. Doesn't chown path itself by default, only its children.
188+
189+ :param bool follow_links: Also Chown links if True
190+ :param bool chowntopdir: Also chown path itself if True
191+ """
192 uid = pwd.getpwnam(owner).pw_uid
193 gid = grp.getgrnam(group).gr_gid
194 if follow_links:
195@@ -447,6 +562,10 @@
196 else:
197 chown = os.lchown
198
199+ if chowntopdir:
200+ broken_symlink = os.path.lexists(path) and not os.path.exists(path)
201+ if not broken_symlink:
202+ chown(path, uid, gid)
203 for root, dirs, files in os.walk(path):
204 for name in dirs + files:
205 full = os.path.join(root, name)
206@@ -461,3 +580,18 @@
207
208 def cpu_arch():
209 return subprocess.check_output(['uname', '-p']).strip()
210+
211+def get_total_ram():
212+ '''The total amount of system RAM in bytes.
213+
214+ This is what is reported by the OS, and may be overcommitted when
215+ there are multiple containers hosted on the same machine.
216+ '''
217+ with open('/proc/meminfo', 'r') as f:
218+ for line in f.readlines():
219+ if line:
220+ key, value, unit = line.split()
221+ if key == 'MemTotal:':
222+ assert unit == 'kB', 'Unknown unit'
223+ return int(value) * 1024 # Classic, not KiB.
224+ raise NotImplementedError()

Subscribers

People subscribed via source and target branches