예제 #1
0
def is_online(device):
    """  check if device is online """
    sys_path = sys_block_path(device)
    device_size = util.load_file(
        os.path.join(sys_path, 'size'))
    # a block device should have non-zero size to be usable
    return int(device_size) > 0
예제 #2
0
파일: mdadm.py 프로젝트: lucasmoura/curtin
def md_present(mdname):
    """Check if mdname is present in /proc/mdstat"""
    if not mdname:
        raise ValueError('md_present requires a valid md name')

    try:
        mdstat = util.load_file('/proc/mdstat')
    except IOError as e:
        if util.is_file_not_found_exc(e):
            LOG.warning('Failed to read /proc/mdstat; '
                        'md modules might not be loaded')
            return False
        else:
            raise e

    md_kname = dev_short(mdname)
    # Find lines like:
    # md10 : active raid1 vdc1[1] vda2[0]
    present = [
        line for line in mdstat.splitlines()
        if line.split(":")[0].rstrip() == md_kname
    ]
    if len(present) > 0:
        return True
    return False
예제 #3
0
def shutdown_lvm(device):
    """
    Shutdown specified lvm device.
    """
    device = block.sys_block_path(device)
    # lvm devices have a dm directory that containes a file 'name' containing
    # '{volume group}-{logical volume}'. The volume can be freed using lvremove
    name_file = os.path.join(device, 'dm', 'name')
    lvm_name = util.load_file(name_file).strip()
    (vg_name, lv_name) = lvm.split_lvm_name(lvm_name)
    vg_lv_name = "%s/%s" % (vg_name, lv_name)
    devname = "/dev/" + vg_lv_name

    # wipe contents of the logical volume first
    LOG.info('Wiping lvm logical volume: %s', devname)
    block.quick_zero(devname, partitions=False)

    # remove the logical volume
    LOG.debug('using "lvremove" on %s', vg_lv_name)
    util.subp(['lvremove', '--force', '--force', vg_lv_name])

    # if that was the last lvol in the volgroup, get rid of volgroup
    if len(lvm.get_lvols_in_volgroup(vg_name)) == 0:
        pvols = lvm.get_pvols_in_volgroup(vg_name)
        util.subp(['vgremove', '--force', '--force', vg_name], rcs=[0, 5])

        # wipe the underlying physical volumes
        for pv in pvols:
            LOG.info('Wiping lvm physical volume: %s', pv)
            block.quick_zero(pv, partitions=False)

    # refresh lvmetad
    lvm.lvm_scan()
예제 #4
0
 def test_load_file_simple(self):
     fname = 'test.cfg'
     contents = "#curtin-config"
     with simple_mocked_open(content=contents) as m_open:
         loaded_contents = util.load_file(fname, decode=False)
         self.assertEqual(contents, loaded_contents)
         m_open.assert_called_with(fname, 'rb')
예제 #5
0
    def test_udevadm_info_escape_quotes(self, m_subp):
        """verify we escape quotes when we fail to split. """
        mypath = '/dev/sdz'
        datafile = 'tests/data/udevadm_info_sandisk_cruzer.txt'
        m_subp.return_value = (util.load_file(datafile), "")
        info = udevadm_info(mypath)
        m_subp.assert_called_with(
            ['udevadm', 'info', '--query=property', '--export', mypath],
            capture=True)
        """
        Replicate what udevadm_info parsing does and use pdb to examine what's
        happening.

        (Pdb) original_value
        "SanDisk'"
        (Pdb) quoted_value
        '\'SanDisk\'"\'"\'\''
        (Pdb) split_value
        ["SanDisk'"]
        (Pdb) expected_value
        "SanDisk'"
        """
        original_value = "SanDisk'"
        quoted_value = shlex_quote(original_value)
        split_value = shlex.split(quoted_value)
        expected_value = split_value if ' ' in split_value else split_value[0]

        self.assertEqual(expected_value, info['SCSI_VENDOR'])
        self.assertEqual(expected_value, info['SCSI_VENDOR_ENC'])
예제 #6
0
 def test_load_file_respects_decode_false(self, mock_decode):
     fname = 'test.cfg'
     contents = b'start \xc3\xa9 end'
     with simple_mocked_open(contents):
         loaded_contents = util.load_file(fname, decode=False)
         self.assertEqual(type(loaded_contents), bytes)
         self.assertEqual(loaded_contents, contents)
