Merge lp:~harlowja/cloud-init/ug-cleanup-part-deuce into lp:~cloud-init-dev/cloud-init/trunk
- ug-cleanup-part-deuce
- Merge into trunk
Proposed by
Joshua Harlow
Status: | Merged |
---|---|
Merged at revision: | 677 |
Proposed branch: | lp:~harlowja/cloud-init/ug-cleanup-part-deuce |
Merge into: | lp:~cloud-init-dev/cloud-init/trunk |
Diff against target: |
603 lines (+236/-95) 9 files modified
cloudinit/config/cc_byobu.py (+18/-9) cloudinit/config/cc_set_passwords.py (+7/-10) cloudinit/config/cc_ssh.py (+7/-10) cloudinit/config/cc_ssh_authkey_fingerprints.py (+11/-5) cloudinit/config/cc_ssh_import_id.py (+18/-19) cloudinit/config/cc_users_groups.py (+5/-2) cloudinit/distros/__init__.py (+100/-30) cloudinit/util.py (+30/-0) tests/unittests/test_distros/test_user_data_normalize.py (+40/-10) |
To merge this branch: | bzr merge lp:~harlowja/cloud-init/ug-cleanup-part-deuce |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
cloud-init Commiters | Pending | ||
Review via email: mp+127067@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 673. By Joshua Harlow
-
Add a comment as to why distros can't be
imported without being renamed due to
previous usage of the attribute 'distros' - 674. By Joshua Harlow
-
Make byobu more tolerant of the user not being
located and warn when it is not found + only
run the shell command when actual contents
exist to run. - 675. By Joshua Harlow
-
Sync with head and fix conflicts.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'cloudinit/config/cc_byobu.py' | |||
2 | --- cloudinit/config/cc_byobu.py 2012-06-23 03:58:50 +0000 | |||
3 | +++ cloudinit/config/cc_byobu.py 2012-09-28 21:23:20 +0000 | |||
4 | @@ -18,12 +18,17 @@ | |||
5 | 18 | # You should have received a copy of the GNU General Public License | 18 | # You should have received a copy of the GNU General Public License |
6 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
7 | 20 | 20 | ||
8 | 21 | # Ensure this is aliased to a name not 'distros' | ||
9 | 22 | # since the module attribute 'distros' | ||
10 | 23 | # is a list of distros that are supported, not a sub-module | ||
11 | 24 | from cloudinit import distros as ds | ||
12 | 25 | |||
13 | 21 | from cloudinit import util | 26 | from cloudinit import util |
14 | 22 | 27 | ||
15 | 23 | distros = ['ubuntu', 'debian'] | 28 | distros = ['ubuntu', 'debian'] |
16 | 24 | 29 | ||
17 | 25 | 30 | ||
19 | 26 | def handle(name, cfg, _cloud, log, args): | 31 | def handle(name, cfg, cloud, log, args): |
20 | 27 | if len(args) != 0: | 32 | if len(args) != 0: |
21 | 28 | value = args[0] | 33 | value = args[0] |
22 | 29 | else: | 34 | else: |
23 | @@ -56,16 +61,20 @@ | |||
24 | 56 | 61 | ||
25 | 57 | shcmd = "" | 62 | shcmd = "" |
26 | 58 | if mod_user: | 63 | if mod_user: |
30 | 59 | user = util.get_cfg_option_str(cfg, "user", "ubuntu") | 64 | (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) |
31 | 60 | shcmd += " sudo -Hu \"%s\" byobu-launcher-%s" % (user, bl_inst) | 65 | (user, _user_config) = ds.extract_default(users) |
32 | 61 | shcmd += " || X=$(($X+1)); " | 66 | if not user: |
33 | 67 | log.warn(("No default byobu user provided, " | ||
34 | 68 | "can not launch %s for the default user"), bl_inst) | ||
35 | 69 | else: | ||
36 | 70 | shcmd += " sudo -Hu \"%s\" byobu-launcher-%s" % (user, bl_inst) | ||
37 | 71 | shcmd += " || X=$(($X+1)); " | ||
38 | 62 | if mod_sys: | 72 | if mod_sys: |
39 | 63 | shcmd += "echo \"%s\" | debconf-set-selections" % dc_val | 73 | shcmd += "echo \"%s\" | debconf-set-selections" % dc_val |
40 | 64 | shcmd += " && dpkg-reconfigure byobu --frontend=noninteractive" | 74 | shcmd += " && dpkg-reconfigure byobu --frontend=noninteractive" |
41 | 65 | shcmd += " || X=$(($X+1)); " | 75 | shcmd += " || X=$(($X+1)); " |
42 | 66 | 76 | ||
48 | 67 | cmd = ["/bin/sh", "-c", "%s %s %s" % ("X=0;", shcmd, "exit $X")] | 77 | if len(shcmd): |
49 | 68 | 78 | cmd = ["/bin/sh", "-c", "%s %s %s" % ("X=0;", shcmd, "exit $X")] | |
50 | 69 | log.debug("Setting byobu to %s", value) | 79 | log.debug("Setting byobu to %s", value) |
51 | 70 | 80 | util.subp(cmd, capture=False) | |
47 | 71 | util.subp(cmd, capture=False) | ||
52 | 72 | 81 | ||
53 | === modified file 'cloudinit/config/cc_set_passwords.py' | |||
54 | --- cloudinit/config/cc_set_passwords.py 2012-08-31 21:45:15 +0000 | |||
55 | +++ cloudinit/config/cc_set_passwords.py 2012-09-28 21:23:20 +0000 | |||
56 | @@ -20,6 +20,11 @@ | |||
57 | 20 | 20 | ||
58 | 21 | import sys | 21 | import sys |
59 | 22 | 22 | ||
60 | 23 | # Ensure this is aliased to a name not 'distros' | ||
61 | 24 | # since the module attribute 'distros' | ||
62 | 25 | # is a list of distros that are supported, not a sub-module | ||
63 | 26 | from cloudinit import distros as ds | ||
64 | 27 | |||
65 | 23 | from cloudinit import ssh_util | 28 | from cloudinit import ssh_util |
66 | 24 | from cloudinit import util | 29 | from cloudinit import util |
67 | 25 | 30 | ||
68 | @@ -50,18 +55,10 @@ | |||
69 | 50 | expire = util.get_cfg_option_bool(chfg, 'expire', expire) | 55 | expire = util.get_cfg_option_bool(chfg, 'expire', expire) |
70 | 51 | 56 | ||
71 | 52 | if not plist and password: | 57 | if not plist and password: |
81 | 53 | user = cloud.distro.get_default_user() | 58 | (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) |
82 | 54 | 59 | (user, _user_config) = ds.extract_default(users) | |
74 | 55 | if 'users' in cfg: | ||
75 | 56 | |||
76 | 57 | user_zero = cfg['users'][0] | ||
77 | 58 | |||
78 | 59 | if isinstance(user_zero, dict) and 'name' in user_zero: | ||
79 | 60 | user = user_zero['name'] | ||
80 | 61 | |||
83 | 62 | if user: | 60 | if user: |
84 | 63 | plist = "%s:%s" % (user, password) | 61 | plist = "%s:%s" % (user, password) |
85 | 64 | |||
86 | 65 | else: | 62 | else: |
87 | 66 | log.warn("No default or defined user to change password for.") | 63 | log.warn("No default or defined user to change password for.") |
88 | 67 | 64 | ||
89 | 68 | 65 | ||
90 | === modified file 'cloudinit/config/cc_ssh.py' | |||
91 | --- cloudinit/config/cc_ssh.py 2012-08-31 18:45:40 +0000 | |||
92 | +++ cloudinit/config/cc_ssh.py 2012-09-28 21:23:20 +0000 | |||
93 | @@ -21,6 +21,11 @@ | |||
94 | 21 | import glob | 21 | import glob |
95 | 22 | import os | 22 | import os |
96 | 23 | 23 | ||
97 | 24 | # Ensure this is aliased to a name not 'distros' | ||
98 | 25 | # since the module attribute 'distros' | ||
99 | 26 | # is a list of distros that are supported, not a sub-module | ||
100 | 27 | from cloudinit import distros as ds | ||
101 | 28 | |||
102 | 24 | from cloudinit import ssh_util | 29 | from cloudinit import ssh_util |
103 | 25 | from cloudinit import util | 30 | from cloudinit import util |
104 | 26 | 31 | ||
105 | @@ -102,16 +107,8 @@ | |||
106 | 102 | " %s to file %s"), keytype, keyfile) | 107 | " %s to file %s"), keytype, keyfile) |
107 | 103 | 108 | ||
108 | 104 | try: | 109 | try: |
119 | 105 | # TODO(utlemming): consolidate this stanza that occurs in: | 110 | (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) |
120 | 106 | # cc_ssh_import_id, cc_set_passwords, maybe cc_users_groups.py | 111 | (user, _user_config) = ds.extract_default(users) |
111 | 107 | user = cloud.distro.get_default_user() | ||
112 | 108 | |||
113 | 109 | if 'users' in cfg: | ||
114 | 110 | user_zero = cfg['users'][0] | ||
115 | 111 | |||
116 | 112 | if user_zero != "default": | ||
117 | 113 | user = user_zero | ||
118 | 114 | |||
121 | 115 | disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) | 112 | disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) |
122 | 116 | disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", | 113 | disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", |
123 | 117 | DISABLE_ROOT_OPTS) | 114 | DISABLE_ROOT_OPTS) |
124 | 118 | 115 | ||
125 | === modified file 'cloudinit/config/cc_ssh_authkey_fingerprints.py' | |||
126 | --- cloudinit/config/cc_ssh_authkey_fingerprints.py 2012-09-28 20:35:53 +0000 | |||
127 | +++ cloudinit/config/cc_ssh_authkey_fingerprints.py 2012-09-28 21:23:20 +0000 | |||
128 | @@ -21,7 +21,11 @@ | |||
129 | 21 | 21 | ||
130 | 22 | from prettytable import PrettyTable | 22 | from prettytable import PrettyTable |
131 | 23 | 23 | ||
133 | 24 | from cloudinit import distros | 24 | # Ensure this is aliased to a name not 'distros' |
134 | 25 | # since the module attribute 'distros' | ||
135 | 26 | # is a list of distros that are supported, not a sub-module | ||
136 | 27 | from cloudinit import distros as ds | ||
137 | 28 | |||
138 | 25 | from cloudinit import ssh_util | 29 | from cloudinit import ssh_util |
139 | 26 | from cloudinit import util | 30 | from cloudinit import util |
140 | 27 | 31 | ||
141 | @@ -41,8 +45,10 @@ | |||
142 | 41 | hasher = hashlib.new(hash_meth) | 45 | hasher = hashlib.new(hash_meth) |
143 | 42 | hasher.update(base64.b64decode(b64_text)) | 46 | hasher.update(base64.b64decode(b64_text)) |
144 | 43 | return ":".join(_split_hash(hasher.hexdigest())) | 47 | return ":".join(_split_hash(hasher.hexdigest())) |
146 | 44 | except TypeError: | 48 | except (TypeError, ValueError): |
147 | 45 | # Raised when b64 not really b64... | 49 | # Raised when b64 not really b64... |
148 | 50 | # or when the hash type is not really | ||
149 | 51 | # a known/supported hash type... | ||
150 | 46 | return '?' | 52 | return '?' |
151 | 47 | 53 | ||
152 | 48 | 54 | ||
153 | @@ -92,8 +98,8 @@ | |||
154 | 92 | 98 | ||
155 | 93 | hash_meth = util.get_cfg_option_str(cfg, "authkey_hash", "md5") | 99 | hash_meth = util.get_cfg_option_str(cfg, "authkey_hash", "md5") |
156 | 94 | extract_func = ssh_util.extract_authorized_keys | 100 | extract_func = ssh_util.extract_authorized_keys |
158 | 95 | (users, _groups) = distros.normalize_users_groups(cfg, cloud.distro) | 101 | (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) |
159 | 96 | for (user_name, _cfg) in users.items(): | 102 | for (user_name, _cfg) in users.items(): |
160 | 97 | (auth_key_fn, auth_key_entries) = extract_func(user_name, cloud.paths) | 103 | (auth_key_fn, auth_key_entries) = extract_func(user_name, cloud.paths) |
163 | 98 | _pprint_key_entries(user_name, auth_key_fn, auth_key_entries, | 104 | _pprint_key_entries(user_name, auth_key_fn, |
164 | 99 | hash_meth) | 105 | auth_key_entries, hash_meth) |
165 | 100 | 106 | ||
166 | === modified file 'cloudinit/config/cc_ssh_import_id.py' | |||
167 | --- cloudinit/config/cc_ssh_import_id.py 2012-08-31 19:36:05 +0000 | |||
168 | +++ cloudinit/config/cc_ssh_import_id.py 2012-09-28 21:23:20 +0000 | |||
169 | @@ -18,6 +18,11 @@ | |||
170 | 18 | # You should have received a copy of the GNU General Public License | 18 | # You should have received a copy of the GNU General Public License |
171 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
172 | 20 | 20 | ||
173 | 21 | # Ensure this is aliased to a name not 'distros' | ||
174 | 22 | # since the module attribute 'distros' | ||
175 | 23 | # is a list of distros that are supported, not a sub-module | ||
176 | 24 | from cloudinit import distros as ds | ||
177 | 25 | |||
178 | 21 | from cloudinit import util | 26 | from cloudinit import util |
179 | 22 | import pwd | 27 | import pwd |
180 | 23 | 28 | ||
181 | @@ -39,33 +44,27 @@ | |||
182 | 39 | return | 44 | return |
183 | 40 | 45 | ||
184 | 41 | # import for cloudinit created users | 46 | # import for cloudinit created users |
185 | 47 | (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) | ||
186 | 42 | elist = [] | 48 | elist = [] |
189 | 43 | for user_cfg in cfg['users']: | 49 | for (user, user_cfg) in users.items(): |
188 | 44 | user = None | ||
190 | 45 | import_ids = [] | 50 | import_ids = [] |
197 | 46 | 51 | if user_cfg['default']: | |
192 | 47 | if isinstance(user_cfg, str) and user_cfg == "default": | ||
193 | 48 | user = cloud.distro.get_default_user() | ||
194 | 49 | if not user: | ||
195 | 50 | continue | ||
196 | 51 | |||
198 | 52 | import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) | 52 | import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) |
204 | 53 | 53 | else: | |
200 | 54 | elif isinstance(user_cfg, dict): | ||
201 | 55 | user = None | ||
202 | 56 | import_ids = [] | ||
203 | 57 | |||
205 | 58 | try: | 54 | try: |
206 | 59 | user = user_cfg['name'] | ||
207 | 60 | import_ids = user_cfg['ssh_import_id'] | 55 | import_ids = user_cfg['ssh_import_id'] |
208 | 61 | |||
209 | 62 | if import_ids and isinstance(import_ids, str): | ||
210 | 63 | import_ids = str(import_ids).split(',') | ||
211 | 64 | |||
212 | 65 | except: | 56 | except: |
214 | 66 | log.debug("user %s is not configured for ssh_import" % user) | 57 | log.debug("User %s is not configured for ssh_import_id", user) |
215 | 67 | continue | 58 | continue |
216 | 68 | 59 | ||
217 | 60 | try: | ||
218 | 61 | import_ids = util.uniq_merge(import_ids) | ||
219 | 62 | import_ids = [str(i) for i in import_ids] | ||
220 | 63 | except: | ||
221 | 64 | log.debug("User %s is not correctly configured for ssh_import_id", | ||
222 | 65 | user) | ||
223 | 66 | continue | ||
224 | 67 | |||
225 | 69 | if not len(import_ids): | 68 | if not len(import_ids): |
226 | 70 | continue | 69 | continue |
227 | 71 | 70 | ||
228 | 72 | 71 | ||
229 | === modified file 'cloudinit/config/cc_users_groups.py' | |||
230 | --- cloudinit/config/cc_users_groups.py 2012-09-28 20:35:53 +0000 | |||
231 | +++ cloudinit/config/cc_users_groups.py 2012-09-28 21:23:20 +0000 | |||
232 | @@ -16,7 +16,10 @@ | |||
233 | 16 | # You should have received a copy of the GNU General Public License | 16 | # You should have received a copy of the GNU General Public License |
234 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
235 | 18 | 18 | ||
237 | 19 | from cloudinit import distros | 19 | # Ensure this is aliased to a name not 'distros' |
238 | 20 | # since the module attribute 'distros' | ||
239 | 21 | # is a list of distros that are supported, not a sub-module | ||
240 | 22 | from cloudinit import distros as ds | ||
241 | 20 | 23 | ||
242 | 21 | from cloudinit.settings import PER_INSTANCE | 24 | from cloudinit.settings import PER_INSTANCE |
243 | 22 | 25 | ||
244 | @@ -24,7 +27,7 @@ | |||
245 | 24 | 27 | ||
246 | 25 | 28 | ||
247 | 26 | def handle(name, cfg, cloud, _log, _args): | 29 | def handle(name, cfg, cloud, _log, _args): |
249 | 27 | (users, groups) = distros.normalize_users_groups(cfg, cloud.distro) | 30 | (users, groups) = ds.normalize_users_groups(cfg, cloud.distro) |
250 | 28 | for (name, members) in groups.items(): | 31 | for (name, members) in groups.items(): |
251 | 29 | cloud.distro.create_group(name, members) | 32 | cloud.distro.create_group(name, members) |
252 | 30 | for (user, config) in users.items(): | 33 | for (user, config) in users.items(): |
253 | 31 | 34 | ||
254 | === modified file 'cloudinit/distros/__init__.py' | |||
255 | --- cloudinit/distros/__init__.py 2012-09-28 19:38:48 +0000 | |||
256 | +++ cloudinit/distros/__init__.py 2012-09-28 21:23:20 +0000 | |||
257 | @@ -24,6 +24,7 @@ | |||
258 | 24 | from StringIO import StringIO | 24 | from StringIO import StringIO |
259 | 25 | 25 | ||
260 | 26 | import abc | 26 | import abc |
261 | 27 | import itertools | ||
262 | 27 | import os | 28 | import os |
263 | 28 | import re | 29 | import re |
264 | 29 | 30 | ||
265 | @@ -186,8 +187,10 @@ | |||
266 | 186 | 'gecos': "%s" % (self.default_user.title()), | 187 | 'gecos': "%s" % (self.default_user.title()), |
267 | 187 | 'sudo': "ALL=(ALL) NOPASSWD:ALL", | 188 | 'sudo': "ALL=(ALL) NOPASSWD:ALL", |
268 | 188 | } | 189 | } |
271 | 189 | if self.default_user_groups: | 190 | def_groups = self.default_user_groups |
272 | 190 | user_cfg['groups'] = _uniq_merge_sorted(self.default_user_groups) | 191 | if not def_groups: |
273 | 192 | def_groups = [] | ||
274 | 193 | user_cfg['groups'] = util.uniq_merge_sorted(def_groups) | ||
275 | 191 | return user_cfg | 194 | return user_cfg |
276 | 192 | 195 | ||
277 | 193 | def create_user(self, name, **kwargs): | 196 | def create_user(self, name, **kwargs): |
278 | @@ -398,39 +401,27 @@ | |||
279 | 398 | return default | 401 | return default |
280 | 399 | 402 | ||
281 | 400 | 403 | ||
304 | 401 | def _uniq_merge_sorted(*lists): | 404 | # Normalizes a input group configuration |
305 | 402 | return sorted(_uniq_merge(*lists)) | 405 | # which can be a comma seperated list of |
306 | 403 | 406 | # group names, or a list of group names | |
307 | 404 | 407 | # or a python dictionary of group names | |
308 | 405 | def _uniq_merge(*lists): | 408 | # to a list of members of that group. |
309 | 406 | combined_list = [] | 409 | # |
310 | 407 | for a_list in lists: | 410 | # The output is a dictionary of group |
311 | 408 | if isinstance(a_list, (str, basestring)): | 411 | # names => members of that group which |
312 | 409 | a_list = a_list.strip().split(",") | 412 | # is the standard form used in the rest |
313 | 410 | else: | 413 | # of cloud-init |
292 | 411 | a_list = [str(a) for a in a_list] | ||
293 | 412 | a_list = [a.strip() for a in a_list if a.strip()] | ||
294 | 413 | combined_list.extend(a_list) | ||
295 | 414 | uniq_list = [] | ||
296 | 415 | for a in combined_list: | ||
297 | 416 | if a in uniq_list: | ||
298 | 417 | continue | ||
299 | 418 | else: | ||
300 | 419 | uniq_list.append(a) | ||
301 | 420 | return uniq_list | ||
302 | 421 | |||
303 | 422 | |||
314 | 423 | def _normalize_groups(grp_cfg): | 414 | def _normalize_groups(grp_cfg): |
315 | 424 | if isinstance(grp_cfg, (str, basestring, list)): | 415 | if isinstance(grp_cfg, (str, basestring, list)): |
316 | 425 | c_grp_cfg = {} | 416 | c_grp_cfg = {} |
318 | 426 | for i in _uniq_merge(grp_cfg): | 417 | for i in util.uniq_merge(grp_cfg): |
319 | 427 | c_grp_cfg[i] = [] | 418 | c_grp_cfg[i] = [] |
320 | 428 | grp_cfg = c_grp_cfg | 419 | grp_cfg = c_grp_cfg |
321 | 429 | 420 | ||
322 | 430 | groups = {} | 421 | groups = {} |
323 | 431 | if isinstance(grp_cfg, (dict)): | 422 | if isinstance(grp_cfg, (dict)): |
324 | 432 | for (grp_name, grp_members) in grp_cfg.items(): | 423 | for (grp_name, grp_members) in grp_cfg.items(): |
326 | 433 | groups[grp_name] = _uniq_merge_sorted(grp_members) | 424 | groups[grp_name] = util.uniq_merge_sorted(grp_members) |
327 | 434 | else: | 425 | else: |
328 | 435 | raise TypeError(("Group config must be list, dict " | 426 | raise TypeError(("Group config must be list, dict " |
329 | 436 | " or string types only and not %s") % | 427 | " or string types only and not %s") % |
330 | @@ -438,6 +429,21 @@ | |||
331 | 438 | return groups | 429 | return groups |
332 | 439 | 430 | ||
333 | 440 | 431 | ||
334 | 432 | # Normalizes a input group configuration | ||
335 | 433 | # which can be a comma seperated list of | ||
336 | 434 | # user names, or a list of string user names | ||
337 | 435 | # or a list of dictionaries with components | ||
338 | 436 | # that define the user config + 'name' (if | ||
339 | 437 | # a 'name' field does not exist then the | ||
340 | 438 | # default user is assumed to 'own' that | ||
341 | 439 | # configuration. | ||
342 | 440 | # | ||
343 | 441 | # The output is a dictionary of user | ||
344 | 442 | # names => user config which is the standard | ||
345 | 443 | # form used in the rest of cloud-init. Note | ||
346 | 444 | # the default user will have a special config | ||
347 | 445 | # entry 'default' which will be marked as true | ||
348 | 446 | # all other users will be marked as false. | ||
349 | 441 | def _normalize_users(u_cfg, def_user_cfg=None): | 447 | def _normalize_users(u_cfg, def_user_cfg=None): |
350 | 442 | if isinstance(u_cfg, (dict)): | 448 | if isinstance(u_cfg, (dict)): |
351 | 443 | ad_ucfg = [] | 449 | ad_ucfg = [] |
352 | @@ -453,12 +459,12 @@ | |||
353 | 453 | " for key %s") % (util.obj_name(v), k)) | 459 | " for key %s") % (util.obj_name(v), k)) |
354 | 454 | u_cfg = ad_ucfg | 460 | u_cfg = ad_ucfg |
355 | 455 | elif isinstance(u_cfg, (str, basestring)): | 461 | elif isinstance(u_cfg, (str, basestring)): |
357 | 456 | u_cfg = _uniq_merge_sorted(u_cfg) | 462 | u_cfg = util.uniq_merge_sorted(u_cfg) |
358 | 457 | 463 | ||
359 | 458 | users = {} | 464 | users = {} |
360 | 459 | for user_config in u_cfg: | 465 | for user_config in u_cfg: |
361 | 460 | if isinstance(user_config, (str, basestring, list)): | 466 | if isinstance(user_config, (str, basestring, list)): |
363 | 461 | for u in _uniq_merge(user_config): | 467 | for u in util.uniq_merge(user_config): |
364 | 462 | if u and u not in users: | 468 | if u and u not in users: |
365 | 463 | users[u] = {} | 469 | users[u] = {} |
366 | 464 | elif isinstance(user_config, (dict)): | 470 | elif isinstance(user_config, (dict)): |
367 | @@ -491,22 +497,59 @@ | |||
368 | 491 | 497 | ||
369 | 492 | # Fixup the default user into the real | 498 | # Fixup the default user into the real |
370 | 493 | # default user name and replace it... | 499 | # default user name and replace it... |
371 | 500 | def_user = None | ||
372 | 494 | if users and 'default' in users: | 501 | if users and 'default' in users: |
373 | 495 | def_config = users.pop('default') | 502 | def_config = users.pop('default') |
374 | 496 | if def_user_cfg: | 503 | if def_user_cfg: |
375 | 504 | # Pickup what the default 'real name' is | ||
376 | 505 | # and any groups that are provided by the | ||
377 | 506 | # default config | ||
378 | 497 | def_user = def_user_cfg.pop('name') | 507 | def_user = def_user_cfg.pop('name') |
379 | 498 | def_groups = def_user_cfg.pop('groups', []) | 508 | def_groups = def_user_cfg.pop('groups', []) |
380 | 509 | # Pickup any config + groups for that user name | ||
381 | 510 | # that we may have previously extracted | ||
382 | 499 | parsed_config = users.pop(def_user, {}) | 511 | parsed_config = users.pop(def_user, {}) |
385 | 500 | users_groups = _uniq_merge_sorted(parsed_config.get('groups', []), | 512 | parsed_groups = parsed_config.get('groups', []) |
386 | 501 | def_groups) | 513 | # Now merge our extracted groups with |
387 | 514 | # anything the default config provided | ||
388 | 515 | users_groups = util.uniq_merge_sorted(parsed_groups, def_groups) | ||
389 | 502 | parsed_config['groups'] = ",".join(users_groups) | 516 | parsed_config['groups'] = ",".join(users_groups) |
390 | 517 | # The real config for the default user is the | ||
391 | 518 | # combination of the default user config provided | ||
392 | 519 | # by the distro, the default user config provided | ||
393 | 520 | # by the above merging for the user 'default' and | ||
394 | 521 | # then the parsed config from the user's 'real name' | ||
395 | 522 | # which does not have to be 'default' (but could be) | ||
396 | 503 | users[def_user] = util.mergemanydict([def_user_cfg, | 523 | users[def_user] = util.mergemanydict([def_user_cfg, |
397 | 504 | def_config, | 524 | def_config, |
398 | 505 | parsed_config]) | 525 | parsed_config]) |
399 | 506 | 526 | ||
400 | 527 | # Ensure that only the default user that we | ||
401 | 528 | # found (if any) is actually marked as being | ||
402 | 529 | # the default user | ||
403 | 530 | if users: | ||
404 | 531 | for (uname, uconfig) in users.items(): | ||
405 | 532 | if def_user and uname == def_user: | ||
406 | 533 | uconfig['default'] = True | ||
407 | 534 | else: | ||
408 | 535 | uconfig['default'] = False | ||
409 | 536 | |||
410 | 507 | return users | 537 | return users |
411 | 508 | 538 | ||
412 | 509 | 539 | ||
413 | 540 | # Normalizes a set of user/users and group | ||
414 | 541 | # dictionary configuration into a useable | ||
415 | 542 | # format that the rest of cloud-init can | ||
416 | 543 | # understand using the default user | ||
417 | 544 | # provided by the input distrobution (if any) | ||
418 | 545 | # to allow for mapping of the 'default' user. | ||
419 | 546 | # | ||
420 | 547 | # Output is a dictionary of group names -> [member] (list) | ||
421 | 548 | # and a dictionary of user names -> user configuration (dict) | ||
422 | 549 | # | ||
423 | 550 | # If 'user' exists it will override | ||
424 | 551 | # the 'users'[0] entry (if a list) otherwise it will | ||
425 | 552 | # just become an entry in the returned dictionary (no override) | ||
426 | 510 | def normalize_users_groups(cfg, distro): | 553 | def normalize_users_groups(cfg, distro): |
427 | 511 | if not cfg: | 554 | if not cfg: |
428 | 512 | cfg = {} | 555 | cfg = {} |
429 | @@ -548,6 +591,33 @@ | |||
430 | 548 | return (users, groups) | 591 | return (users, groups) |
431 | 549 | 592 | ||
432 | 550 | 593 | ||
433 | 594 | # Given a user dictionary config it will | ||
434 | 595 | # extract the default user name and user config | ||
435 | 596 | # from that list and return that tuple or | ||
436 | 597 | # return (None, None) if no default user is | ||
437 | 598 | # found in the given input | ||
438 | 599 | def extract_default(users, default_name=None, default_config=None): | ||
439 | 600 | if not users: | ||
440 | 601 | users = {} | ||
441 | 602 | |||
442 | 603 | def safe_find(entry): | ||
443 | 604 | config = entry[1] | ||
444 | 605 | if not config or 'default' not in config: | ||
445 | 606 | return False | ||
446 | 607 | else: | ||
447 | 608 | return config['default'] | ||
448 | 609 | |||
449 | 610 | tmp_users = users.items() | ||
450 | 611 | tmp_users = dict(itertools.ifilter(safe_find, tmp_users)) | ||
451 | 612 | if not tmp_users: | ||
452 | 613 | return (default_name, default_config) | ||
453 | 614 | else: | ||
454 | 615 | name = tmp_users.keys()[0] | ||
455 | 616 | config = tmp_users[name] | ||
456 | 617 | config.pop('default', None) | ||
457 | 618 | return (name, config) | ||
458 | 619 | |||
459 | 620 | |||
460 | 551 | def fetch(name): | 621 | def fetch(name): |
461 | 552 | locs = importer.find_module(name, | 622 | locs = importer.find_module(name, |
462 | 553 | ['', __name__], | 623 | ['', __name__], |
463 | 554 | 624 | ||
464 | === modified file 'cloudinit/util.py' | |||
465 | --- cloudinit/util.py 2012-09-28 20:31:50 +0000 | |||
466 | +++ cloudinit/util.py 2012-09-28 21:23:20 +0000 | |||
467 | @@ -249,6 +249,36 @@ | |||
468 | 249 | raise | 249 | raise |
469 | 250 | 250 | ||
470 | 251 | 251 | ||
471 | 252 | # Merges X lists, and then keeps the | ||
472 | 253 | # unique ones, but orders by sort order | ||
473 | 254 | # instead of by the original order | ||
474 | 255 | def uniq_merge_sorted(*lists): | ||
475 | 256 | return sorted(uniq_merge(*lists)) | ||
476 | 257 | |||
477 | 258 | |||
478 | 259 | # Merges X lists and then iterates over those | ||
479 | 260 | # and only keeps the unique items (order preserving) | ||
480 | 261 | # and returns that merged and uniqued list as the | ||
481 | 262 | # final result. | ||
482 | 263 | # | ||
483 | 264 | # Note: if any entry is a string it will be | ||
484 | 265 | # split on commas and empty entries will be | ||
485 | 266 | # evicted and merged in accordingly. | ||
486 | 267 | def uniq_merge(*lists): | ||
487 | 268 | combined_list = [] | ||
488 | 269 | for a_list in lists: | ||
489 | 270 | if isinstance(a_list, (str, basestring)): | ||
490 | 271 | a_list = a_list.strip().split(",") | ||
491 | 272 | # Kickout the empty ones | ||
492 | 273 | a_list = [a for a in a_list if len(a)] | ||
493 | 274 | combined_list.extend(a_list) | ||
494 | 275 | uniq_list = [] | ||
495 | 276 | for i in combined_list: | ||
496 | 277 | if i not in uniq_list: | ||
497 | 278 | uniq_list.append(i) | ||
498 | 279 | return uniq_list | ||
499 | 280 | |||
500 | 281 | |||
501 | 252 | def clean_filename(fn): | 282 | def clean_filename(fn): |
502 | 253 | for (k, v) in FN_REPLACEMENTS.iteritems(): | 283 | for (k, v) in FN_REPLACEMENTS.iteritems(): |
503 | 254 | fn = fn.replace(k, v) | 284 | fn = fn.replace(k, v) |
504 | 255 | 285 | ||
505 | === modified file 'tests/unittests/test_distros/test_user_data_normalize.py' | |||
506 | --- tests/unittests/test_distros/test_user_data_normalize.py 2012-09-28 01:30:01 +0000 | |||
507 | +++ tests/unittests/test_distros/test_user_data_normalize.py 2012-09-28 21:23:20 +0000 | |||
508 | @@ -119,8 +119,8 @@ | |||
509 | 119 | (users, _groups) = self._norm(ug_cfg, distro) | 119 | (users, _groups) = self._norm(ug_cfg, distro) |
510 | 120 | self.assertIn('joe', users) | 120 | self.assertIn('joe', users) |
511 | 121 | self.assertIn('bob', users) | 121 | self.assertIn('bob', users) |
514 | 122 | self.assertEquals({}, users['joe']) | 122 | self.assertEquals({'default': False}, users['joe']) |
515 | 123 | self.assertEquals({}, users['bob']) | 123 | self.assertEquals({'default': False}, users['bob']) |
516 | 124 | 124 | ||
517 | 125 | def test_users_simple(self): | 125 | def test_users_simple(self): |
518 | 126 | distro = self._make_distro('ubuntu') | 126 | distro = self._make_distro('ubuntu') |
519 | @@ -133,8 +133,8 @@ | |||
520 | 133 | (users, _groups) = self._norm(ug_cfg, distro) | 133 | (users, _groups) = self._norm(ug_cfg, distro) |
521 | 134 | self.assertIn('joe', users) | 134 | self.assertIn('joe', users) |
522 | 135 | self.assertIn('bob', users) | 135 | self.assertIn('bob', users) |
525 | 136 | self.assertEquals({}, users['joe']) | 136 | self.assertEquals({'default': False}, users['joe']) |
526 | 137 | self.assertEquals({}, users['bob']) | 137 | self.assertEquals({'default': False}, users['bob']) |
527 | 138 | 138 | ||
528 | 139 | def test_users_old_user(self): | 139 | def test_users_old_user(self): |
529 | 140 | distro = self._make_distro('ubuntu', 'bob') | 140 | distro = self._make_distro('ubuntu', 'bob') |
530 | @@ -179,8 +179,7 @@ | |||
531 | 179 | } | 179 | } |
532 | 180 | (users, _groups) = self._norm(ug_cfg, distro) | 180 | (users, _groups) = self._norm(ug_cfg, distro) |
533 | 181 | self.assertIn('zetta', users) | 181 | self.assertIn('zetta', users) |
536 | 182 | ug_cfg = { | 182 | ug_cfg = {} |
535 | 183 | } | ||
537 | 184 | (users, groups) = self._norm(ug_cfg, distro) | 183 | (users, groups) = self._norm(ug_cfg, distro) |
538 | 185 | self.assertEquals({}, users) | 184 | self.assertEquals({}, users) |
539 | 186 | self.assertEquals({}, groups) | 185 | self.assertEquals({}, groups) |
540 | @@ -198,6 +197,35 @@ | |||
541 | 198 | users['bob']['groups']) | 197 | users['bob']['groups']) |
542 | 199 | self.assertEquals(True, | 198 | self.assertEquals(True, |
543 | 200 | users['bob']['blah']) | 199 | users['bob']['blah']) |
544 | 200 | self.assertEquals(True, | ||
545 | 201 | users['bob']['default']) | ||
546 | 202 | |||
547 | 203 | def test_users_dict_extract(self): | ||
548 | 204 | distro = self._make_distro('ubuntu', 'bob') | ||
549 | 205 | ug_cfg = { | ||
550 | 206 | 'users': [ | ||
551 | 207 | 'default', | ||
552 | 208 | ], | ||
553 | 209 | } | ||
554 | 210 | (users, _groups) = self._norm(ug_cfg, distro) | ||
555 | 211 | self.assertIn('bob', users) | ||
556 | 212 | (name, config) = distros.extract_default(users) | ||
557 | 213 | self.assertEquals(name, 'bob') | ||
558 | 214 | expected_config = {} | ||
559 | 215 | def_config = None | ||
560 | 216 | try: | ||
561 | 217 | def_config = distro.get_default_user() | ||
562 | 218 | except NotImplementedError: | ||
563 | 219 | pass | ||
564 | 220 | if not def_config: | ||
565 | 221 | def_config = {} | ||
566 | 222 | expected_config.update(def_config) | ||
567 | 223 | |||
568 | 224 | # Ignore these for now | ||
569 | 225 | expected_config.pop('name', None) | ||
570 | 226 | expected_config.pop('groups', None) | ||
571 | 227 | config.pop('groups', None) | ||
572 | 228 | self.assertEquals(config, expected_config) | ||
573 | 201 | 229 | ||
574 | 202 | def test_users_dict_default(self): | 230 | def test_users_dict_default(self): |
575 | 203 | distro = self._make_distro('ubuntu', 'bob') | 231 | distro = self._make_distro('ubuntu', 'bob') |
576 | @@ -210,6 +238,8 @@ | |||
577 | 210 | self.assertIn('bob', users) | 238 | self.assertIn('bob', users) |
578 | 211 | self.assertEquals(",".join(distro.get_default_user()['groups']), | 239 | self.assertEquals(",".join(distro.get_default_user()['groups']), |
579 | 212 | users['bob']['groups']) | 240 | users['bob']['groups']) |
580 | 241 | self.assertEquals(True, | ||
581 | 242 | users['bob']['default']) | ||
582 | 213 | 243 | ||
583 | 214 | def test_users_dict_trans(self): | 244 | def test_users_dict_trans(self): |
584 | 215 | distro = self._make_distro('ubuntu') | 245 | distro = self._make_distro('ubuntu') |
585 | @@ -223,8 +253,8 @@ | |||
586 | 223 | (users, _groups) = self._norm(ug_cfg, distro) | 253 | (users, _groups) = self._norm(ug_cfg, distro) |
587 | 224 | self.assertIn('joe', users) | 254 | self.assertIn('joe', users) |
588 | 225 | self.assertIn('bob', users) | 255 | self.assertIn('bob', users) |
591 | 226 | self.assertEquals({'tr_me': True}, users['joe']) | 256 | self.assertEquals({'tr_me': True, 'default': False}, users['joe']) |
592 | 227 | self.assertEquals({}, users['bob']) | 257 | self.assertEquals({'default': False}, users['bob']) |
593 | 228 | 258 | ||
594 | 229 | def test_users_dict(self): | 259 | def test_users_dict(self): |
595 | 230 | distro = self._make_distro('ubuntu') | 260 | distro = self._make_distro('ubuntu') |
596 | @@ -237,5 +267,5 @@ | |||
597 | 237 | (users, _groups) = self._norm(ug_cfg, distro) | 267 | (users, _groups) = self._norm(ug_cfg, distro) |
598 | 238 | self.assertIn('joe', users) | 268 | self.assertIn('joe', users) |
599 | 239 | self.assertIn('bob', users) | 269 | self.assertIn('bob', users) |
602 | 240 | self.assertEquals({}, users['joe']) | 270 | self.assertEquals({'default': False}, users['joe']) |
603 | 241 | self.assertEquals({}, users['bob']) | 271 | self.assertEquals({'default': False}, users['bob']) |