1"""
2Run-time utilities
3"""
4#
5# Copyright (C) 2014 SUSE LLC
6
7
8import logging
9import os
10import re
11
12import salt.utils.files
13from salt.exceptions import CommandExecutionError
14
15log = logging.getLogger(__name__)
16
17
18def _verify_run(out, cmd=None):
19    """
20    Crash to the log if command execution was not successful.
21    """
22    if out.get("retcode", 0) and out["stderr"]:
23        if cmd:
24            log.debug("Command: '%s'", cmd)
25
26        log.debug("Return code: %s", out.get("retcode"))
27        log.debug("Error output:\n%s", out.get("stderr", "N/A"))
28
29        raise CommandExecutionError(out["stderr"])
30
31
32def _get_mounts(fs_type=None):
33    """
34    List mounted filesystems.
35    """
36    mounts = {}
37    with salt.utils.files.fopen("/proc/mounts") as fhr:
38        for line in fhr.readlines():
39            line = salt.utils.stringutils.to_unicode(line)
40            device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split(
41                " "
42            )
43            if fs_type and fstype != fs_type:
44                continue
45            if mounts.get(device) is None:
46                mounts[device] = []
47
48            data = {"mount_point": mntpnt, "options": options.split(",")}
49            if not fs_type:
50                data["type"] = fstype
51            mounts[device].append(data)
52    return mounts
53
54
55# TODO: Due to "blkid -o export" strongly differs from system to system, this must go away in favor of _blkid() below!
56def _blkid_output(out, fs_type=None):
57    """
58    Parse blkid output.
59    """
60    flt = lambda data: [el for el in data if el.strip()]
61    data = {}
62    for dev_meta in flt(out.split("\n\n")):
63        dev = {}
64        for items in flt(dev_meta.strip().split("\n")):
65            key, val = items.split("=", 1)
66            dev[key.lower()] = val
67        if fs_type and dev.get("type", "") == fs_type or not fs_type:
68            if "type" in dev and fs_type:
69                dev.pop("type")
70            data[dev.pop("devname")] = dev
71
72    if fs_type:
73        mounts = _get_mounts(fs_type)
74        for device in mounts:
75            if data.get(device):
76                data[device]["mounts"] = mounts[device]
77
78    return data
79
80
81def _blkid(fs_type=None):
82    """
83    Return available media devices.
84
85    :param fs_type: Filter only devices that are formatted by that file system.
86    """
87    flt = lambda data: [el for el in data if el.strip()]
88    data = dict()
89    for dev_meta in flt(
90        os.popen("blkid -o full").read().split(os.linesep)
91    ):  # No __salt__ around at this point.
92        dev_meta = dev_meta.strip()
93        if not dev_meta:
94            continue
95        device = dev_meta.split(" ")
96        dev_name = device.pop(0)[:-1]
97        data[dev_name] = dict()
98        for k_set in device:
99            ks_key, ks_value = [elm.replace('"', "") for elm in k_set.split("=")]
100            data[dev_name][ks_key.lower()] = ks_value
101
102    if fs_type:
103        mounts = _get_mounts(fs_type)
104        for device in mounts:
105            if data.get(device):
106                data[device]["mounts"] = mounts[device]
107
108    return data
109
110
111def _is_device(path):
112    """
113    Return True if path is a physical device.
114    """
115    out = __salt__["cmd.run_all"]("file -i {}".format(path))
116    _verify_run(out)
117
118    # Always [device, mime, charset]. See (file --help)
119    return re.split(r"\s+", out["stdout"])[1][:-1] == "inode/blockdevice"
120