예제 #7
0
    def run_install(self, cfg):
        # runs an install with a provided config
        # return stdout, stderr, exit_code, log_file_contents

        # src_url is required by arg parser, but not used here.
        src_url = 'file://' + self.tmpd + '/NOT_USED'
        mcfg = cfg.copy()
        log_file = cfg_file = None
        rc = None
        try:
            log_file = tempfile.mktemp(dir=self.tmpd)
            cfg_file = tempfile.mktemp(dir=self.tmpd)
            mcfg['install'] = cfg.get('install', {})
            mcfg['install']['log_file'] = log_file
            mcfg['sources'] = {'testsrc': src_url}
            util.write_file(cfg_file, json.dumps(mcfg))
            print(json.dumps(mcfg))
            try:
                out, err = self.run_main(['install', '--config=' + cfg_file])
                rc = 0
            except util.ProcessExecutionError as e:
                out = e.stdout
                err = e.stderr
                rc = e.exit_code
            log_contents = util.load_file(log_file)
        finally:
            for f in [f for f in (log_file, cfg_file) if f]:
                os.unlink(f)

        return out, err, rc, log_contents
예제 #8
0
def _maybe_remove_legacy_eth0(target,
                              path="etc/network/interfaces.d/eth0.cfg"):
    """Ubuntu cloud images previously included a 'eth0.cfg' that had
       hard coded content.  That file would interfere with the rendered
       configuration if it was present.

       if the file does not exist do nothing.
       If the file exists:
         - with known content, remove it and warn
         - with unknown content, leave it and warn
    """

    cfg = util.target_path(target, path=path)
    if not os.path.exists(cfg):
        LOG.warn('Failed to find legacy network conf file %s', cfg)
        return

    bmsg = "Dynamic networking config may not apply."
    try:
        contents = util.load_file(cfg)
        known_contents = ["auto eth0", "iface eth0 inet dhcp"]
        lines = [
            f.strip() for f in contents.splitlines() if not f.startswith("#")
        ]
        if lines == known_contents:
            util.del_file(cfg)
            msg = "removed %s with known contents" % cfg
        else:
            msg = (bmsg + " '%s' exists with user configured content." % cfg)
    except Exception:
        msg = bmsg + " %s exists, but could not be read." % cfg
        LOG.exception(msg)
        raise

    LOG.warn(msg)
예제 #9
0
 def test_default_is_zero(self):
     flen = 1024
     myfile = self.tmp_path("def_zero")
     util.write_file(myfile, flen * b'\1', omode="wb")
     block.wipe_file(myfile)
     found = util.load_file(myfile, decode=False)
     self.assertEqual(found, flen * b'\0')
예제 #10
0
def generate_sources_list(cfg, release, mirrors, target=None):
    """ generate_sources_list
        create a source.list file based on a custom or default template
        by replacing mirrors and release in the template
    """
    default_mirrors = get_default_mirrors(distro.get_architecture(target))
    aptsrc = "/etc/apt/sources.list"
    params = {'RELEASE': release}
    for k in mirrors:
        params[k] = mirrors[k]

    tmpl = cfg.get('sources_list', None)
    if tmpl is None:
        LOG.info(
            "No custom template provided, fall back to modify"
            "mirrors in %s on the target system", aptsrc)
        tmpl = util.load_file(paths.target_path(target, aptsrc))
        # Strategy if no custom template was provided:
        # - Only replacing mirrors
        # - no reason to replace "release" as it is from target anyway
        # - The less we depend upon, the more stable this is against changes
        # - warn if expected original content wasn't found
        tmpl = mirror_to_placeholder(tmpl, default_mirrors['PRIMARY'],
                                     "$MIRROR")
        tmpl = mirror_to_placeholder(tmpl, default_mirrors['SECURITY'],
                                     "$SECURITY")

    orig = paths.target_path(target, aptsrc)
    if os.path.exists(orig):
        os.rename(orig, orig + ".curtin.old")

    rendered = util.render_string(tmpl, params)
    disabled = disable_suites(cfg.get('disable_suites'), rendered, release)
    util.write_file(paths.target_path(target, aptsrc), disabled, mode=0o644)
예제 #11
0
def _md_get_members_list(devpath, state_check):
    md_dev, _partno = get_blockdev_for_partition(devpath)
    sysfs_md = sys_block_path(md_dev, "md")
    return [
        dev_path(dev[4:]) for dev in os.listdir(sysfs_md)
        if (dev.startswith('dev-') and state_check(
            util.load_file(os.path.join(sysfs_md, dev, 'state')).strip()))
    ]
예제 #12
0
def md_get_devices_list(devpath):
    sysfs_md = sys_block_path(devpath, "md")
    devices = [
        dev_path(dev[4:]) for dev in os.listdir(sysfs_md)
        if (dev.startswith('dev-') and util.load_file(
            os.path.join(sysfs_md, dev, 'state')).strip() != 'spare')
    ]
    return devices
예제 #13
0
def get_blockdev_for_partition(devpath, strict=True):
    """
    find the parent device for a partition.
    returns a tuple of the parent block device and the partition number
    if device is not a partition, None will be returned for partition number
    """
    # normalize path
    rpath = os.path.realpath(devpath)

    # convert an entry in /dev/ to parent disk and partition number
    # if devpath is a block device and not a partition, return (devpath, None)
    base = '/sys/class/block'

    # input of /dev/vdb, /dev/disk/by-label/foo, /sys/block/foo,
    # /sys/block/class/foo, or just foo
    syspath = os.path.join(base, path_to_kname(devpath))

    # don't need to try out multiple sysfs paths as path_to_kname handles cciss
    if strict and not os.path.exists(syspath):
        raise OSError("%s had no syspath (%s)" % (devpath, syspath))

    if rpath.startswith('/dev/dm-'):
        parent_info = multipath.mpath_partition_to_mpath_id_and_partnumber(
            rpath)
        if parent_info is not None:
            mpath_id, ptnum = parent_info
            return os.path.realpath('/dev/mapper/' + mpath_id), ptnum

    ptpath = os.path.join(syspath, "partition")
    if not os.path.exists(ptpath):
        return (rpath, None)

    ptnum = util.load_file(ptpath).rstrip()

    # for a partition, real syspath is something like:
    # /sys/devices/pci0000:00/0000:00:04.0/virtio1/block/vda/vda1
    rsyspath = os.path.realpath(syspath)
    disksyspath = os.path.dirname(rsyspath)

    diskmajmin = util.load_file(os.path.join(disksyspath, "dev")).rstrip()
    diskdevpath = os.path.realpath("/dev/block/%s" % diskmajmin)

    # diskdevpath has something like 253:0
    # and udev has put links in /dev/block/253:0 to the device name in /dev/
    return (diskdevpath, ptnum)
예제 #14
0
    def test_skip_rename_resolvconf_gone(self, m_rename):
        self.m_shutil.copy.side_effect = self.mycopy
        self.m_shutil.rmtree.side_effect = self.mydel
        with util.ChrootableTarget(self.target):
            tp = paths.target_path(self.target, path='/etc/resolv.conf')
            target_conf = util.load_file(tp)
            self.assertEqual(self.host_content, target_conf)

        self.assertEqual(0, m_rename.call_count)
예제 #15
0
파일: mdadm.py 프로젝트: tjjh89017/curtin
def mdadm_stop(devpath, retries=None):
    assert_valid_devpath(devpath)
    if not retries:
        retries = [0.2] * 60

    sync_action = md_sysfs_attr_path(devpath, 'sync_action')
    sync_max = md_sysfs_attr_path(devpath, 'sync_max')
    sync_min = md_sysfs_attr_path(devpath, 'sync_min')

    LOG.info("mdadm stopping: %s" % devpath)
    for (attempt, wait) in enumerate(retries):
        try:
            LOG.debug('mdadm: stop on %s attempt %s', devpath, attempt)
            # An array in 'resync' state may not be stoppable, attempt to
            # cancel an ongoing resync
            val = md_sysfs_attr(devpath, 'sync_action')
            LOG.debug('%s/sync_max = %s', sync_action, val)
            if val != "idle":
                LOG.debug("mdadm: setting array sync_action=idle")
                try:
                    util.write_file(sync_action, content="idle")
                except (IOError, OSError) as e:
                    LOG.debug("mdadm: (non-fatal) write to %s failed %s",
                              sync_action, e)

            # Setting the sync_{max,min} may can help prevent the array from
            # changing back to 'resync' which may prevent the array from being
            # stopped
            val = md_sysfs_attr(devpath, 'sync_max')
            LOG.debug('%s/sync_max = %s', sync_max, val)
            if val != "0":
                LOG.debug("mdadm: setting array sync_{min,max}=0")
                try:
                    for sync_file in [sync_max, sync_min]:
                        util.write_file(sync_file, content="0")
                except (IOError, OSError) as e:
                    LOG.debug('mdadm: (non-fatal) write to %s failed %s',
                              sync_file, e)

            # one wonders why this command doesn't do any of the above itself?
            out, err = util.subp(["mdadm", "--manage", "--stop", devpath],
                                 capture=True)
            LOG.debug("mdadm stop command output:\n%s\n%s", out, err)
            LOG.info("mdadm: successfully stopped %s after %s attempt(s)",
                     devpath, attempt+1)
            return

        except util.ProcessExecutionError:
            LOG.warning("mdadm stop failed, retrying ")
            if os.path.isfile('/proc/mdstat'):
                LOG.critical("/proc/mdstat:\n%s",
                             util.load_file('/proc/mdstat'))
            LOG.debug("mdadm: stop failed, retrying in %s seconds", wait)
            time.sleep(wait)
            pass

    raise OSError('Failed to stop mdadm device %s', devpath)
