ifeoktistov has proposed merging lp:~ifeoktistov/cloud-init/remotedisk-setup into lp:cloud-init.
Requested reviews: cloud init development team (cloud-init-dev) For more details, see: https://code.launchpad.net/~ifeoktistov/cloud-init/remotedisk-setup/+merge/294055 Added remotedisk-setup config module which provides a simple and uniform way to handle remote disks such as: - iSCSI LUN's: - configures Open iSCSI initiator; - configures device multipath; - enables necessary services; - attaches iSCSI LUN; - discovers multipath device; - creates logical volume; - creates filesystem; - mounts filesystem; - configures /etc/fstab - Hypervisor disks (OpenStack Cinder volumes, AWS EBS, etc): - creates logical volume; - creates filesystem; - mounts filesystem; - configures /etc/fstab - NFS shares: - mounts NFS share; - configures /etc/fstab The module was extensively tested on RHEL/CentOS 6.7, RHEL/CentOS 7.2, and Ubuntu 14.04 in OpenStack/KVM and AWS. iSCSI is tested against NetApp cDOT storage. -- Your team cloud init development team is requested to review the proposed merge of lp:~ifeoktistov/cloud-init/remotedisk-setup into lp:cloud-init.
=== added file 'cloudinit/config/cc_remotedisk_setup.py' --- cloudinit/config/cc_remotedisk_setup.py 1970-01-01 00:00:00 +0000 +++ cloudinit/config/cc_remotedisk_setup.py 2016-05-06 21:33:46 +0000 @@ -0,0 +1,678 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# +# Author: Igor Feoktistov <[email protected]> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import logging +import os +import time +import shlex +import fnmatch +import subprocess +import re +from string import whitespace + +from cloudinit.settings import PER_INSTANCE +from cloudinit import type_utils +from cloudinit import util +from cloudinit import templater + +frequency = PER_INSTANCE + +WAIT_4_BLOCKDEV_MAPPING_ITER = 60 +WAIT_4_BLOCKDEV_MAPPING_SLEEP = 5 +WAIT_4_BLOCKDEV_DEVICE_ITER = 12 +WAIT_4_BLOCKDEV_DEVICE_SLEEP = 5 + +LVM_CMD = util.which("lvm") +ISCSIADM_CMD = util.which("iscsiadm") +MULTIPATH_CMD = util.which("multipath") +SYSTEMCTL_CMD = util.which("systemctl") +CHKCONFIG_CMD = util.which("chkconfig") +SERVICE_CMD = util.which("service") +FSTAB_PATH = "/etc/fstab" +ISCSI_INITIATOR_PATH = "/etc/iscsi/initiatorname.iscsi" + + +def handle(_name, cfg, cloud, log, _args): + if "remotedisk_setup" not in cfg: + log.debug("Skipping module named %s, no configuration found" % _name) + return + remotedisk_setup = cfg.get("remotedisk_setup") + log.debug("setting up remote disk: %s", str(remotedisk_setup)) + for definition in remotedisk_setup: + try: + device = definition.get("device") + if device: + if device.startswith("iscsi"): + handle_iscsi(cfg, cloud, log, definition) + elif device.startswith("nfs"): + handle_nfs(cfg, cloud, log, definition) + elif device.startswith("ebs"): + handle_ebs(cfg, cloud, log, definition) + elif device.startswith("ephemeral"): + handle_ebs(cfg, cloud, log, definition) + else: + if "fs_type" in definition: + fs_type = definition.get("fs_type") + if fs_type == "nfs": + handle_nfs(cfg, cloud, log, definition) + else: + handle_ebs(cfg, cloud, log, definition) + else: + util.logexc(log, "Expexted \"fs_type\" parameter") + else: + util.logexc(log, "Expexted \"device\" parameter") + except Exception as e: + util.logexc(log, "Failed during remote disk operation\n" + "Exception: %s" % e) + + +def handle_iscsi(cfg, cloud, log, definition): + # Handle iSCSI LUN + device = definition.get("device") + try: + (iscsi_host, + iscsi_proto, + iscsi_port, + iscsi_lun, + iscsi_target) = device.split(":", 5)[1:] + except Exception as e: + util.logexc(log, + "handle_iscsi: " + "expected \"device\" attribute in the format: " + "\"iscsi:<iSCSI host>:<protocol>:<port>:<LUN>:" + "<iSCSI target name>\": %s" % e) + return + (hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud) + if "initiator_name" in definition: + initiator_name = definition.get("initiator_name") + else: + initiator_name = "iqn.2005-02.com.open-iscsi:%s" % hostname + util.write_file(ISCSI_INITIATOR_PATH, "InitiatorName=%s" % initiator_name) + multipath_tmpl_fn = cloud.get_template_filename("multipath.conf") + if not multipath_tmpl_fn: + util.logexc(log, "handle_iscsi: template multipath.conf not found") + return + templater.render_to_file(multipath_tmpl_fn, "/etc/multipath.conf", {}) + if cloud.distro.osfamily == "redhat": + iscsi_services = ["iscsi", "iscsid"] + multipath_services = ["multipathd"] + elif cloud.distro.osfamily == 'debian': + iscsi_services = ["open-iscsi"] + multipath_services = ["multipath-tools"] + else: + util.logexc(log, + "handle_iscsi: " + "unsupported osfamily \"%s\"" % cloud.distro.osfamily) + return + for service in iscsi_services: + _service_wrapper(cloud, log, service, "enable") + _service_wrapper(cloud, log, service, "restart") + for service in multipath_services: + _service_wrapper(cloud, log, service, "enable") + _service_wrapper(cloud, log, service, "restart") + blockdev = _iscsi_lun_discover(log, + iscsi_host, + iscsi_port, + iscsi_lun, + iscsi_target) + if blockdev: + lvm_group = definition.get("lvm_group") + lvm_volume = definition.get("lvm_volume") + fs_type = definition.get("fs_type") + fs_opts = definition.get("fs_opts") + mount_point = definition.get("mount_point") + mount_opts = definition.get("mount_opts") + if not mount_opts: + mount_opts = 'defaults,_netdev' + else: + if mount_opts.find("_netdev") == -1: + mount_opts = "%s,_netdev" % (mount_opts) + fs_freq = definition.get("fs_freq") + if not fs_freq: + fs_freq = "1" + fs_passno = definition.get("fs_passno") + if not fs_passno: + fs_passno = "2" + if lvm_group and lvm_volume: + for vg_name in _list_vg_names(): + if vg_name == lvm_group: + util.logexc(log, + "handle_iscsi: " + "logical volume group '%s' exists already" + % lvm_group) + return + for lv_name in _list_lv_names(): + if lv_name == lvm_volume: + util.logexc(log, + "handle_iscsi: " + "logical volume '%s' exists already" + % lvm_volume) + return + blockdev = _create_lv(log, blockdev, lvm_group, lvm_volume) + if blockdev: + if mount_point and fs_type: + _create_fs(log, blockdev, fs_type, fs_opts) + _add_fstab_entry(log, + blockdev, + mount_point, + fs_type, + mount_opts, + fs_freq, + fs_passno) + _mount_fs(log, mount_point) + else: + util.logexc(log, + "handle_iscsi: " + "expexted \"mount_point\" " + "and \"fs_type\" parameters") + + +def handle_nfs(cfg, cloud, log, definition): + # Handle NFS share mounts + device = definition.get("device") + if device.startswith("nfs"): + (proto, share_path) = device.split(":", 1) + else: + share_path = device + fs_type = definition.get("fs_type") + mount_point = definition.get("mount_point") + mount_opts = definition.get("mount_opts") + if not mount_opts: + mount_opts = "defaults" + fs_freq = definition.get("fs_freq") + if not fs_freq: + fs_freq = "0" + fs_passno = definition.get("fs_passno") + if not fs_passno: + fs_passno = "0" + if mount_point and fs_type: + _add_fstab_entry(log, + share_path, + mount_point, + fs_type, + mount_opts, + fs_freq, + fs_passno) + _mount_fs(log, mount_point) + else: + util.logexc(log, + "handle_nfs: " + "expexted \"mount_point\" and \"fs_type\" parameters") + + +def handle_ebs(cfg, cloud, log, definition): + # Handle block device either explicitly provided via device path or + # via device name mapping (Amazon/OpenStack) + device = definition.get("device") + blockdev = _cloud_device_2_os_device(cloud, log, device) + if blockdev: + lvm_group = definition.get("lvm_group") + lvm_volume = definition.get("lvm_volume") + fs_type = definition.get("fs_type") + fs_opts = definition.get("fs_opts") + mount_point = definition.get("mount_point") + mount_opts = definition.get("mount_opts") + if not mount_opts: + mount_opts = "defaults" + fs_freq = definition.get("fs_freq") + if not fs_freq: + fs_freq = "1" + fs_passno = definition.get("fs_passno") + if not fs_passno: + fs_passno = "2" + if lvm_group and lvm_volume: + for vg_name in _list_vg_names(): + if vg_name == lvm_group: + util.logexc(log, + "handle_ebs: " + "logical volume group '%s' exists already" + % lvm_group) + return + for lv_name in _list_lv_names(): + if lv_name == lvm_volume: + util.logexc(log, + "handle_ebs: " + "logical volume '%s' exists already" + % lvm_volume) + return + blockdev = _create_lv(log, blockdev, lvm_group, lvm_volume) + if blockdev: + if mount_point and fs_type: + _create_fs(log, blockdev, fs_type, fs_opts) + _add_fstab_entry(log, + blockdev, + mount_point, + fs_type, + mount_opts, + fs_freq, + fs_passno) + _mount_fs(log, mount_point) + else: + util.logexc(log, + "handle_ebs: " + "expexted \"mount_point\" and " + "\"fs_type\" parameters") + + +def _cloud_device_2_os_device(cloud, log, name): + # Translate cloud device (ebs# and ephemaral#) to OS block device path + blockdev = None + for i in range(WAIT_4_BLOCKDEV_MAPPING_ITER): + if (cloud.datasource.metadata and + "block-device-mapping" in cloud.datasource.metadata): + metadata = cloud.datasource.metadata + else: + if (cloud.datasource.ec2_metadata and + "block-device-mapping" in cloud.datasource.ec2_metadata): + metadata = cloud.datasource.ec2_metadata + else: + util.logexc(log, + "_cloud_device_2_os_device: " + "metadata item block-device-mapping not found") + return None + blockdev_items = metadata["block-device-mapping"].iteritems() + for (map_name, device) in blockdev_items: + if map_name == name: + blockdev = device + break + if blockdev is None: + cloud.datasource.get_data() + time.sleep(WAIT_4_BLOCKDEV_MAPPING_SLEEP) + continue + if blockdev is None: + util.logexc(log, + "_cloud_device_2_os_device: " + "unable to convert %s to a device" % name) + return None + if not blockdev.startswith("/"): + blockdev_path = "/dev/%s" % blockdev + else: + blockdev_path = blockdev + for i in range(WAIT_4_BLOCKDEV_DEVICE_ITER): + if os.path.exists(blockdev_path): + return blockdev_path + time.sleep(WAIT_4_BLOCKDEV_DEVICE_SLEEP) + util.logexc(log, + "_cloud_device_2_os_device: " + "device %s does not exist" % blockdev_path) + return None + + +def _list_vg_names(): + # List all LVM volume groups + p = subprocess.Popen([LVM_CMD, "vgs", "-o", "vg_name"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + err = p.wait() + if err: + return [] + output = p.communicate()[0] + output = output.split("\n") + if not output: + return [] + header = output[0].strip() + if header != "VG": + return [] + names = [] + for name in output[1:]: + if not name: + break + names.append(name.strip()) + return names + + +def _list_lv_names(): + # List all LVM logical volumes + p = subprocess.Popen([LVM_CMD, "lvs", "-o", "lv_name"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + err = p.wait() + if err: + return [] + output = p.communicate()[0] + output = output.split("\n") + if not output: + return [] + header = output[0].strip() + if header != "LV": + return [] + names = [] + for name in output[1:]: + if not name: + break + names.append(name.strip()) + return names + + +def _create_lv(log, device, vg_name, lv_name): + # Create volume group + pvcreate_cmd = [LVM_CMD, "pvcreate", device] + vgcreate_cmd = [LVM_CMD, "vgcreate", vg_name, device] + lvcreate_cmd = [LVM_CMD, + "lvcreate", "-l", "100%FREE", "--name", lv_name, vg_name] + try: + util.subp(pvcreate_cmd) + util.subp(vgcreate_cmd) + util.subp(lvcreate_cmd) + return "/dev/mapper/%s-%s" % (vg_name, lv_name) + except Exception as e: + util.logexc(log, + "_create_lv: " + "failed to create LVM volume '%s' for device '%s': %s" + % (lv_name, device, e)) + return None + + +def _create_fs(log, device, fs_type, fs_opts=None): + # Create filesystem + mkfs_cmd = util.which("mkfs.%s" % fs_type) + if not mkfs_cmd: + mkfs_cmd = util.which("mk%s" % fs_type) + if not mkfs_cmd: + util.logexc(log, + "_create_fs: " + "cannot create filesystem type '%s': " + "failed to find mkfs.%s command" % (fs_type, fs_type)) + return + try: + if fs_opts: + util.subp([mkfs_cmd, fs_opts, device]) + else: + util.subp([mkfs_cmd, device]) + except Exception as e: + util.logexc(log, + "_create_fs: " + "failed to create filesystem type '%s': %s" % (fs_type, e)) + + +def _add_fstab_entry(log, + device, + mount_point, + fs_type, + mount_opts, + fs_freq, + fs_passno): + # Create fstab entry + fstab_lines = [] + for line in util.load_file(FSTAB_PATH).splitlines(): + try: + toks = re.compile("[%s]+" % (whitespace)).split(line) + except: + pass + if len(toks) > 0 and toks[0] == device: + util.logexc(log, + "_add_fstab_entry: " + "file %s has device %s already" % (FSTAB_PATH, device)) + return + if len(toks) > 1 and toks[1] == mount_point: + util.logexc(log, + "_add_fstab_entry: " + "file %s has mount point %s already" + % (FSTAB_PATH, mount_point)) + return + fstab_lines.append(line) + fstab_lines.extend(["%s\t%s\t%s\t%s\t%s\t%s" % + (device, + mount_point, + fs_type, + mount_opts, + fs_freq, + fs_passno)]) + contents = "%s\n" % ('\n'.join(fstab_lines)) + util.write_file(FSTAB_PATH, contents) + + +def _mount_fs(log, mount_point): + # Mount filesystem according to fstab entry + try: + util.ensure_dir(mount_point) + except Exception as e: + util.logexc(log, + "_mount_fs: " + "failed to make '%s' mount point directory: %s" + % (mount_point, e)) + return + try: + util.subp(["mount", mount_point]) + except Exception as e: + util.logexc(log, + "_mount_fs: " + "activating mounts via 'mount %s' failed: %s" + % (mount_point, e)) + + +def _service_wrapper(cloud, log, service, command): + # Wrapper for service related commands + if cloud.distro.osfamily == "redhat": + if SYSTEMCTL_CMD: + svc_cmd = [SYSTEMCTL_CMD, command, service] + else: + if command == "enable" or command == "disable": + if CHKCONFIG_CMD: + if command == "enable": + svc_cmd = [CHKCONFIG_CMD, service, "on"] + else: + svc_cmd = [CHKCONFIG_CMD, service, "off"] + else: + util.logexc(log, + "_handle_service: " + "service config command \"chkconfig\" " + "not found") + return + else: + svc_cmd = [SERVICE_CMD, service, command] + elif cloud.distro.osfamily == "debian": + if SYSTEMCTL_CMD: + svc_cmd = [SYSTEMCTL_CMD, command, service] + else: + if command == 'enable' or command == "disable": + if os.path.exists('/usr/sbin/update-rc.d'): + svc_cmd = ['/usr/sbin/update-rc.d', service, "defaults"] + else: + util.logexc(log, + "_handle_service: " + "command \"/usr/sbin/update-rc.d\" not found") + return + else: + svc_cmd = [SERVICE_CMD, service, command] + else: + util.logexc(log, + "_handle_service: " + "unsupported osfamily \"%s\"" % cloud.distro.osfamily) + return + try: + util.subp(svc_cmd, capture=False) + except Exception as e: + util.logexc(log, + "_handle_service: " + "failure to \"%s\" \"%s\": %s" % (command, service, e)) + + +def _iscsi_lun_discover(log, iscsi_host, iscsi_port, iscsi_lun, iscsi_target): + # Discover iSCSI target and map LUN ID to multipath device path + blockdev = None + for i in range(WAIT_4_BLOCKDEV_MAPPING_ITER): + try: + util.subp([ISCSIADM_CMD, + "--mode", + "discoverydb", + "--type", + "sendtargets", + "--portal", + "%s:%s" % (iscsi_host, iscsi_port), + "--discover", + "--login", + "all"], + capture=False) + except Exception as e: + util.logexc(log, + "_iscsi_lun_discover: " + "failure in attempt to discover iSCSI LUN for target " + "\"%s\": %s" % (iscsi_target, e)) + return None + p = subprocess.Popen([ISCSIADM_CMD, "-m", "node"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + err = p.wait() + if err: + util.logexc(log, + "_iscsi_lun_discover: " + "failure from \"%s -m node\" command" % ISCSIADM_CMD) + return None + output = p.communicate()[0] + output = output.split("\n") + if not output: + util.logexc(log, + "_iscsi_lun_discover: " + "no iSCSI nodes discovered for target \"%s\"" + % iscsi_target) + time.sleep(WAIT_4_BLOCKDEV_MAPPING_SLEEP) + continue + for node in output: + iscsi_portal = node.split(",", 1)[0] + if iscsi_portal: + try: + util.subp([ISCSIADM_CMD, + "-m", + "node", + "-T", + iscsi_target, + "-p", + iscsi_portal, + "--op", + "update", + "-n", + "node.startup", + "-v", + "automatic"], + capture=False) + except Exception as e: + util.logexc(log, + "_iscsi_lun_discover: " + "failure in attempt to set automatic binding " + "for target portal \"%s\": %s" + % (iscsi_portal, e)) + return None + p = subprocess.Popen([ISCSIADM_CMD, "-m", "session", "-P3"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + err = p.wait() + if err: + util.logexc(log, + "_iscsi_lun_discover: " + "failure from \"%s -m session -P3\" command" + % ISCSIADM_CMD) + return None + output = p.communicate()[0] + output = output.split("\n") + if not output: + util.logexc(log, + "_iscsi_lun_discover: " + "no iSCSI sessions discovered for target \"%s\"" + % iscsi_target) + else: + current_iscsi_target = None + current_iscsi_sid = None + current_iscsi_lun = None + for line in output: + m = re.search("^Target: ([a-z0-9\.:-]*)", line) + if m: + current_iscsi_target = m.group(1) + continue + else: + if (current_iscsi_target and + current_iscsi_target == iscsi_target): + m = re.search("SID: ([0-9]*)", line) + if m: + if current_iscsi_sid and not current_iscsi_lun: + try: + util.subp([ISCSIADM_CMD, + "-m", + "session", + "-r", + current_iscsi_sid, + "-u"], + capture=False) + except: + pass + current_iscsi_sid = m.group(1) + current_iscsi_lun = None + continue + m = re.search("scsi[0-9]* Channel [0-9]* " + "Id [0-9]* Lun: ([0-9]*)", line) + if m: + current_iscsi_lun = m.group(1) + continue + if (current_iscsi_lun and + current_iscsi_lun == iscsi_lun): + m = re.search("Attached scsi disk (sd[a-z]*)", + line) + if m: + attached_scsi_disk = m.group(1) + p = subprocess.Popen(["/lib/udev/scsi_id", + "-g", "-u", "-d", + "/dev/%s" + % attached_scsi_disk], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + err = p.wait() + if err: + util.logexc(log, + "_iscsi_lun_discover: " + "failure from " + "\"/lib/udev/scsi_id\" " + "command") + return None + output2 = p.communicate()[0] + output2 = output2.split('\n') + if not output2: + util.logexc(log, + "_iscsi_lun_discover: " + "no wwid returned for device " + "\"/dev/%s\"" + % attached_scsi_disk) + else: + blockdev = "/dev/mapper/%s" % output2[0] + if current_iscsi_sid and not current_iscsi_lun: + try: + util.subp([ISCSIADM_CMD, + "-m", + "session", + "-r", + current_iscsi_sid, + "-u"], + capture=False) + except: + pass + if blockdev: + break + else: + time.sleep(WAIT_4_BLOCKDEV_MAPPING_SLEEP) + if blockdev: + for i in range(WAIT_4_BLOCKDEV_DEVICE_ITER): + if os.path.exists(blockdev): + return blockdev + try: + util.subp([MULTIPATH_CMD], capture=False) + except Exception as e: + util.logexc(log, + "_iscsi_lun_discover: " + "failure to run \"%s\": %s" % (MULTIPATH_CMD, e)) + return None + time.sleep(WAIT_4_BLOCKDEV_DEVICE_SLEEP) + else: + return None === modified file 'config/cloud.cfg' --- config/cloud.cfg 2016-03-09 22:34:11 +0000 +++ config/cloud.cfg 2016-05-06 21:33:46 +0000 @@ -44,6 +44,7 @@ # this can be used by upstart jobs for 'start on cloud-config'. - emit_upstart - disk_setup + - remotedisk_setup - mounts - ssh-import-id - locale === added file 'doc/examples/cloud-config-remotedisk-setup.txt' --- doc/examples/cloud-config-remotedisk-setup.txt 1970-01-01 00:00:00 +0000 +++ doc/examples/cloud-config-remotedisk-setup.txt 2016-05-06 21:33:46 +0000 @@ -0,0 +1,95 @@ +#cloud-config +# +# The module remotedisk_setup provides a simple and uniform way +# to handle remote disks such as: +# - iSCSI LUN's: +# - configures Open iSCSI initiator; +# - configures device multipath; +# - enables necessary services; +# - attaches iSCSI LUN; +# - discovers multipath device; +# - creates logical volume; +# - creates filesystem; +# - mounts filesystem; +# - configures /etc/fstab +# - Hypervisor disks (OpenStack Cinder volumes, AWS EBS, etc): +# - creates logical volume; +# - creates filesystem; +# - mounts filesystem; +# - configures /etc/fstab +# - NFS shares: +# - mounts NFS share; +# - configures /etc/fstab +# +remotedisk_setup: + +############################################ +# Example configuration to handle iSCSI LUN: +############################################ + - device: 'iscsi:192.168.1.1:6:3260:1:iqn.1992-08.com.netapp:sn.62546b567fbf11e4811590e2ba6cc3b4:vs.10' + lvm_group: 'vg_data1' + lvm_volume: 'lv_data1' + fs_type: 'ext4' + mount_point: '/apps/data1' + +############################################ +# Parameters: +# mandatory: +# device: 'iscsi:<iSCSI target host/LIF>:<transport protocol>:<port>:<LUN ID>:<iSCSI target name>' +# fs_type: '<filesystem type>' +# mount_point: '<mount point dir path>' +# optional: +# initiator_name: '<iSCSI initiator name, default is iqn.2005-02.com.open-iscsi:<hostname>>' +# mount_opts: '<filesystem mount options, default is "defaults,_netdev">' +# lvm_group: '<LVM logical group name>' +# lvm_volume: '<LVM logical volume name>' +# fs_opts: '<filesystem create options specific to mkfs.fs_type>' +# fs_freq: '<fstab fs freq, default is "1">' +# fs_passno: '<fstab fs passno, default is "2">' +# notes: +# missing lvm_group and lvm_volume will cause filesystem creation on top of multipath device +# + +########################################################## +# Example configuration to handle OpenStack Cinder volume: +########################################################## + - device: 'ebs0' + lvm_group: 'vg_data1' + lvm_volume: 'lv_data1' + fs_type: 'ext4' + mount_point: '/apps/data1' + +########################################################## +# Parameters: +# mandatory: +# device: 'ebs<0-9> or block device path /dev/vd<b-z>' +# fs_type: '<filesystem type>' +# mount_point: '<mount point dir path>' +# optional: +# mount_opts: '<filesystem mount options, default is "defaults">' +# lvm_group: '<LVM logical group name>' +# lvm_volume: '<LVM logical volume name>' +# fs_opts: '<filesystem create options specific to mkfs.fs_type>' +# fs_freq: '<fstab fs freq, default is "1">' +# fs_passno: '<fstab fs passno, default is "2">' +# notes: +# missing lvm_group and lvm_volume will cause filesystem creation on top of block device +# + +############################################# +# Example configuration to handle NFS shares: +############################################# + - device: 'nfs:192.168.1.1:/myshare' + mount_point: '/apps/data1' + mount_opts: 'tcp,rw,rsize=65536,wsize=65536' + +############################################# +# Parameters: +# mandatory: +# device: 'nfs:<NFS host>:<NFS share path>' +# mount_point: '<mount point dir path>' +# optional: +# mount_opts: '<NFS share mount options, default is "defaults">' +# fs_type: 'nfs' +# fs_freq: '<fstab fs freq, default is "0">' +# fs_passno: '<fstab fs passno, default is "0">' === added file 'templates/multipath.conf.tmpl' --- templates/multipath.conf.tmpl 1970-01-01 00:00:00 +0000 +++ templates/multipath.conf.tmpl 2016-05-06 21:33:46 +0000 @@ -0,0 +1,29 @@ +defaults { + find_multipaths yes + user_friendly_names no + no_path_retry queue + queue_without_daemon no + flush_on_last_del yes + max_fds max +} +blacklist { + devnode "^hd[a-z]" + devnode "^vd[a-z]" + devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" + devnode "^cciss.*" +} +devices { + device { + vendor "NETAPP" + product "LUN" + path_grouping_policy group_by_prio + features "3 queue_if_no_path pg_init_retries 50" + prio "alua" + path_checker tur + failback immediate + path_selector "round-robin 0" + hardware_handler "1 alua" + rr_weight uniform + rr_min_io 128 + } +}
_______________________________________________ Mailing list: https://launchpad.net/~cloud-init-dev Post to : [email protected] Unsubscribe : https://launchpad.net/~cloud-init-dev More help : https://help.launchpad.net/ListHelp

