PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/xfs.py |
# # The MIT License (MIT) # Copyright (C) 2014 SUSE LLC # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """ Module for managing XFS file systems. """ import logging import os import re import time import salt.utils.data import salt.utils.files import salt.utils.path import salt.utils.platform from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) def __virtual__(): """ Only work on POSIX-like systems """ return not salt.utils.platform.is_windows() and __grains__.get("kernel") == "Linux" def _verify_run(out, cmd=None): """ Crash to the log if command execution was not successful. """ if out.get("retcode", 0) and out["stderr"]: if cmd: log.debug('Command: "%s"', cmd) log.debug("Return code: %s", out.get("retcode")) log.debug("Error output:\n%s", out.get("stderr", "N/A")) raise CommandExecutionError(out["stderr"]) def _xfs_info_get_kv(serialized): """ Parse one line of the XFS info output. """ # No need to know sub-elements here if serialized.startswith("="): serialized = serialized[1:].strip() serialized = serialized.replace(" = ", "=*** ").replace(" =", "=") # Keywords has no spaces, values do opt = [] for tkn in serialized.split(" "): if not opt or "=" in tkn: opt.append(tkn) else: opt[len(opt) - 1] = opt[len(opt) - 1] + " " + tkn # Preserve ordering return [tuple(items.split("=")) for items in opt] def _parse_xfs_info(data): """ Parse output from "xfs_info" or "xfs_growfs -n". """ ret = {} spr = re.compile(r"\s+") entry = None for line in [spr.sub(" ", l).strip().replace(", ", " ") for l in data.split("\n")]: if not line or "=" not in line: continue nfo = _xfs_info_get_kv(line) if not line.startswith("="): entry = nfo.pop(0) ret[entry[0]] = {"section": entry[(entry[1] != "***" and 1 or 0)]} ret[entry[0]].update(dict(nfo)) return ret def info(device): """ Get filesystem geometry information. CLI Example: .. code-block:: bash salt '*' xfs.info /dev/sda1 """ out = __salt__["cmd.run_all"](f"xfs_info {device}") if out.get("stderr"): raise CommandExecutionError(out["stderr"].replace("xfs_info:", "").strip()) return _parse_xfs_info(out["stdout"]) def _xfsdump_output(data): """ Parse CLI output of the xfsdump utility. """ out = {} summary = [] summary_block = False for line in [l.strip() for l in data.split("\n") if l.strip()]: line = re.sub("^xfsdump: ", "", line) if line.startswith("session id:"): out["Session ID"] = line.split(" ")[-1] elif line.startswith("session label:"): out["Session label"] = re.sub("^session label: ", "", line) elif line.startswith("media file size"): out["Media size"] = re.sub(r"^media file size\s+", "", line) elif line.startswith("dump complete:"): out["Dump complete"] = re.sub(r"^dump complete:\s+", "", line) elif line.startswith("Dump Status:"): out["Status"] = re.sub(r"^Dump Status:\s+", "", line) elif line.startswith("Dump Summary:"): summary_block = True continue if line.startswith(" ") and summary_block: summary.append(line.strip()) elif not line.startswith(" ") and summary_block: summary_block = False if summary: out["Summary"] = " ".join(summary) return out def dump(device, destination, level=0, label=None, noerase=None): """ Dump filesystem device to the media (file, tape etc). Required parameters: * **device**: XFS device, content of which to be dumped. * **destination**: Specifies a dump destination. Valid options are: * **label**: Label of the dump. Otherwise automatically generated label is used. * **level**: Specifies a dump level of 0 to 9. * **noerase**: Pre-erase media. Other options are not used in order to let ``xfsdump`` use its default values, as they are most optimal. See the ``xfsdump(8)`` manpage for a more complete description of these options. CLI Example: .. code-block:: bash salt '*' xfs.dump /dev/sda1 /detination/on/the/client salt '*' xfs.dump /dev/sda1 /detination/on/the/client label='Company accountancy' salt '*' xfs.dump /dev/sda1 /detination/on/the/client noerase=True """ if not salt.utils.path.which("xfsdump"): raise CommandExecutionError('Utility "xfsdump" has to be installed or missing.') label = ( label and label or time.strftime( f'XFS dump for "{device}" of %Y.%m.%d, %H:%M', time.localtime() ).replace("'", '"') ) cmd = ["xfsdump"] cmd.append("-F") # Force if not noerase: cmd.append("-E") # pre-erase cmd.append(f"-L '{label}'") # Label cmd.append(f"-l {level}") # Dump level cmd.append(f"-f {destination}") # Media destination cmd.append(device) # Device cmd = " ".join(cmd) out = __salt__["cmd.run_all"](cmd) _verify_run(out, cmd=cmd) return _xfsdump_output(out["stdout"]) def _xr_to_keyset(line): """ Parse xfsrestore output keyset elements. """ tkns = [elm for elm in line.strip().split(":", 1) if elm] if len(tkns) == 1: return f"'{tkns[0]}': " else: key, val = tkns return f"'{key.strip()}': '{val.strip()}'," def _xfs_inventory_output(out): """ Transform xfsrestore inventory data output to a Python dict source and evaluate it. """ data = [] out = [line for line in out.split("\n") if line.strip()] # No inventory yet if len(out) == 1 and "restore status" in out[0].lower(): return {"restore_status": out[0]} ident = 0 data.append("{") for line in out[:-1]: if len([elm for elm in line.strip().split(":") if elm]) == 1: n_ident = len(re.sub("[^\t]", "", line)) if ident > n_ident: for step in range(ident): data.append("},") ident = n_ident data.append(_xr_to_keyset(line)) data.append("{") else: data.append(_xr_to_keyset(line)) for step in range(ident + 1): data.append("},") data.append("},") # We are evaling into a python dict, a json load # would be safer data = eval("\n".join(data))[0] # pylint: disable=W0123 data["restore_status"] = out[-1] return data def inventory(): """ Display XFS dump inventory without restoration. CLI Example: .. code-block:: bash salt '*' xfs.inventory """ out = __salt__["cmd.run_all"]("xfsrestore -I") _verify_run(out) return _xfs_inventory_output(out["stdout"]) def _xfs_prune_output(out, uuid): """ Parse prune output. """ data = {} cnt = [] cutpoint = False for line in [l.strip() for l in out.split("\n") if l]: if line.startswith("-"): if cutpoint: break else: cutpoint = True continue if cutpoint: cnt.append(line) for kset in [e for e in cnt[1:] if ":" in e]: key, val = (t.strip() for t in kset.split(":", 1)) data[key.lower().replace(" ", "_")] = val return data.get("uuid") == uuid and data or {} def prune_dump(sessionid): """ Prunes the dump session identified by the given session id. CLI Example: .. code-block:: bash salt '*' xfs.prune_dump b74a3586-e52e-4a4a-8775-c3334fa8ea2c """ out = __salt__["cmd.run_all"](f"xfsinvutil -s {sessionid} -F") _verify_run(out) data = _xfs_prune_output(out["stdout"], sessionid) if data: return data raise CommandExecutionError(f'Session UUID "{sessionid}" was not found.') def _blkid_output(out): """ Parse blkid output. """ def flt(data): return [el for el in data if el.strip()] data = {} for dev_meta in flt(out.split("\n\n")): dev = {} for items in flt(dev_meta.strip().split("\n")): key, val = items.split("=", 1) dev[key.lower()] = val if dev.pop("type", None) == "xfs": dev["label"] = dev.get("label") data[dev.pop("devname")] = dev mounts = _get_mounts() for device in mounts: if data.get(device): data[device].update(mounts[device]) return data def devices(): """ Get known XFS formatted devices on the system. CLI Example: .. code-block:: bash salt '*' xfs.devices """ out = __salt__["cmd.run_all"]("blkid -o export") _verify_run(out) return _blkid_output(out["stdout"]) def _xfs_estimate_output(out): """ Parse xfs_estimate output. """ spc = re.compile(r"\s+") data = {} for line in [l for l in out.split("\n") if l.strip()][1:]: directory, bsize, blocks, megabytes, logsize = spc.sub(" ", line).split(" ") data[directory] = { "block _size": bsize, "blocks": blocks, "megabytes": megabytes, "logsize": logsize, } return data def estimate(path): """ Estimate the space that an XFS filesystem will take. For each directory estimate the space that directory would take if it were copied to an XFS filesystem. Estimation does not cross mount points. CLI Example: .. code-block:: bash salt '*' xfs.estimate /path/to/file salt '*' xfs.estimate /path/to/dir/* """ if not os.path.exists(path): raise CommandExecutionError(f'Path "{path}" was not found.') out = __salt__["cmd.run_all"](f"xfs_estimate -v {path}") _verify_run(out) return _xfs_estimate_output(out["stdout"]) def mkfs( device, label=None, ssize=None, noforce=None, bso=None, gmo=None, ino=None, lso=None, rso=None, nmo=None, dso=None, ): """ Create a file system on the specified device. By default wipes out with force. General options: * **label**: Specify volume label. * **ssize**: Specify the fundamental sector size of the filesystem. * **noforce**: Do not force create filesystem, if disk is already formatted. Filesystem geometry options: * **bso**: Block size options. * **gmo**: Global metadata options. * **dso**: Data section options. These options specify the location, size, and other parameters of the data section of the filesystem. * **ino**: Inode options to specify the inode size of the filesystem, and other inode allocation parameters. * **lso**: Log section options. * **nmo**: Naming options. * **rso**: Realtime section options. See the ``mkfs.xfs(8)`` manpage for a more complete description of corresponding options description. CLI Example: .. code-block:: bash salt '*' xfs.mkfs /dev/sda1 salt '*' xfs.mkfs /dev/sda1 dso='su=32k,sw=6' noforce=True salt '*' xfs.mkfs /dev/sda1 dso='su=32k,sw=6' lso='logdev=/dev/sda2,size=10000b' """ def getopts(args): return dict( (args and ("=" in args) and args or None) and [kw.split("=") for kw in args.split(",")] or [] ) cmd = ["mkfs.xfs"] if label: cmd.append("-L") cmd.append(f"'{label}'") if ssize: cmd.append("-s") cmd.append(ssize) for switch, opts in [ ("-b", bso), ("-m", gmo), ("-n", nmo), ("-i", ino), ("-d", dso), ("-l", lso), ("-r", rso), ]: try: if getopts(opts): cmd.append(switch) cmd.append(opts) except Exception: # pylint: disable=broad-except raise CommandExecutionError( f'Wrong parameters "{opts}" for option "{switch}"' ) if not noforce: cmd.append("-f") cmd.append(device) cmd = " ".join(cmd) out = __salt__["cmd.run_all"](cmd) _verify_run(out, cmd=cmd) return _parse_xfs_info(out["stdout"]) def modify(device, label=None, lazy_counting=None, uuid=None): """ Modify parameters of an XFS filesystem. CLI Example: .. code-block:: bash salt '*' xfs.modify /dev/sda1 label='My backup' lazy_counting=False salt '*' xfs.modify /dev/sda1 uuid=False salt '*' xfs.modify /dev/sda1 uuid=True """ if not label and lazy_counting is None and uuid is None: raise CommandExecutionError( f'Nothing specified for modification for "{device}" device' ) cmd = ["xfs_admin"] if label: cmd.append("-L") cmd.append(f"'{label}'") if lazy_counting is False: cmd.append("-c") cmd.append("0") elif lazy_counting: cmd.append("-c") cmd.append("1") if uuid is False: cmd.append("-U") cmd.append("nil") elif uuid: cmd.append("-U") cmd.append("generate") cmd.append(device) cmd = " ".join(cmd) _verify_run(__salt__["cmd.run_all"](cmd), cmd=cmd) out = __salt__["cmd.run_all"](f"blkid -o export {device}") _verify_run(out) return _blkid_output(out["stdout"]) def _get_mounts(): """ List mounted filesystems. """ mounts = {} with salt.utils.files.fopen("/proc/mounts") as fhr: for line in salt.utils.data.decode(fhr.readlines()): device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split( " " ) if fstype != "xfs": continue mounts[device] = { "mount_point": mntpnt, "options": options.split(","), } return mounts def defragment(device): """ Defragment mounted XFS filesystem. In order to mount a filesystem, device should be properly mounted and writable. CLI Example: .. code-block:: bash salt '*' xfs.defragment /dev/sda1 """ if device == "/": raise CommandExecutionError("Root is not a device.") if not _get_mounts().get(device): raise CommandExecutionError(f'Device "{device}" is not mounted') out = __salt__["cmd.run_all"](f"xfs_fsr {device}") _verify_run(out) return {"log": out["stdout"]}