예제 #16
0
    def test_chrootable_target_renames_and_copies_resolvconf_if_symlink(self):
        target_rconf = os.path.join(self.target, 'etc/resolv.conf')
        os.symlink('../run/foobar/wark.conf', target_rconf)

        self.m_shutil.copy.side_effect = self.mycopy
        self.m_shutil.rmtree.side_effect = self.mydel
        with util.ChrootableTarget(self.target):
            target_conf = util.load_file(
                paths.target_path(self.target, path='/etc/resolv.conf'))
            self.assertEqual(self.host_content, target_conf)
예제 #17
0
def shutdown_swap(path):
    """release swap device from kernel swap pool if present"""
    procswaps = util.load_file('/proc/swaps')
    for swapline in procswaps.splitlines():
        if swapline.startswith(path):
            msg = ('Removing %s from active use as swap device, '
                   'needed for storage config' % path)
            LOG.warning(msg)
            util.subp(['swapoff', path])
            return
예제 #18
0
 def test_reader_fhandle(self):
     srcfile = self.tmp_path("fhandle_src")
     trgfile = self.tmp_path("fhandle_trg")
     data = '\n'.join(["this is source file." for f in range(0, 10)] + [])
     util.write_file(srcfile, data)
     util.write_file(trgfile, 'a' * len(data))
     with open(srcfile, "rb") as fp:
         block.wipe_file(trgfile, reader=fp.read)
     found = util.load_file(trgfile)
     self.assertEqual(data, found)
예제 #19
0
def sysfs_partition_data(blockdev=None, sysfs_path=None):
    # given block device or sysfs_path, return a list of tuples
    # of (kernel_name, number, offset, size)
    if blockdev:
        blockdev = os.path.normpath(blockdev)
        sysfs_path = sys_block_path(blockdev)
    elif sysfs_path:
        # use normpath to ensure that paths with trailing slash work
        sysfs_path = os.path.normpath(sysfs_path)
        blockdev = os.path.join('/dev', os.path.basename(sysfs_path))
    else:
        raise ValueError("Blockdev and sysfs_path cannot both be None")

    # queue property is only on parent devices, ie, we can't read
    # /sys/class/block/vda/vda1/queue/* as queue is only on the
    # parent device
    sysfs_prefix = sysfs_path
    (parent, partnum) = get_blockdev_for_partition(blockdev)
    if partnum:
        sysfs_prefix = sys_block_path(parent)
        partnum = int(partnum)

    block_size = int(
        util.load_file(os.path.join(sysfs_prefix, 'queue/logical_block_size')))
    unit = block_size

    ptdata = []
    for part_sysfs in get_sysfs_partitions(sysfs_prefix):
        data = {}
        for sfile in ('partition', 'start', 'size'):
            dfile = os.path.join(part_sysfs, sfile)
            if not os.path.isfile(dfile):
                continue
            data[sfile] = int(util.load_file(dfile))
        if partnum is None or data['partition'] == partnum:
            ptdata.append((
                path_to_kname(part_sysfs),
                data['partition'],
                data['start'] * unit,
                data['size'] * unit,
            ))

    return ptdata
예제 #20
0
def sysfs_to_dict(input):
    """ Simple converter for loading key: value lines from a recursive
        grep -r . <sysfs path> > sysfs_data
    """
    data = util.load_file(input)

    return {
        y[0]: ":".join(y[1:])
        for y in [x.split(":") for x in data.split("\n") if len(x) > 0]
    }
예제 #21
0
 def test_mount_apply_skips_mounting_swap(self, m_mount_fstab_data):
     """mount_apply does not mount swap fs, but should write fstab."""
     fdata = block_meta.FstabData(spec="/dev/xxxx1",
                                  path="none",
                                  fstype='swap')
     fstab = self.tmp_path("fstab")
     block_meta.mount_apply(fdata, fstab=fstab)
     contents = util.load_file(fstab)
     self.assertEqual(0, m_mount_fstab_data.call_count)
     self.assertIn("/dev/xxxx1", contents)
     self.assertIn("swap", contents)
