Skip to content

Instantly share code, notes, and snippets.

@csonto
Created September 29, 2015 16:22
Show Gist options
  • Save csonto/91ca080b9ec4b122ace6 to your computer and use it in GitHub Desktop.
Save csonto/91ca080b9ec4b122ace6 to your computer and use it in GitHub Desktop.
lvm-json
#!/usr/bin/python
import sys
import json
from lvmjson import *
import argparse
if __name__ == "__main__":
AVAILABLE_COMMANDS = ["lvs", "vgs", "pvs", "dminfo"]
ap = argparse.ArgumentParser(description="Take a command and produce JSON output.")
ap.add_argument("--segments", action="store_const", const=True, default=False, help="list segments. (Makes sense only for lvs.)")
ap.add_argument("-a", "--all", action="store_const", const=True, default=False, help="list hidden LVs. (Makes sense only for lvs.)")
ap.add_argument("-v", "--verbose", action="store_true", default=False, help="list more fields. (Makes sense only for lvs.)")
ap.add_argument("-o", "--options", metavar="FIELDS", type=str, action="append",
help="Comma separated list of fields to list with optional `+` prefix to add to the list of fields.\n"
+ "This option can be repeated. With `+` prefix the FIELDS are added to previous value, without the prfix it is overridden.")
ap.add_argument("COMMAND", metavar="COMMAND", nargs=1, help="Command to run. One of `lvs`, `vgs`, `pvs` or `dminfo` (shortcut for `dmsetup info`).")
ap.add_argument("DEV", metavar="DEV", nargs="*", help="An object to be passed to COMMAND. By default all objects are listed.")
args = ap.parse_args()
#import pprint
#pprint.pprint(args)
d = {}
d["all"] = args.all or False
d["verbose"] = args.verbose or False
d["segments"] = args.segments or False
d["options"] = args.options or []
if not args.COMMAND:
print >> sys.stderr, "COMMAND is mandatory!"
sys.exit(1)
cmd = args.COMMAND[0]
if cmd not in AVAILABLE_COMMANDS:
print >> sys.stderr, "COMMAND(%s) must be one of %s." % (cmd, AVAILABLE_COMMANDS)
sys.exit(1)
if cmd == "dminfo":
r = dmsetup_list(args.DEV, d)
else:
r = lvm_list_cmd(cmd, args.DEV, d)
print json.dumps(r, indent=2)
import re
import subprocess
_KEY_VALUE_RE=re.compile("([A-Z0-9_]*)='(([^\\\\']|\\\\.)*)'")
def lvm_parse(s):
"""
Parse output of lvs, pvs or vgs with `--nameprefixes --noheadings` and return list of dictionaries.
"""
lines=s.splitlines()
return [dict((match[0], match[1]) for match in _KEY_VALUE_RE.findall(line)) for line in lines]
def _opts(opts):
answ = ""
for opt in opts:
if opt:
if opt[0] == "+":
answ = answ+","+opt[1:]
else:
answ = opt
if answ and answ[0] == ",":
return answ[1:]
else:
return answ
def _lvm_cmd(cmd, args, kwargs):
opts = _opts(kwargs.get("options", []))
#print "%r" % ((cmd, args, kwargs, opts),)
return (
[cmd, "--nameprefixes", "--noheadings"] + args + kwargs.get("devices", [])
+ (["--all"] if kwargs.get("all", False) else [])
+ (["--verbose"] if kwargs.get("verbose", False) else [])
+ (["--segments"] if cmd == "lvs" and kwargs.get("segments", False) else [])
+ (["-o" + opts] if len(opts) > 0 else [])
)
def _dmsetup(args, kwargs):
opts = _opts(kwargs.get("options", []))
return (
["dmsetup", "info", "-c", "--nameprefixes", "--noheadings"] + args + kwargs.get("devices", [])
+ (["-o" + opts] if len(opts) > 0 else [])
)
def _run_cmd(cmd, kwargs):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
r = { "cmd": cmd, "rc": p.returncode, "stdout": out, "stderr": err }
return r
def lvm_list_cmd(cmd, args, kwargs):
"""
Return dictionary with cmd, rc, stdout, stderr and parsed
"""
d = _run_cmd(_lvm_cmd(cmd, args, kwargs), kwargs)
d["parsed"] = lvm_parse(d["stdout"])
return d
def dmsetup_list(args, kwargs):
"""
Return dictionary with cmd, rc, stdout, stderr and parsed
"""
d = _run_cmd(_dmsetup(args, kwargs), kwargs)
d["parsed"] = lvm_parse(d["stdout"])
return d
@csonto
Copy link
Author

csonto commented Sep 30, 2015

Running command ./lvm-json -v lvs /dev/vg_cimrman/lv_home will produce:

{
  "stdout": "  LVM2_LV_NAME='lv_home' LVM2_VG_NAME='vg_cimrman' LVM2_SEG_COUNT='1' LVM2_LV_ATTR='-wi-ao---' LVM2_LV_SIZE='400.00g' LVM2_LV_MAJOR='-1' LVM2_LV_MINOR='-1' LVM2_LV_KERNEL_MAJOR='253' LVM2_LV_KERNEL_MINOR='140' LVM2_POOL_LV='' LVM2_ORIGIN='' LVM2_DATA_PERCENT='' LVM2_METADATA_PERCENT='' LVM2_MOVE_PV='' LVM2_COPY_PERCENT='' LVM2_MIRROR_LOG='' LVM2_CONVERT_LV='' LVM2_LV_UUID='tLwZcy-85bh-CAD0-BlvZ-1u0T-PcPJ-bINxXv'\n",
  "cmd": [
    "lvs",
    "--nameprefixes",
    "--noheadings",
    "/dev/vg_cimrman/lv_home",
    "--verbose"
  ],
  "host": "",
  "stderr": "    Using logical volume(s) on command line\n",
  "rc": 0,
  "parsed": [
    {
      "LVM2_CONVERT_LV": "",
      "LVM2_LV_MINOR": "-1",
      "LVM2_LV_KERNEL_MAJOR": "253",
      "LVM2_LV_ATTR": "-wi-ao---",
      "LVM2_POOL_LV": "",
      "LVM2_DATA_PERCENT": "",
      "LVM2_ORIGIN": "",
      "LVM2_COPY_PERCENT": "",
      "LVM2_MIRROR_LOG": "",
      "LVM2_LV_NAME": "lv_home",
      "LVM2_METADATA_PERCENT": "",
      "LVM2_VG_NAME": "vg_cimrman",
      "LVM2_LV_KERNEL_MINOR": "140",
      "LVM2_SEG_COUNT": "1",
      "LVM2_LV_SIZE": "400.00g",
      "LVM2_MOVE_PV": "",
      "LVM2_LV_UUID": "tLwZcy-85bh-CAD0-BlvZ-1u0T-PcPJ-bINxXv",
      "LVM2_LV_MAJOR": "-1"
    }
  ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment