Merge lp:~darkmuggle-deactivatedaccount/cloud-init/azure-disk_format into lp:~cloud-init-dev/cloud-init/trunk
- azure-disk_format
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 878 |
Proposed branch: | lp:~darkmuggle-deactivatedaccount/cloud-init/azure-disk_format |
Merge into: | lp:~cloud-init-dev/cloud-init/trunk |
Diff against target: |
745 lines (+245/-145) 8 files modified
ChangeLog (+1/-0) cloudinit/config/cc_disk_setup.py (+83/-82) cloudinit/sources/DataSourceAzure.py (+21/-7) cloudinit/sources/DataSourceSmartOS.py (+8/-8) doc/examples/cloud-config-disk-setup.txt (+46/-16) doc/sources/smartos/README.rst (+17/-11) tests/unittests/test_datasource/test_azure.py (+55/-5) tests/unittests/test_datasource/test_smartos.py (+14/-16) |
To merge this branch: | bzr merge lp:~darkmuggle-deactivatedaccount/cloud-init/azure-disk_format |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser | Pending | ||
Review via email:
|
Commit message
Azure: partition and create filesystem of ephemeral storage device.
This utilizes the work on the 'cc_disk_setup' to make the Azure datasource
by default partition and create a filesystem on the ephemeral storage
device.
The user can override this behavior via user-data.
Description of the change
Add the ability for cloud-init to handle formating the Azure ephemeral storage.
- 873. By Scott Moser
-
merge from trunk
- 874. By Scott Moser
-
fix pep8
- 875. By Scott Moser
-
add entry to ChangeLog
- 876. By Scott Moser
-
re-work 'ephemeral_disk' and location of builtin config
Previously we had this 'ephemeral_disk' entry in the datasource config
for Azure, and then we also copied some entries into the .cfg
for that datasource from the datasource config.Ie, datasource[
'Azure' ]['disk_ setup'] would be oddly copied
into the .cfg object that was returned by 'get_config_obj'Now, instead, we have a BUILTIN_
CLOUD_CONFIG, which has those same
values in it.The other change here is that 'ephemeral_disk' now has no meaning.
Instead, we add a populated-by-default entry 'disk_aliases' to the
BUILTIN_DS_CFG, and then just return entries in it for
'device_name_to_ device' - 877. By Scott Moser
-
fix tests small other changes
Also
* cloudinit/sources/ DataSourceAzure .py: invalid xml in a file called
'ovfenv.xml' should raise BrokenAzureDatasource rather than
NonAzureDataSource
* cloudinit/sources/ DataSourceSmart OS.py:
cloudinit/sources/ DataSourceAzure .py
use 'ephemeral0' as the device name in builtin fs_setup* tests/unittests
/test_datasourc e/test_ azure.py:
* always patch 'list_possible_azure_ds_ devs' as it calls find_devs_with
which calls blkid, and dramatically was slowing down tests on my system.
* test_user_cfg_set_ agent_command_ plain:
fix this test to not depend on specific format of yaml.dumps().
* test_userdata_arrives: add a test that user-data makes it through - 878. By Scott Moser
-
fix doc for smartos
- 879. By Scott Moser
-
fix probably debug code that explicitly set 'partition' to 'any'.
- 880. By Scott Moser
-
update documentation
- 881. By Scott Moser
-
azure data source 'ephemeral0' point to '/dev/sdb', not /dev/sdb1
in general block device mappings should be to block devices, not
partitoins. - 882. By Scott Moser
-
fix syntax error in last commit
- 883. By Scott Moser
-
update to get functional with any alias in a 'device' entry
- 884. By Scott Moser
-
adjust aliases early rather than late.
this adds 2 functions
update_disk_setup_ devices
update_fs_setup_ devices Which update the appropriate datatype, and translate the names.
Translating early means we don't have to deal with updating in the mkfs or
mkpart calls explicitly.These are more easily unit tested as they just take a dictionary of the
expected type and a 'transformer' that should return a new name or None. - 885. By Scott Moser
-
merge from ben. disable partition creation on smartos
- 886. By Scott Moser
-
demote .info to .debug, add another debug
- 887. By Scott Moser
-
fix test to adjust for 'ephemeral0' being /dev/sdb
- 888. By Scott Moser
-
fix one bug
- 889. By Scott Moser
-
change LOG.debug('format' % var) to ('format', var)
- 890. By Scott Moser
-
find_device_node: treat label=None as label=""
Since for a string there is no difference, we're just
checking for this here. - 891. By Ben Howard
-
Updated the documentation on disk formating
Preview Diff
1 | === modified file 'ChangeLog' |
2 | --- ChangeLog 2013-09-26 12:47:14 +0000 |
3 | +++ ChangeLog 2013-09-27 22:26:03 +0000 |
4 | @@ -3,6 +3,7 @@ |
5 | - small fix for OVF datasource for iso transport on non-iso9660 filesystem |
6 | - determine if upstart version is suitable for |
7 | 'initctl reload-configuration' (LP: #1124384). If so, then invoke it. |
8 | + supports setting up instance-store disk with partition table and filesystem. |
9 | - add Azure datasource. |
10 | - add support for SuSE / SLES [Juerg Haefliger] |
11 | - add a trailing carriage return to chpasswd input, which reportedly |
12 | |
13 | === modified file 'cloudinit/config/cc_disk_setup.py' |
14 | --- cloudinit/config/cc_disk_setup.py 2013-09-19 22:49:50 +0000 |
15 | +++ cloudinit/config/cc_disk_setup.py 2013-09-27 22:26:03 +0000 |
16 | @@ -41,7 +41,8 @@ |
17 | """ |
18 | disk_setup = cfg.get("disk_setup") |
19 | if isinstance(disk_setup, dict): |
20 | - log.info("Partitioning disks.") |
21 | + update_disk_setup_devices(disk_setup, cloud.device_name_to_device) |
22 | + log.debug("Partitioning disks: %s", str(disk_setup)) |
23 | for disk, definition in disk_setup.items(): |
24 | if not isinstance(definition, dict): |
25 | log.warn("Invalid disk definition for %s" % disk) |
26 | @@ -51,13 +52,14 @@ |
27 | log.debug("Creating new partition table/disk") |
28 | util.log_time(logfunc=LOG.debug, |
29 | msg="Creating partition on %s" % disk, |
30 | - func=mkpart, args=(disk, cloud, definition)) |
31 | + func=mkpart, args=(disk, definition)) |
32 | except Exception as e: |
33 | util.logexc(LOG, "Failed partitioning operation\n%s" % e) |
34 | |
35 | fs_setup = cfg.get("fs_setup") |
36 | if isinstance(fs_setup, list): |
37 | - log.info("Creating file systems.") |
38 | + log.debug("setting up filesystems: %s", str(fs_setup)) |
39 | + update_fs_setup_devices(fs_setup, cloud.device_name_to_device) |
40 | for definition in fs_setup: |
41 | if not isinstance(definition, dict): |
42 | log.warn("Invalid file system definition: %s" % definition) |
43 | @@ -68,31 +70,48 @@ |
44 | device = definition.get('device') |
45 | util.log_time(logfunc=LOG.debug, |
46 | msg="Creating fs for %s" % device, |
47 | - func=mkfs, args=(cloud, definition)) |
48 | + func=mkfs, args=(definition,)) |
49 | except Exception as e: |
50 | util.logexc(LOG, "Failed during filesystem operation\n%s" % e) |
51 | |
52 | |
53 | -def is_default_device(name, cloud, fallback=None): |
54 | - """ |
55 | - Ask the cloud datasource if the 'name' maps to a default |
56 | - device. If so, return that value, otherwise return 'name', or |
57 | - fallback if so defined. |
58 | - """ |
59 | - |
60 | - _dev = None |
61 | - try: |
62 | - _dev = cloud.device_name_to_device(name) |
63 | - except Exception as e: |
64 | - util.logexc(LOG, "Failed to find mapping for %s" % e) |
65 | - |
66 | - if _dev: |
67 | - return _dev |
68 | - |
69 | - if fallback: |
70 | - return fallback |
71 | - |
72 | - return name |
73 | +def update_disk_setup_devices(disk_setup, tformer): |
74 | + # update 'disk_setup' dictionary anywhere were a device may occur |
75 | + # update it with the response from 'tformer' |
76 | + for origname in disk_setup.keys(): |
77 | + transformed = tformer(origname) |
78 | + if transformed is None or transformed == origname: |
79 | + continue |
80 | + if transformed in disk_setup: |
81 | + LOG.info("Replacing %s in disk_setup for translation of %s", |
82 | + origname, transformed) |
83 | + del disk_setup[transformed] |
84 | + |
85 | + disk_setup[transformed] = disk_setup[origname] |
86 | + disk_setup[transformed]['_origname'] = origname |
87 | + del disk_setup[origname] |
88 | + LOG.debug("updated disk_setup device entry '%s' to '%s'", |
89 | + origname, transformed) |
90 | + |
91 | + |
92 | +def update_fs_setup_devices(disk_setup, tformer): |
93 | + # update 'fs_setup' dictionary anywhere were a device may occur |
94 | + # update it with the response from 'tformer' |
95 | + for definition in disk_setup: |
96 | + if not isinstance(definition, dict): |
97 | + LOG.warn("entry in disk_setup not a dict: %s", definition) |
98 | + continue |
99 | + |
100 | + origname = definition.get('device') |
101 | + if origname is None: |
102 | + continue |
103 | + |
104 | + transformed = tformer(origname) |
105 | + if transformed is None or transformed == origname: |
106 | + continue |
107 | + |
108 | + definition['_origname'] = origname |
109 | + definition['device'] = transformed |
110 | |
111 | |
112 | def value_splitter(values, start=None): |
113 | @@ -195,6 +214,10 @@ |
114 | |
115 | Note: This works with GPT partition tables! |
116 | """ |
117 | + # label of None is same as no label |
118 | + if label is None: |
119 | + label = "" |
120 | + |
121 | if not valid_targets: |
122 | valid_targets = ['disk', 'part'] |
123 | |
124 | @@ -219,8 +242,8 @@ |
125 | for key, value in value_splitter(part): |
126 | d[key.lower()] = value |
127 | |
128 | - if d['fstype'] == fs_type and \ |
129 | - ((label_match and d['label'] == label) or not label_match): |
130 | + if (d['fstype'] == fs_type and |
131 | + ((label_match and d['label'] == label) or not label_match)): |
132 | # If we find a matching device, we return that |
133 | return ('/dev/%s' % d['name'], True) |
134 | |
135 | @@ -397,8 +420,8 @@ |
136 | # Create a single partition |
137 | return "0," |
138 | |
139 | - if (len(layout) == 0 and isinstance(layout, list)) or \ |
140 | - not isinstance(layout, list): |
141 | + if ((len(layout) == 0 and isinstance(layout, list)) or |
142 | + not isinstance(layout, list)): |
143 | raise Exception("Partition layout is invalid") |
144 | |
145 | last_part_num = len(layout) |
146 | @@ -414,8 +437,7 @@ |
147 | |
148 | if isinstance(part, list): |
149 | if len(part) != 2: |
150 | - raise Exception("Partition was incorrectly defined: %s" % \ |
151 | - part) |
152 | + raise Exception("Partition was incorrectly defined: %s" % part) |
153 | percent, part_type = part |
154 | |
155 | part_size = int((float(size) * (float(percent) / 100)) / 1024) |
156 | @@ -488,12 +510,11 @@ |
157 | return get_dyn_func("exec_mkpart_%s", table_type, device, layout) |
158 | |
159 | |
160 | -def mkpart(device, cloud, definition): |
161 | +def mkpart(device, definition): |
162 | """ |
163 | Creates the partition table. |
164 | |
165 | Parameters: |
166 | - cloud: the cloud object |
167 | definition: dictionary describing how to create the partition. |
168 | |
169 | The following are supported values in the dict: |
170 | @@ -508,29 +529,18 @@ |
171 | overwrite = definition.get('overwrite', False) |
172 | layout = definition.get('layout', False) |
173 | table_type = definition.get('table_type', 'mbr') |
174 | - _device = is_default_device(device, cloud) |
175 | |
176 | # Check if the default device is a partition or not |
177 | LOG.debug("Checking against default devices") |
178 | - if _device and (_device != device): |
179 | - if not is_device_valid(_device): |
180 | - _device = _device[:-1] |
181 | - |
182 | - if not is_device_valid(_device): |
183 | - raise Exception("Unable to find backing block device for %s" % \ |
184 | - device) |
185 | - else: |
186 | - LOG.debug("Mapped %s to physical device %s" % (device, _device)) |
187 | - device = _device |
188 | |
189 | if (isinstance(layout, bool) and not layout) or not layout: |
190 | LOG.debug("Device is not to be partitioned, skipping") |
191 | return # Device is not to be partitioned |
192 | |
193 | # This prevents you from overwriting the device |
194 | - LOG.debug("Checking if device %s is a valid device" % device) |
195 | + LOG.debug("Checking if device %s is a valid device", device) |
196 | if not is_device_valid(device): |
197 | - raise Exception("Device %s is not a disk device!" % device) |
198 | + raise Exception("Device %s is not a disk device!", device) |
199 | |
200 | LOG.debug("Checking if device layout matches") |
201 | if check_partition_layout(table_type, device, layout): |
202 | @@ -549,13 +559,13 @@ |
203 | part_definition = get_partition_layout(table_type, device_size, layout) |
204 | LOG.debug(" Layout is: %s" % part_definition) |
205 | |
206 | - LOG.debug("Creating partition table on %s" % device) |
207 | + LOG.debug("Creating partition table on %s", device) |
208 | exec_mkpart(table_type, device, part_definition) |
209 | |
210 | - LOG.debug("Partition table created for %s" % device) |
211 | - |
212 | - |
213 | -def mkfs(cloud, fs_cfg): |
214 | + LOG.debug("Partition table created for %s", device) |
215 | + |
216 | + |
217 | +def mkfs(fs_cfg): |
218 | """ |
219 | Create a file system on the device. |
220 | |
221 | @@ -576,54 +586,45 @@ |
222 | |
223 | When 'cmd' is provided then no other parameter is required. |
224 | """ |
225 | - fs_cfg['partition'] = 'any' |
226 | label = fs_cfg.get('label') |
227 | device = fs_cfg.get('device') |
228 | - partition = str(fs_cfg.get('partition')) |
229 | + partition = str(fs_cfg.get('partition', 'any')) |
230 | fs_type = fs_cfg.get('filesystem') |
231 | fs_cmd = fs_cfg.get('cmd', []) |
232 | fs_opts = fs_cfg.get('extra_opts', []) |
233 | overwrite = fs_cfg.get('overwrite', False) |
234 | |
235 | # This allows you to define the default ephemeral or swap |
236 | - LOG.debug("Checking %s against default devices" % device) |
237 | - _device = is_default_device(label, cloud, fallback=device) |
238 | - if _device and (_device != device): |
239 | - if not is_device_valid(_device): |
240 | - raise Exception("Unable to find backing block device for %s" % \ |
241 | - device) |
242 | - else: |
243 | - LOG.debug("Mapped %s to physical device %s" % (device, _device)) |
244 | - device = _device |
245 | + LOG.debug("Checking %s against default devices", device) |
246 | |
247 | if not partition or partition.isdigit(): |
248 | # Handle manual definition of partition |
249 | if partition.isdigit(): |
250 | device = "%s%s" % (device, partition) |
251 | - LOG.debug("Manual request of partition %s for %s" % ( |
252 | - partition, device)) |
253 | + LOG.debug("Manual request of partition %s for %s", |
254 | + partition, device) |
255 | |
256 | # Check to see if the fs already exists |
257 | - LOG.debug("Checking device %s" % device) |
258 | + LOG.debug("Checking device %s", device) |
259 | check_label, check_fstype, _ = check_fs(device) |
260 | - LOG.debug("Device %s has %s %s" % (device, check_label, check_fstype)) |
261 | + LOG.debug("Device %s has %s %s", device, check_label, check_fstype) |
262 | |
263 | if check_label == label and check_fstype == fs_type: |
264 | - LOG.debug("Existing file system found at %s" % device) |
265 | + LOG.debug("Existing file system found at %s", device) |
266 | |
267 | if not overwrite: |
268 | - LOG.warn("Device %s has required file system" % device) |
269 | + LOG.debug("Device %s has required file system", device) |
270 | return |
271 | else: |
272 | - LOG.warn("Destroying filesystem on %s" % device) |
273 | + LOG.warn("Destroying filesystem on %s", device) |
274 | |
275 | else: |
276 | - LOG.debug("Device %s is cleared for formating" % device) |
277 | + LOG.debug("Device %s is cleared for formating", device) |
278 | |
279 | elif partition and str(partition).lower() in ('auto', 'any'): |
280 | # For auto devices, we match if the filesystem does exist |
281 | odevice = device |
282 | - LOG.debug("Identifying device to create %s filesytem on" % label) |
283 | + LOG.debug("Identifying device to create %s filesytem on", label) |
284 | |
285 | # any mean pick the first match on the device with matching fs_type |
286 | label_match = True |
287 | @@ -632,33 +633,32 @@ |
288 | |
289 | device, reuse = find_device_node(device, fs_type=fs_type, label=label, |
290 | label_match=label_match) |
291 | - LOG.debug("Automatic device for %s identified as %s" % ( |
292 | - odevice, device)) |
293 | + LOG.debug("Automatic device for %s identified as %s", odevice, device) |
294 | |
295 | if reuse: |
296 | LOG.debug("Found filesystem match, skipping formating.") |
297 | return |
298 | |
299 | if not device: |
300 | - LOG.debug("No device aviable that matches request.") |
301 | - LOG.debug("Skipping fs creation for %s" % fs_cfg) |
302 | + LOG.debug("No device aviable that matches request. " |
303 | + "Skipping fs creation for %s", fs_cfg) |
304 | return |
305 | |
306 | else: |
307 | LOG.debug("Error in device identification handling.") |
308 | return |
309 | |
310 | - LOG.debug("File system %s will be created on %s" % (label, device)) |
311 | + LOG.debug("File system %s will be created on %s", label, device) |
312 | |
313 | # Make sure the device is defined |
314 | if not device: |
315 | - LOG.critical("Device is not known: %s" % fs_cfg) |
316 | + LOG.warn("Device is not known: %s", device) |
317 | return |
318 | |
319 | # Check that we can create the FS |
320 | - if not label or not fs_type: |
321 | - LOG.debug("Command to create filesystem %s is bad. Skipping." % \ |
322 | - label) |
323 | + if not (fs_type or fs_cmd): |
324 | + raise Exception("No way to create filesystem '%s'. fs_type or fs_cmd " |
325 | + "must be set.", label) |
326 | |
327 | # Create the commands |
328 | if fs_cmd: |
329 | @@ -673,7 +673,8 @@ |
330 | mkfs_cmd = util.which("mk%s" % fs_type) |
331 | |
332 | if not mkfs_cmd: |
333 | - LOG.critical("Unable to locate command to create filesystem.") |
334 | + LOG.warn("Cannot create fstype '%s'. No mkfs.%s command", fs_type, |
335 | + fs_type) |
336 | return |
337 | |
338 | fs_cmd = [mkfs_cmd, device] |
339 | @@ -685,8 +686,8 @@ |
340 | if fs_opts: |
341 | fs_cmd.extend(fs_opts) |
342 | |
343 | - LOG.debug("Creating file system %s on %s" % (label, device)) |
344 | - LOG.debug(" Using cmd: %s" % "".join(fs_cmd)) |
345 | + LOG.debug("Creating file system %s on %s", label, device) |
346 | + LOG.debug(" Using cmd: %s", "".join(fs_cmd)) |
347 | try: |
348 | util.subp(fs_cmd) |
349 | except Exception as e: |
350 | |
351 | === modified file 'cloudinit/sources/DataSourceAzure.py' |
352 | --- cloudinit/sources/DataSourceAzure.py 2013-09-09 23:44:50 +0000 |
353 | +++ cloudinit/sources/DataSourceAzure.py 2013-09-27 22:26:03 +0000 |
354 | @@ -44,8 +44,20 @@ |
355 | 'policy': True, |
356 | 'command': BOUNCE_COMMAND, |
357 | 'hostname_command': 'hostname', |
358 | - } |
359 | -} |
360 | + }, |
361 | + 'disk_aliases': {'ephemeral0': '/dev/sdb'}, |
362 | +} |
363 | + |
364 | +BUILTIN_CLOUD_CONFIG = { |
365 | + 'disk_setup': { |
366 | + 'ephemeral0': {'table_type': 'mbr', |
367 | + 'layout': True, |
368 | + 'overwrite': False} |
369 | + }, |
370 | + 'fs_setup': [{'filesystem': 'ext4', 'device': 'ephemeral0', |
371 | + 'partition': 'auto'}], |
372 | +} |
373 | + |
374 | DS_CFG_PATH = ['datasource', DS_NAME] |
375 | |
376 | |
377 | @@ -94,7 +106,7 @@ |
378 | (md, self.userdata_raw, cfg, files) = ret |
379 | self.seed = cdev |
380 | self.metadata = util.mergemanydict([md, DEFAULT_METADATA]) |
381 | - self.cfg = cfg |
382 | + self.cfg = util.mergemanydict([cfg, BUILTIN_CLOUD_CONFIG]) |
383 | found = cdev |
384 | |
385 | LOG.debug("found datasource in %s", cdev) |
386 | @@ -112,8 +124,8 @@ |
387 | self.metadata['random_seed'] = seed |
388 | |
389 | # now update ds_cfg to reflect contents pass in config |
390 | - usercfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {}) |
391 | - self.ds_cfg = util.mergemanydict([usercfg, self.ds_cfg]) |
392 | + user_ds_cfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {}) |
393 | + self.ds_cfg = util.mergemanydict([user_ds_cfg, self.ds_cfg]) |
394 | mycfg = self.ds_cfg |
395 | |
396 | # walinux agent writes files world readable, but expects |
397 | @@ -161,9 +173,11 @@ |
398 | pubkeys = pubkeys_from_crt_files(fp_files) |
399 | |
400 | self.metadata['public-keys'] = pubkeys |
401 | - |
402 | return True |
403 | |
404 | + def device_name_to_device(self, name): |
405 | + return self.ds_cfg['disk_aliases'].get(name) |
406 | + |
407 | def get_config_obj(self): |
408 | return self.cfg |
409 | |
410 | @@ -349,7 +363,7 @@ |
411 | try: |
412 | dom = minidom.parseString(contents) |
413 | except Exception as e: |
414 | - raise NonAzureDataSource("invalid xml: %s" % e) |
415 | + raise BrokenAzureDataSource("invalid xml: %s" % e) |
416 | |
417 | results = find_child(dom.documentElement, |
418 | lambda n: n.localName == "ProvisioningSection") |
419 | |
420 | === modified file 'cloudinit/sources/DataSourceSmartOS.py' |
421 | --- cloudinit/sources/DataSourceSmartOS.py 2013-09-19 22:49:50 +0000 |
422 | +++ cloudinit/sources/DataSourceSmartOS.py 2013-09-27 22:26:03 +0000 |
423 | @@ -72,14 +72,17 @@ |
424 | 'iptables_disable'], |
425 | 'base64_keys': [], |
426 | 'base64_all': False, |
427 | - 'ephemeral_disk': '/dev/vdb', |
428 | + 'disk_aliases': {'ephemeral0': '/dev/vdb'}, |
429 | +} |
430 | + |
431 | +BUILTIN_CLOUD_CONFIG = { |
432 | 'disk_setup': { |
433 | 'ephemeral0': {'table_type': 'mbr', |
434 | - 'layout': True, |
435 | + 'layout': False, |
436 | 'overwrite': False} |
437 | }, |
438 | 'fs_setup': [{'label': 'ephemeral0', 'filesystem': 'ext3', |
439 | - 'device': '/dev/xvdb', 'partition': 'auto'}], |
440 | + 'device': 'ephemeral0', 'partition': 'auto'}], |
441 | } |
442 | |
443 | |
444 | @@ -94,9 +97,7 @@ |
445 | BUILTIN_DS_CONFIG]) |
446 | |
447 | self.metadata = {} |
448 | - self.cfg = {} |
449 | - self.cfg['disk_setup'] = self.ds_cfg.get('disk_setup') |
450 | - self.cfg['fs_setup'] = self.ds_cfg.get('fs_setup') |
451 | + self.cfg = BUILTIN_CLOUD_CONFIG |
452 | |
453 | self.seed = self.ds_cfg.get("serial_device") |
454 | self.seed_timeout = self.ds_cfg.get("serial_timeout") |
455 | @@ -154,8 +155,7 @@ |
456 | return True |
457 | |
458 | def device_name_to_device(self, name): |
459 | - if 'ephemeral0' in name: |
460 | - return self.ds_cfg['ephemeral_disk'] |
461 | + return self.ds_cfg['disk_aliases'].get(name) |
462 | |
463 | def get_config_obj(self): |
464 | return self.cfg |
465 | |
466 | === modified file 'doc/examples/cloud-config-disk-setup.txt' |
467 | --- doc/examples/cloud-config-disk-setup.txt 2013-09-19 22:49:50 +0000 |
468 | +++ doc/examples/cloud-config-disk-setup.txt 2013-09-27 22:26:03 +0000 |
469 | @@ -19,36 +19,36 @@ |
470 | |
471 | Default disk definitions for Windows Azure |
472 | ------------------------------------------ |
473 | -(Not implemented yet due to conflict with WALinuxAgent in Ubuntu) |
474 | |
475 | +device_aliases: {'ephemeral0': '/dev/sdb'} |
476 | disk_setup: |
477 | - /dev/sdb: |
478 | + ephemeral0: |
479 | type: mbr |
480 | layout: True |
481 | overwrite: False |
482 | |
483 | fs_setup: |
484 | - label: ephemeral0 |
485 | - filesystem: ext3 |
486 | + filesystem: ext4 |
487 | device: ephemeral0 |
488 | - partition: any |
489 | + partition: auto |
490 | |
491 | |
492 | Default disk definitions for SmartOS |
493 | ------------------------------------ |
494 | |
495 | -ephemeral_disk: /dev/vdb |
496 | +device_aliases: {'ephemeral0': '/dev/sdb'} |
497 | disk_setup: |
498 | - /dev/vdb: |
499 | + ephemeral0: |
500 | type: mbr |
501 | - layout: True |
502 | + layout: False |
503 | overwrite: False |
504 | |
505 | fs_setup: |
506 | - label: ephemeral0 |
507 | filesystem: ext3 |
508 | - device: /dev/vdb |
509 | - partition: 1 |
510 | + device: ephemeral0 |
511 | + partition: auto |
512 | |
513 | Cavaut for SmartOS: if ephemeral disk is not defined, then the disk will |
514 | not be automatically added to the mounts. |
515 | @@ -188,13 +188,43 @@ |
516 | of the ephemeral storage layer. |
517 | |
518 | <PART_VALUE>: The valid options are: |
519 | - "auto": auto is a special in the sense that you are telling cloud-init |
520 | - not to care whether there is a partition or not. Auto will put the |
521 | - first partition that does not contain a file system already. In |
522 | - the absence of a partition table, it will put it directly on the |
523 | - disk. |
524 | - |
525 | - "none": Put the partition directly on the disk. |
526 | + "auto|any": tell cloud-init not to care whether there is a partition |
527 | + or not. Auto will use the first partition that does not contain a |
528 | + file system already. In the absence of a partition table, it will |
529 | + put it directly on the disk. |
530 | + |
531 | + "auto": If a file system that matches the specification in terms of |
532 | + label, type and device, then cloud-init will skip the creation of |
533 | + the file system. |
534 | + |
535 | + "any": If a file system that matches the file system type and device, |
536 | + then cloud-init will skip the creation of the file system. |
537 | + |
538 | + Devices are selected based on first-detected, starting with partitions |
539 | + and then the raw disk. Consider the following: |
540 | + NAME FSTYPE LABEL |
541 | + xvdb |
542 | + ├─xvdb1 ext4 |
543 | + ├─xvdb2 |
544 | + ├─xvdb3 btrfs test |
545 | + └─xvdb4 ext4 test |
546 | + |
547 | + If you ask for 'auto', label of 'test, and file system of 'ext4' |
548 | + then cloud-init will select the 2nd partition, even though there |
549 | + is a partition match at the 4th partition. |
550 | + |
551 | + If you ask for 'any' and a label of 'test', then cloud-init will |
552 | + select the 1st partition. |
553 | + |
554 | + If you ask for 'auto' and don't define label, then cloud-init will |
555 | + select the 1st partition. |
556 | + |
557 | + In general, if you have a specific partition configuration in mind, |
558 | + you should define either the device or the partition number. 'auto' |
559 | + and 'any' are specifically intended for formating ephemeral storage or |
560 | + for simple schemes. |
561 | + |
562 | + "none": Put the file system directly on the device. |
563 | |
564 | <NUM>: where NUM is the actual partition number. |
565 | |
566 | |
567 | === modified file 'doc/sources/smartos/README.rst' |
568 | --- doc/sources/smartos/README.rst 2013-09-19 22:49:50 +0000 |
569 | +++ doc/sources/smartos/README.rst 2013-09-27 22:26:03 +0000 |
570 | @@ -73,15 +73,21 @@ |
571 | (i.e. /etc/cloud/cloud.cfg.d) that sets which values should not be |
572 | base64 decoded. |
573 | |
574 | -ephemeral_disk: |
575 | +disk_aliases and ephemeral disk: |
576 | --------------- |
577 | - |
578 | -In order to instruct Cloud-init which disk to auto-mount. By default, |
579 | -SmartOS only supports a single ephemeral disk. |
580 | - |
581 | -The default SmartOS configuration will prepare the ephemeral disk and format |
582 | -it for you. SmartOS does not, by default, prepare the ephemeral disk for you. |
583 | - |
584 | -If you change ephemeral_disk, you should also consider changing |
585 | -the default disk formatting parameters. See |
586 | -doc/examples/cloud-config-disk-setup.txt for information on using this. |
587 | +By default, SmartOS only supports a single ephemeral disk. That disk is |
588 | +completely empty (un-partitioned with no filesystem). |
589 | + |
590 | +The SmartOS datasource has built-in cloud-config which instructs the |
591 | +'disk_setup' module to partition and format the ephemeral disk. |
592 | + |
593 | +You can control the disk_setup then in 2 ways: |
594 | + 1. through the datasource config, you can change the 'alias' of |
595 | + ephermeral0 to reference another device. The default is: |
596 | + 'disk_aliases': {'ephemeral0': '/dev/vdb'}, |
597 | + Which means anywhere disk_setup sees a device named 'ephemeral0' |
598 | + then /dev/vdb will be substituted. |
599 | + 2. you can provide disk_setup or fs_setup data in user-data to overwrite |
600 | + the datasource's built-in values. |
601 | + |
602 | +See doc/examples/cloud-config-disk-setup.txt for information on disk_setup. |
603 | |
604 | === modified file 'tests/unittests/test_datasource/test_azure.py' |
605 | --- tests/unittests/test_datasource/test_azure.py 2013-08-15 17:16:01 +0000 |
606 | +++ tests/unittests/test_datasource/test_azure.py 2013-09-27 22:26:03 +0000 |
607 | @@ -120,8 +120,7 @@ |
608 | |
609 | mod = DataSourceAzure |
610 | |
611 | - if data.get('dsdevs'): |
612 | - self.apply_patches([(mod, 'list_possible_azure_ds_devs', dsdevs)]) |
613 | + self.apply_patches([(mod, 'list_possible_azure_ds_devs', dsdevs)]) |
614 | |
615 | self.apply_patches([(mod, 'invoke_agent', _invoke_agent), |
616 | (mod, 'write_files', _write_files), |
617 | @@ -154,9 +153,12 @@ |
618 | |
619 | def test_user_cfg_set_agent_command_plain(self): |
620 | # set dscfg in via plaintext |
621 | - cfg = {'agent_command': "my_command"} |
622 | + # we must have friendly-to-xml formatted plaintext in yaml_cfg |
623 | + # not all plaintext is expected to work. |
624 | + yaml_cfg = "{agent_command: my_command}\n" |
625 | + cfg = yaml.safe_load(yaml_cfg) |
626 | odata = {'HostName': "myhost", 'UserName': "myuser", |
627 | - 'dscfg': {'text': yaml.dump(cfg), 'encoding': 'plain'}} |
628 | + 'dscfg': {'text': yaml_cfg, 'encoding': 'plain'}} |
629 | data = {'ovfcontent': construct_valid_ovf_env(data=odata)} |
630 | |
631 | dsrc = self._get_ds(data) |
632 | @@ -290,11 +292,59 @@ |
633 | |
634 | self.assertEqual(data.get('apply_hostname_bounce', "N/A"), "N/A") |
635 | |
636 | + def test_default_ephemeral(self): |
637 | + # make sure the ephemeral device works |
638 | + odata = {} |
639 | + data = {'ovfcontent': construct_valid_ovf_env(data=odata), |
640 | + 'sys_cfg': {}} |
641 | + |
642 | + dsrc = self._get_ds(data) |
643 | + ret = dsrc.get_data() |
644 | + self.assertTrue(ret) |
645 | + cfg = dsrc.get_config_obj() |
646 | + |
647 | + self.assertEquals(dsrc.device_name_to_device("ephemeral0"), |
648 | + "/dev/sdb") |
649 | + assert 'disk_setup' in cfg |
650 | + assert 'fs_setup' in cfg |
651 | + self.assertIsInstance(cfg['disk_setup'], dict) |
652 | + self.assertIsInstance(cfg['fs_setup'], list) |
653 | + |
654 | + def test_provide_disk_aliases(self): |
655 | + # Make sure that user can affect disk aliases |
656 | + dscfg = {'disk_aliases': {'ephemeral0': '/dev/sdc'}} |
657 | + odata = {'HostName': "myhost", 'UserName': "myuser", |
658 | + 'dscfg': {'text': base64.b64encode(yaml.dump(dscfg)), |
659 | + 'encoding': 'base64'}} |
660 | + usercfg = {'disk_setup': {'/dev/sdc': {'something': '...'}, |
661 | + 'ephemeral0': False}} |
662 | + userdata = '#cloud-config' + yaml.dump(usercfg) + "\n" |
663 | + |
664 | + ovfcontent = construct_valid_ovf_env(data=odata, userdata=userdata) |
665 | + data = {'ovfcontent': ovfcontent, 'sys_cfg': {}} |
666 | + |
667 | + dsrc = self._get_ds(data) |
668 | + ret = dsrc.get_data() |
669 | + self.assertTrue(ret) |
670 | + cfg = dsrc.get_config_obj() |
671 | + self.assertTrue(cfg) |
672 | + self.assertEquals(dsrc.device_name_to_device("ephemeral0"), |
673 | + "/dev/sdc") |
674 | + |
675 | + def test_userdata_arrives(self): |
676 | + userdata = "This is my user-data" |
677 | + xml = construct_valid_ovf_env(data={}, userdata=userdata) |
678 | + data = {'ovfcontent': xml} |
679 | + dsrc = self._get_ds(data) |
680 | + dsrc.get_data() |
681 | + |
682 | + self.assertEqual(userdata, dsrc.userdata_raw) |
683 | + |
684 | |
685 | class TestReadAzureOvf(MockerTestCase): |
686 | def test_invalid_xml_raises_non_azure_ds(self): |
687 | invalid_xml = "<foo>" + construct_valid_ovf_env(data={}) |
688 | - self.assertRaises(DataSourceAzure.NonAzureDataSource, |
689 | + self.assertRaises(DataSourceAzure.BrokenAzureDataSource, |
690 | DataSourceAzure.read_azure_ovf, invalid_xml) |
691 | |
692 | def test_load_with_pubkeys(self): |
693 | |
694 | === modified file 'tests/unittests/test_datasource/test_smartos.py' |
695 | --- tests/unittests/test_datasource/test_smartos.py 2013-09-19 22:49:50 +0000 |
696 | +++ tests/unittests/test_datasource/test_smartos.py 2013-09-27 22:26:03 +0000 |
697 | @@ -79,7 +79,6 @@ |
698 | if self.last in self.mockdata: |
699 | if not self.mocked_out: |
700 | self.mocked_out = [x for x in self._format_out()] |
701 | - print self.mocked_out |
702 | |
703 | if len(self.mocked_out) > self.count: |
704 | self.count += 1 |
705 | @@ -275,26 +274,25 @@ |
706 | self.assertIsInstance(cfg['disk_setup'], dict) |
707 | self.assertIsInstance(cfg['fs_setup'], list) |
708 | |
709 | - def test_override_builtin_ds(self): |
710 | + def test_override_disk_aliases(self): |
711 | # Test to make sure that the built-in DS is overriden |
712 | - data = {} |
713 | - data['disk_setup'] = {'test_dev': {}} |
714 | - data['fs_setup'] = [{'label': 'test_dev'}] |
715 | - data['serial_device'] = '/dev/ttyS2' |
716 | - dsrc = self._get_ds(ds_cfg=data) |
717 | - cfg = dsrc.get_config_obj() |
718 | - |
719 | + builtin = DataSourceSmartOS.BUILTIN_DS_CONFIG |
720 | + |
721 | + mydscfg = {'disk_aliases': {'FOO': '/dev/bar'}} |
722 | + |
723 | + # expect that these values are in builtin, or this is pointless |
724 | + for k in mydscfg: |
725 | + self.assertIn(k, builtin) |
726 | + |
727 | + dsrc = self._get_ds(ds_cfg=mydscfg) |
728 | ret = dsrc.get_data() |
729 | self.assertTrue(ret) |
730 | |
731 | - assert 'disk_setup' in cfg |
732 | - assert 'fs_setup' in cfg |
733 | - self.assertIsInstance(cfg['disk_setup'], dict) |
734 | - self.assertIsInstance(cfg['fs_setup'], list) |
735 | - assert 'test_dev' in cfg['disk_setup'] |
736 | - assert 'test_dev' in cfg['fs_setup'][0]['label'] |
737 | + self.assertEqual(mydscfg['disk_aliases']['FOO'], |
738 | + dsrc.ds_cfg['disk_aliases']['FOO']) |
739 | |
740 | - self.assertEquals(data['serial_device'], dsrc.seed) |
741 | + self.assertEqual(dsrc.device_name_to_device('FOO'), |
742 | + mydscfg['disk_aliases']['FOO']) |
743 | |
744 | |
745 | def apply_patches(patches): |