예제 #22
0
    def test_apt_srcl_custom(self):
        """test_apt_srcl_custom - Test rendering a custom source template"""
        cfg = yaml.safe_load(YAML_TEXT_CUSTOM_SL)
        target = self.new_root

        arch = util.get_architecture()
        # would fail inside the unittest context
        with mock.patch.object(util, 'get_architecture', return_value=arch):
            with mock.patch.object(util,
                                   'lsb_release',
                                   return_value={'codename': 'fakerel'}):
                apt_config.handle_apt(cfg, target)

        self.assertEqual(
            EXPECTED_CONVERTED_CONTENT,
            util.load_file(util.target_path(target, "/etc/apt/sources.list")))
        cloudfile = util.target_path(
            target, '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg')
        self.assertEqual({'apt_preserve_sources_list': True},
                         yaml.load(util.load_file(cloudfile)))
예제 #23
0
파일: mdadm.py 프로젝트: tjjh89017/curtin
def md_sysfs_attr(md_devname, attrname):
    """ Return the attribute str of an md device found under the 'md' dir """
    attrdata = ''
    if not valid_mdname(md_devname):
        raise ValueError('Invalid md devicename: [{}]'.format(md_devname))

    sysfs_attr_path = md_sysfs_attr_path(md_devname, attrname)
    if os.path.isfile(sysfs_attr_path):
        attrdata = util.load_file(sysfs_attr_path).strip()

    return attrdata
예제 #24
0
    def test_chrootable_target_renames_and_copies_resolvconf(self):
        content = "target_resolvconf"
        util.write_file(os.path.join(self.target, 'etc/resolv.conf'), content)

        self.m_shutil.copy.side_effect = self.mycopy
        self.m_shutil.rmtree.side_effect = self.mydel

        with util.ChrootableTarget(self.target):
            target_conf = util.load_file(
                paths.target_path(self.target, path='/etc/resolv.conf'))
            self.assertEqual(self.host_content, target_conf)
예제 #25
0
def load_tfile(filename):
    """ load_tfile
    load file and return content after decoding
    """
    try:
        content = util.load_file(filename, decode=True)
    except Exception as error:
        print('failed to load file content for test: %s' % error)
        raise

    return content
예제 #26
0
 def add_size_to_holders_tree(tree):
     """add size information to generated holders trees"""
     size_file = os.path.join(tree['device'], 'size')
     # size file is always represented in 512 byte sectors even if
     # underlying disk uses a larger logical_block_size
     size = ((512 * int(util.load_file(size_file)))
             if os.path.exists(size_file) else None)
     tree['size'] = util.bytes2human(size) if args.human else str(size)
     for holder in tree['holders']:
         add_size_to_holders_tree(holder)
     return tree
예제 #27
0
    def test_reader_used(self):
        flen = 17

        def reader(size):
            return size * b'\1'

        myfile = self.tmp_path("reader_used")
        # populate with nulls
        util.write_file(myfile, flen * b'\0', omode="wb")
        block.wipe_file(myfile, reader=reader, buflen=flen)
        found = util.load_file(myfile, decode=False)
        self.assertEqual(found, flen * b'\1')
예제 #28
0
def get_supported_filesystems():
    """ Return a list of filesystems that the kernel currently supports
        as read from /proc/filesystems.

        Raises RuntimeError if /proc/filesystems does not exist.
    """
    proc_fs = "/proc/filesystems"
    if not os.path.exists(proc_fs):
        raise RuntimeError("Unable to read 'filesystems' from %s" % proc_fs)

    return [l.split('\t')[1].strip()
            for l in util.load_file(proc_fs).splitlines()]
예제 #29
0
def check_dos_signature(device):
    """
    check if there is a dos partition table signature present on device
    """
    # the last 2 bytes of a dos partition table have the signature with the
    # value 0xAA55. the dos partition table is always 0x200 bytes long, even if
    # the underlying disk uses a larger logical block size, so the start of
    # this signature must be at 0x1fe
    # https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout
    devname = dev_path(path_to_kname(device))
    return (is_block_device(devname) and util.file_size(devname) >= 0x200 and
            (util.load_file(devname, decode=False, read_len=2, offset=0x1fe)
             == b'\x55\xAA'))
예제 #30
0
파일: dasd.py 프로젝트: tjjh89017/curtin
    def blocksize(self):
        """ Read and return device_id's 'blocksize' value.

        :param: device_id: string of device ccw bus_id.
        :returns: string: the device's current blocksize.
        """
        blkattr = 'block/*/queue/hw_sector_size'
        # In practice there will only be one entry in the directory
        # /sys/bus/ccw/devices/{device_id}/block/, but in case
        # something strange happens and there are more, this assumes
        # all block devices connected to the dasd have the same block
        # size...
        path = glob.glob(self.ccw_device_attr_path(blkattr))[0]
        return util.load_file(path)