diff options
author | Ben Howard <ben.howard@canonical.com> | 2013-09-19 16:49:50 -0600 |
---|---|---|
committer | Ben Howard <ben.howard@canonical.com> | 2013-09-19 16:49:50 -0600 |
commit | d1bad8880c2219b9d7a648169bbe7a0a27c03be2 (patch) | |
tree | 3080345395538f215cea2708982acbc408ead6ba | |
parent | 23f7b8a39bb197db557bdcf851639ea4111b7786 (diff) | |
download | vyos-cloud-init-d1bad8880c2219b9d7a648169bbe7a0a27c03be2.tar.gz vyos-cloud-init-d1bad8880c2219b9d7a648169bbe7a0a27c03be2.zip |
Fixes for the MP.
Changed cc_disk_setup to handle the file systems as a label, no longer
passing "log" around.
Tidied up the documentation to reflect the changes and made grammer,
spelling and improved the content a little.
Added disk_setup to the default modules list.
-rw-r--r-- | cloudinit/config/cc_disk_setup.py | 241 | ||||
-rw-r--r-- | cloudinit/sources/DataSourceSmartOS.py | 72 | ||||
-rw-r--r-- | config/cloud.cfg | 15 | ||||
-rw-r--r-- | doc/examples/cloud-config-disk-setup.txt | 100 | ||||
-rw-r--r-- | doc/sources/smartos/README.rst | 27 | ||||
-rw-r--r-- | tests/unittests/test_datasource/test_smartos.py | 35 |
6 files changed, 317 insertions, 173 deletions
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py index 6af2c9b1..fb404c5d 100644 --- a/cloudinit/config/cc_disk_setup.py +++ b/cloudinit/config/cc_disk_setup.py @@ -18,14 +18,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from cloudinit import util from cloudinit.settings import PER_INSTANCE -import re -import traceback +import logging +import shlex frequency = PER_INSTANCE -virtal_devices = ["ephemeral0", "swap"] -defmnts = ["ephemeral0", "swap"] - # Define the commands to use UDEVADM_CMD = util.which('udevadm') SFDISK_CMD = util.which("sfdisk") @@ -33,46 +30,47 @@ LSBLK_CMD = util.which("lsblk") BLKID_CMD = util.which("blkid") BLKDEV_CMD = util.which("blockdev") +LOG = logging.getLogger(__name__) + + def handle(_name, cfg, cloud, log, _args): """ Call util.prep_disk for disk_setup cloud-config. - The format is: - - disk_setup: - ephmeral0: {type='mbr', layout='True', overwrite='False'} - /dev/xvdj: {type='None'} - /dev/xvdh: {type='mbr', layout:[(33,83),66], overwrite='True'} - - fs_setup: - ephemeral0: {filesystem='ext3', device='ephemeral0', partition='auto'} - mylabel2: {filesystem='ext3', device='/dev/xvda1', partition='None'} - special1: {cmd="mkfs -t %(FILESYSTEM)s -L %(LABEL)s %(DEVICE)s", filesystem='btrfs', device='/dev/xvda1'} - - + See doc/examples/cloud-config_disk-setup.txt for documentation on the + format. """ - disk_setup = cfg.get("disk_setup") if isinstance(disk_setup, dict): log.info("Partitioning disks.") for disk, definition in disk_setup.items(): if not isinstance(definition, dict): - log.debug("Invalid disk definition for %s" % disk) + log.warn("Invalid disk definition for %s" % disk) continue - util.log_time(logfunc=log.info, + try: + log.debug("Creating new partition table/disk") + util.log_time(logfunc=LOG.debug, msg="Creating partition on %s" % disk, - func=mkpart, args=(disk, cloud, definition, log)) + func=mkpart, args=(disk, cloud, definition)) + except Exception as e: + util.logexc(LOG, "Failed partitioning operation\n%s" % e) fs_setup = cfg.get("fs_setup") - if isinstance(fs_setup, dict): - log.info("Setting up filesystems") - for label, definition in fs_setup.items(): + if isinstance(fs_setup, list): + log.info("Creating file systems.") + for definition in fs_setup: if not isinstance(definition, dict): - log.debug("Invalid filesystem definition for %s" % label) - continue + log.warn("Invalid file system definition: %s" % definition) + continue - util.log_time(logfunc=log.debug, msg="Creating fs for %s" % label, - func=mkfs, args=(label, cloud, definition, log)) + try: + log.debug("Creating new filesystem.") + device = definition.get('device') + util.log_time(logfunc=LOG.debug, + msg="Creating fs for %s" % device, + func=mkfs, args=(cloud, definition)) + except Exception as e: + util.logexc(LOG, "Failed during filesystem operation\n%s" % e) def is_default_device(name, cloud, fallback=None): @@ -82,10 +80,11 @@ def is_default_device(name, cloud, fallback=None): fallback if so defined. """ + _dev = None try: _dev = cloud.device_name_to_device(name) except Exception as e: - print e + util.logexc(LOG, "Failed to find mapping for %s" % e) if _dev: return _dev @@ -96,34 +95,19 @@ def is_default_device(name, cloud, fallback=None): return name -def check_value(key, dct, default=None): - """ - Convience function for getting value out of a dict. - """ - - if key in dct: - return dct[key] - if default: - return default - return None - - def value_splitter(values, start=None): """ Returns the key/value pairs of output sent as string like: FOO='BAR' HOME='127.0.0.1' """ - _values = values.split() + _values = shlex.split(values) if start: _values = _values[start:] for key, value in [x.split('=') for x in _values]: - if value == '""': - value = None - elif '"' in value: - value = value.replace('"','') yield key, value + def device_type(device): """ Return the device type of the device by calling lsblk. @@ -148,7 +132,13 @@ def is_device_valid(name, partition=False): """ Check if the device is a valid device. """ - d_type = device_type(name) + d_type = "" + try: + d_type = device_type(name) + except: + LOG.warn("Query against device %s failed" % name) + return False + if partition and d_type == 'part': return True elif not partition and d_type == 'disk': @@ -171,7 +161,6 @@ def check_fs(device): try: out, _err = util.subp(blkid_cmd, rcs=[0, 2]) except Exception as e: - util.logexc(e) raise Exception("Failed during disk check for %s\n%s" % (device, e)) if out: @@ -195,7 +184,8 @@ def is_filesystem(device): return fs_type -def find_device_node(device, fs_type=None, label=None, valid_targets=None): +def find_device_node(device, fs_type=None, label=None, valid_targets=None, + label_match=True): """ Find a device that is either matches the spec, or the first @@ -208,7 +198,8 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None): if not valid_targets: valid_targets = ['disk', 'part'] - lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE,FSTYPE,LABEL', device] + lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE,FSTYPE,LABEL', + device] info = None try: info, _err = util.subp(lsblk_cmd) @@ -216,7 +207,7 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None): raise Exception("Failed during disk check for %s\n%s" % (device, e)) raw_device_used = False - parts = [ x for x in (info.strip()).splitlines() if len(x.split()) > 0 ] + parts = [x for x in (info.strip()).splitlines() if len(x.split()) > 0] for part in parts: d = {'name': None, @@ -228,7 +219,8 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None): for key, value in value_splitter(part): d[key.lower()] = value - if d['fstype'] == fs_type and d['label'] == label: + if d['fstype'] == fs_type and \ + ((label_match and d['label'] == label) or not label_match): # If we find a matching device, we return that return ('/dev/%s' % d['name'], True) @@ -247,6 +239,7 @@ def find_device_node(device, fs_type=None, label=None, valid_targets=None): if not raw_device_used: return (device, False) + LOG.warn("Failed to find device during available device search.") return (None, False) @@ -262,6 +255,8 @@ def is_disk_used(device): info, _err = util.subp(lsblk_cmd) except Exception as e: # if we error out, we can't use the device + util.logexc(LOG, + "Error checking for filesystem on %s\n%s" % (device, e)) return True # If there is any output, then the device has something @@ -313,6 +308,7 @@ def get_dyn_func(*args): except KeyError: raise Exception("No such function %s to call!" % func_name) + def check_partition_mbr_layout(device, layout): """ Returns true if the partition layout matches the one on the disk @@ -363,7 +359,7 @@ def check_partition_mbr_layout(device, layout): # This just makes sure that the number of requested # partitions and the type labels are right for x in range(1, len(layout) + 1): - if isinstance(layout[x-1], tuple): + if isinstance(layout[x - 1], tuple): _, part_type = layout[x] if int(found_layout[x]) != int(part_type): return False @@ -455,10 +451,13 @@ def read_parttbl(device): reliable way to probe the partition table. """ blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device] + udev_cmd = [UDEVADM_CMD, 'settle'] try: + util.subp(udev_cmd) util.subp(blkdev_cmd) + util.subp(udev_cmd) except Exception as e: - raise Exception("Failed on call to partprobe\n%s" % e) + util.logexc(LOG, "Failed reading the partition table %s" % e) def exec_mkpart_mbr(device, layout): @@ -475,6 +474,7 @@ def exec_mkpart_mbr(device, layout): read_parttbl(device) + def exec_mkpart(table_type, device, layout): """ Fetches the function for creating the table type. @@ -488,7 +488,7 @@ def exec_mkpart(table_type, device, layout): return get_dyn_func("exec_mkpart_%s", table_type, device, layout) -def mkpart(device, cloud, definition, log): +def mkpart(device, cloud, definition): """ Creates the partition table. @@ -504,14 +504,14 @@ def mkpart(device, cloud, definition, log): device: the device to work on. """ - log.debug("Checking values for %s definition" % device) - overwrite = check_value('overwrite', definition, False) - layout = check_value('layout', definition, False) - table_type = check_value('type', definition, 'mbr') + LOG.debug("Checking values for %s definition" % device) + overwrite = definition.get('overwrite', False) + layout = definition.get('layout', False) + table_type = definition.get('table_type', 'mbr') _device = is_default_device(device, cloud) # Check if the default device is a partition or not - log.debug("Checking against default devices") + LOG.debug("Checking against default devices") if _device and (_device != device): if not is_device_valid(_device): _device = _device[:-1] @@ -519,40 +519,43 @@ def mkpart(device, cloud, definition, log): if not is_device_valid(_device): raise Exception("Unable to find backing block device for %s" % \ device) + else: + LOG.debug("Mapped %s to physical device %s" % (device, _device)) + device = _device if (isinstance(layout, bool) and not layout) or not layout: - log.debug("Device is not to be partitioned, skipping") + LOG.debug("Device is not to be partitioned, skipping") return # Device is not to be partitioned # This prevents you from overwriting the device - log.debug("Checking if device %s is a valid device" % device) + LOG.debug("Checking if device %s is a valid device" % device) if not is_device_valid(device): raise Exception("Device %s is not a disk device!" % device) - log.debug("Checking if device layout matches") + LOG.debug("Checking if device layout matches") if check_partition_layout(table_type, device, layout): - log.debug("Device partitioning layout matches") + LOG.debug("Device partitioning layout matches") return True - log.debug("Checking if device is safe to partition") + LOG.debug("Checking if device is safe to partition") if not overwrite and (is_disk_used(device) or is_filesystem(device)): - log.debug("Skipping partitioning on configured device %s" % device) + LOG.debug("Skipping partitioning on configured device %s" % device) return - log.debug("Checking for device size") + LOG.debug("Checking for device size") device_size = get_hdd_size(device) - log.debug("Calculating partition layout") + LOG.debug("Calculating partition layout") part_definition = get_partition_layout(table_type, device_size, layout) - log.debug(" Layout is: %s" % part_definition) + LOG.debug(" Layout is: %s" % part_definition) - log.debug("Creating partition table on %s" % device) + LOG.debug("Creating partition table on %s" % device) exec_mkpart(table_type, device, part_definition) - log.debug("Partition table created for %s" % device) + LOG.debug("Partition table created for %s" % device) -def mkfs(label, cloud, fs_cfg, log): +def mkfs(cloud, fs_cfg): """ Create a file system on the device. @@ -568,68 +571,94 @@ def mkfs(label, cloud, fs_cfg, log): first free device or the first device which matches both label and type will be used. + 'any' means the first filesystem that matches + on the device. + When 'cmd' is provided then no other parameter is required. """ - device = check_value('device', fs_cfg) - partition = str(check_value('partition', fs_cfg)) - fs_type = check_value('filesystem', fs_cfg) - fs_cmd = check_value('cmd', fs_cfg, []) - fs_opts = check_value('extra_opts', fs_cfg, []) - overwrite = check_value('overwrite', fs_cfg, False) + fs_cfg['partition'] = 'any' + label = fs_cfg.get('label') + device = fs_cfg.get('device') + partition = str(fs_cfg.get('partition')) + fs_type = fs_cfg.get('filesystem') + fs_cmd = fs_cfg.get('cmd', []) + fs_opts = fs_cfg.get('extra_opts', []) + overwrite = fs_cfg.get('overwrite', False) # This allows you to define the default ephemeral or swap - log.debug("Checking %s label against default devices" % label) - device = is_default_device(label, cloud, fallback=device) + LOG.debug("Checking %s against default devices" % device) + _device = is_default_device(label, cloud, fallback=device) + if _device and (_device != device): + if not is_device_valid(_device): + raise Exception("Unable to find backing block device for %s" % \ + device) + else: + LOG.debug("Mapped %s to physical device %s" % (device, _device)) + device = _device if not partition or partition.isdigit(): # Handle manual definition of partition if partition.isdigit(): device = "%s%s" % (device, partition) - log.debug("Manual request of partition %s for %s" % ( - partition, device)) + LOG.debug("Manual request of partition %s for %s" % ( + partition, device)) # Check to see if the fs already exists - log.debug("Checking device %s" % device) + LOG.debug("Checking device %s" % device) check_label, check_fstype, _ = check_fs(device) - log.debug("Device %s has %s %s" % (device, check_label, check_fstype)) + LOG.debug("Device %s has %s %s" % (device, check_label, check_fstype)) if check_label == label and check_fstype == fs_type: - log.debug("Existing file system found at %s" % device) + LOG.debug("Existing file system found at %s" % device) if not overwrite: - log.debug("Device %s has required file system" % device) + LOG.warn("Device %s has required file system" % device) return else: - log.debug("Destroying filesystem on %s" % device) + LOG.warn("Destroying filesystem on %s" % device) else: - log.debug("Device %s is cleared for formating" % device) + LOG.debug("Device %s is cleared for formating" % device) - elif partition and partition == 'auto': + elif partition and str(partition).lower() in ('auto', 'any'): # For auto devices, we match if the filesystem does exist - log.debug("Identifying device to create %s filesytem on" % label) - device, reuse = find_device_node(device, fs_type=fs_type, label=label) - log.debug("Device identified as %s" % device) + odevice = device + LOG.debug("Identifying device to create %s filesytem on" % label) + + # any mean pick the first match on the device with matching fs_type + label_match = True + if partition.lower() == 'any': + label_match = False + + device, reuse = find_device_node(device, fs_type=fs_type, label=label, + label_match=label_match) + LOG.debug("Automatic device for %s identified as %s" % ( + odevice, device)) if reuse: - log.debug("Found filesystem match, skipping formating.") + LOG.debug("Found filesystem match, skipping formating.") + return + + if not device: + LOG.debug("No device aviable that matches request.") + LOG.debug("Skipping fs creation for %s" % fs_cfg) return else: - log.debug("Error in device identification handling.") + LOG.debug("Error in device identification handling.") return - - log.debug("File system %s will be created on %s" % (label, device)) + LOG.debug("File system %s will be created on %s" % (label, device)) # Make sure the device is defined if not device: - raise Exception("Device identification error for %s" % label) + LOG.critical("Device is not known: %s" % fs_cfg) + return # Check that we can create the FS if not label or not fs_type: - log.debug("Command to create filesystem %s is bad. Skipping." % \ - label) + LOG.debug("Command to create filesystem %s is bad. Skipping." % \ + label) # Create the commands if fs_cmd: @@ -638,22 +667,26 @@ def mkfs(label, cloud, fs_cfg, log): 'device': device, } else: + # Find the mkfs command mkfs_cmd = util.which("mkfs.%s" % fs_type) if not mkfs_cmd: mkfs_cmd = util.which("mk%s" % fs_type) if not mkfs_cmd: - log.debug("Unable to locate command to create filesystem.") + LOG.critical("Unable to locate command to create filesystem.") return - fs_cmd = [mkfs_cmd, "-L", label, device] + fs_cmd = [mkfs_cmd, device] + + if label: + fs_cmd.extend(["-L", label]) # Add the extends FS options if fs_opts: fs_cmd.extend(fs_opts) - log.debug("Creating file system %s on %s" % (label, device)) - print fs_cmd + LOG.debug("Creating file system %s on %s" % (label, device)) + LOG.debug(" Using cmd: %s" % "".join(fs_cmd)) try: util.subp(fs_cmd) except Exception as e: diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py index d348d20b..da1eec79 100644 --- a/cloudinit/sources/DataSourceSmartOS.py +++ b/cloudinit/sources/DataSourceSmartOS.py @@ -35,8 +35,7 @@ import os import os.path import serial -DEF_TTY_LOC = '/dev/ttyS1' -DEF_TTY_TIMEOUT = 60 + LOG = logging.getLogger(__name__) SMARTOS_ATTRIB_MAP = { @@ -49,24 +48,61 @@ SMARTOS_ATTRIB_MAP = { 'motd_sys_info': ('motd_sys_info', True), } -# These are values which will never be base64 encoded. -# They come from the cloud platform, not user -SMARTOS_NO_BASE64 = ['root_authorized_keys', 'motd_sys_info', - 'iptables_disable'] +DS_NAME = 'SmartOS' +DS_CFG_PATH = ['datasource', DS_NAME] +# BUILT-IN DATASOURCE CONFIGURATION +# The following is the built-in configuration. If the values +# are not set via the system configuration, then these default +# will be used: +# serial_device: which serial device to use for the meta-data +# seed_timeout: how long to wait on the device +# no_base64_decode: values which are not base64 encoded and +# are fetched directly from SmartOS, not meta-data values +# base64_keys: meta-data keys that are delivered in base64 +# base64_all: with the exclusion of no_base64_decode values, +# treat all meta-data as base64 encoded +# disk_setup: describes how to partition the ephemeral drive +# fs_setup: describes how to format the ephemeral drive +# +BUILTIN_DS_CONFIG = { + 'serial_device': '/dev/ttyS1', + 'seed_timeout': 60, + 'no_base64_decode': ['root_authorized_keys', + 'motd_sys_info', + 'iptables_disable'], + 'base64_keys': [], + 'base64_all': False, + 'ephemeral_disk': '/dev/vdb', + 'disk_setup': { + 'ephemeral0': {'table_type': 'mbr', + 'layout': True, + 'overwrite': False} + }, + 'fs_setup': [{'label': 'ephemeral0', 'filesystem': 'ext3', + 'device': '/dev/xvdb', 'partition': 'auto'}], +} class DataSourceSmartOS(sources.DataSource): def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) - self.seed_dir = os.path.join(paths.seed_dir, 'sdc') self.is_smartdc = None - self.seed = self.ds_cfg.get("serial_device", DEF_TTY_LOC) - self.seed_timeout = self.ds_cfg.get("serial_timeout", DEF_TTY_TIMEOUT) - self.smartos_no_base64 = self.ds_cfg.get('no_base64_decode', - SMARTOS_NO_BASE64) - self.b64_keys = self.ds_cfg.get('base64_keys', []) - self.b64_all = self.ds_cfg.get('base64_all', False) + self.ds_cfg = util.mergemanydict([ + self.ds_cfg, + util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}), + BUILTIN_DS_CONFIG]) + + self.metadata = {} + self.cfg = {} + self.cfg['disk_setup'] = self.ds_cfg.get('disk_setup') + self.cfg['fs_setup'] = self.ds_cfg.get('fs_setup') + + self.seed = self.ds_cfg.get("serial_device") + self.seed_timeout = self.ds_cfg.get("serial_timeout") + self.smartos_no_base64 = self.ds_cfg.get('no_base64_decode') + self.b64_keys = self.ds_cfg.get('base64_keys') + self.b64_all = self.ds_cfg.get('base64_all') def __str__(self): root = sources.DataSource.__str__(self) @@ -79,7 +115,6 @@ class DataSourceSmartOS(sources.DataSource): if not os.path.exists(self.seed): LOG.debug("Host does not appear to be on SmartOS") return False - self.seed = self.seed dmi_info = dmi_data() if dmi_info is False: @@ -114,10 +149,17 @@ class DataSourceSmartOS(sources.DataSource): elif md['user-script']: ud = md['user-script'] - self.metadata = md + self.metadata = util.mergemanydict([md, self.metadata]) self.userdata_raw = ud return True + def device_name_to_device(self, name): + if 'ephemeral0' in name: + return self.ds_cfg['ephemeral_disk'] + + def get_config_obj(self): + return self.cfg + def get_instance_id(self): return self.metadata['instance-id'] diff --git a/config/cloud.cfg b/config/cloud.cfg index 8b52ca39..382cfaf8 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -14,20 +14,6 @@ disable_root: true # This will cause the set+update hostname module to not operate (if true) preserve_hostname: false -# This sets the default for creating the ephemeral0 on clouds -# that support it. -disk_setup: - ephemeral0 - type: 'mbr' - layout: True - overwrite: False - -fs_setup: - ephemeral0 - filesystem: 'ext3' - device: ephemeral0 - partition: 'auto' - # Example datasource config # datasource: # Ec2: @@ -56,6 +42,7 @@ cloud_config_modules: # Emit the cloud config ready event # this can be used by upstart jobs for 'start on cloud-config'. - emit_upstart + - disk_setup - mounts - ssh-import-id - locale diff --git a/doc/examples/cloud-config-disk-setup.txt b/doc/examples/cloud-config-disk-setup.txt index 518936d1..db2c52a7 100644 --- a/doc/examples/cloud-config-disk-setup.txt +++ b/doc/examples/cloud-config-disk-setup.txt @@ -1,24 +1,63 @@ -Cloud-init supports the creation of simple partition tables and filesystems +Cloud-init supports the creation of simple partition tables and file systems on devices. -default disk definitions ------------------------- +Default disk definitions for AWS +-------------------------------- +(Not implemented yet, but provided for future documentation) disk_setup: ephmeral0: type: 'mbr' layout: True overwrite: False + fs_setup: - ephemeral0: - filesystem: 'ext3' - device: 'ephemeral0' - partition: 'auto' + - label: None, + filesystem: ext3 + device: ephemeral0 + partition: auto + +Default disk definitions for Windows Azure +------------------------------------------ +(Not implemented yet due to conflict with WALinuxAgent in Ubuntu) + +disk_setup: + /dev/sdb: + type: mbr + layout: True + overwrite: False + +fs_setup: + - label: ephemeral0 + filesystem: ext3 + device: ephemeral0 + partition: any + + +Default disk definitions for SmartOS +------------------------------------ + +ephemeral_disk: /dev/vdb +disk_setup: + /dev/vdb: + type: mbr + layout: True + overwrite: False + +fs_setup: + - label: ephemeral0 + filesystem: ext3 + device: /dev/vdb + partition: 1 + +Cavaut for SmartOS: if ephemeral disk is not defined, then the disk will + not be automatically added to the mounts. + The default definition is used to make sure that the ephemeral storage is setup properly. -"disk_setup": disk parititioning +"disk_setup": disk partitioning -------------------------------- The disk_setup directive instructs Cloud-init to partition a disk. The format is: @@ -36,7 +75,7 @@ The disk_setup directive instructs Cloud-init to partition a disk. The format is overwrite: True The format is a list of dicts of dicts. The first value is the name of the -device and the subsiquent values define how to create and layout the partition. +device and the subsequent values define how to create and layout the partition. The general format is: disk_setup: @@ -113,28 +152,29 @@ fs_setup: Setup the file system fs_setup describes the how the file systems are supposed to look. fs_setup: - ephemeral0: - filesystem: 'ext3' - device: 'ephemeral0' - partition: 'auto' - mylabl2: - filesystem: 'ext4' - device: '/dev/xvda1' - special: - cmd: mkfs -t %(FILESYSTEM)s -L %(LABEL)s %(DEVICE)s - filesystem: 'btrfs' - device: '/dev/xvdh' + - label: ephemeral0 + filesystem: 'ext3' + device: 'ephemeral0' + partition: 'auto' + - label: mylabl2 + filesystem: 'ext4' + device: '/dev/xvda1' + - special: + cmd: mkfs -t %(FILESYSTEM)s -L %(LABEL)s %(DEVICE)s + filesystem: 'btrfs' + device: '/dev/xvdh' The general format is: fs_setup: - <LABEL>: - filesystem: <FS_TYPE> - device: <DEVICE> - partition: <PART_VALUE> - overwrite: <OVERWRITE> + - label: <LABEL> + filesystem: <FS_TYPE> + device: <DEVICE> + partition: <PART_VALUE> + overwrite: <OVERWRITE> Where: - <LABEL>: The file system label to be used. + <LABEL>: The file system label to be used. If set to None, no label is + used. <FS_TYPE>: The file system type. It is assumed that the there will be a "mkfs.<FS_TYPE>" that behaves likes "mkfs". On a standard @@ -166,13 +206,5 @@ Where: "false": If an existing file system exists, skip the creation. - "force": Recreate the file system, even it already exists - - Behavior Caveat: The default behavior is to _check_ if the file system exists. If a file system matches the specification, then the operation is a no-op. - - For 'ephemeralX' or 'swap' labeled filesystems, the operation will be a - no-op if a file system of the same type is present, regardless of the label. - This is to accommodate Clouds like EC2 that present a blank file system with - out a label. diff --git a/doc/sources/smartos/README.rst b/doc/sources/smartos/README.rst index fd4e496d..e2d3312e 100644 --- a/doc/sources/smartos/README.rst +++ b/doc/sources/smartos/README.rst @@ -5,11 +5,13 @@ SmartOS Datasource This datasource finds metadata and user-data from the SmartOS virtualization platform (i.e. Joyent). +Please see http://smartos.org/ for information about SmartOS. + SmartOS Platform ---------------- -The SmartOS virtualization platform meta-data to the instance via the second -serial console. On Linux, this is /dev/ttyS1. The data is a provided via a -simple protocol, where something queries for the userdata, where the console +The SmartOS virtualization platform uses meta-data to the instance via the +second serial console. On Linux, this is /dev/ttyS1. The data is a provided +via a simple protocol: something queries for the data, the console responds responds with the status and if "SUCCESS" returns until a single ".\n". New versions of the SmartOS tooling will include support for base64 encoded data. @@ -18,7 +20,7 @@ Userdata -------- In SmartOS parlance, user-data is a actually meta-data. This userdata can be -provided a key-value pairs. +provided as key-value pairs. Cloud-init supports reading the traditional meta-data fields supported by the SmartOS tools. These are: @@ -36,13 +38,13 @@ user-script SmartOS traditionally supports sending over a user-script for execution at the rc.local level. Cloud-init supports running user-scripts as if they were cloud-init user-data. In this sense, anything with a shell interpreter -directive will run +directive will run. user-data and user-script ------------------------- In the event that a user defines the meta-data key of "user-data" it will -always supercede any user-script data. This is for consistency. +always supersede any user-script data. This is for consistency. base64 ------ @@ -70,3 +72,16 @@ or not to base64 decode something: * no_base64_decode: This is a configuration setting (i.e. /etc/cloud/cloud.cfg.d) that sets which values should not be base64 decoded. + +ephemeral_disk: +--------------- + +In order to instruct Cloud-init which disk to auto-mount. By default, +SmartOS only supports a single ephemeral disk. + +The default SmartOS configuration will prepare the ephemeral disk and format +it for you. SmartOS does not, by default, prepare the ephemeral disk for you. + +If you change ephemeral_disk, you should also consider changing +the default disk formatting parameters. See +doc/examples/cloud-config-disk-setup.txt for information on using this. diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py index f53715b0..56fe811e 100644 --- a/tests/unittests/test_datasource/test_smartos.py +++ b/tests/unittests/test_datasource/test_smartos.py @@ -261,6 +261,41 @@ class TestSmartOSDataSource(MockerTestCase): self.assertEquals(MOCK_RETURNS['enable_motd_sys_info'], dsrc.metadata['motd_sys_info']) + def test_default_ephemeral(self): + # Test to make sure that the builtin config has the ephemeral + # configuration. + dsrc = self._get_ds() + cfg = dsrc.get_config_obj() + + ret = dsrc.get_data() + self.assertTrue(ret) + + assert 'disk_setup' in cfg + assert 'fs_setup' in cfg + self.assertIsInstance(cfg['disk_setup'], dict) + self.assertIsInstance(cfg['fs_setup'], list) + + def test_override_builtin_ds(self): + # Test to make sure that the built-in DS is overriden + data = {} + data['disk_setup'] = {'test_dev': {}} + data['fs_setup'] = [{'label': 'test_dev'}] + data['serial_device'] = '/dev/ttyS2' + dsrc = self._get_ds(ds_cfg=data) + cfg = dsrc.get_config_obj() + + ret = dsrc.get_data() + self.assertTrue(ret) + + assert 'disk_setup' in cfg + assert 'fs_setup' in cfg + self.assertIsInstance(cfg['disk_setup'], dict) + self.assertIsInstance(cfg['fs_setup'], list) + assert 'test_dev' in cfg['disk_setup'] + assert 'test_dev' in cfg['fs_setup'][0]['label'] + + self.assertEquals(data['serial_device'], dsrc.seed) + def apply_patches(patches): ret = [] |