mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
185 lines
4.4 KiB
Python
185 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
#
|
|
# This copyrighted material is made available to anyone wishing to use,
|
|
# modify, copy, or redistribute it subject to the terms and conditions
|
|
# of the GNU General Public License v.2.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# Copyright 2015-2016, Vratislav Podzimek <vpodzime@redhat.com>
|
|
|
|
import subprocess
|
|
import shlex
|
|
from fcntl import fcntl, F_GETFL, F_SETFL
|
|
from os import O_NONBLOCK
|
|
import traceback
|
|
import sys
|
|
import re
|
|
|
|
try:
|
|
from .cfg import LVM_CMD
|
|
from .utils import log_debug, log_error
|
|
except:
|
|
from cfg import LVM_CMD
|
|
from utils import log_debug, log_error
|
|
|
|
SHELL_PROMPT = "lvm> "
|
|
|
|
|
|
def _quote_arg(arg):
|
|
if len(shlex.split(arg)) > 1:
|
|
return '"%s"' % arg
|
|
else:
|
|
return arg
|
|
|
|
|
|
class LVMShellProxy(object):
|
|
def _read_until_prompt(self):
|
|
prev_ec = None
|
|
stdout = ""
|
|
while not stdout.endswith(SHELL_PROMPT):
|
|
try:
|
|
tmp = self.lvm_shell.stdout.read()
|
|
if tmp:
|
|
stdout += tmp.decode("utf-8")
|
|
except IOError:
|
|
# nothing written yet
|
|
pass
|
|
|
|
# strip the prompt from the STDOUT before returning and grab the exit
|
|
# code if it's available
|
|
m = self.re.match(stdout)
|
|
if m:
|
|
prev_ec = int(m.group(2))
|
|
strip_idx = -1 * len(m.group(1))
|
|
else:
|
|
strip_idx = -1 * len(SHELL_PROMPT)
|
|
|
|
return stdout[:strip_idx], prev_ec
|
|
|
|
def _read_line(self):
|
|
while True:
|
|
try:
|
|
tmp = self.lvm_shell.stdout.readline()
|
|
if tmp:
|
|
return tmp.decode("utf-8")
|
|
except IOError:
|
|
pass
|
|
|
|
def _discard_echo(self, expected):
|
|
line = ""
|
|
while line != expected:
|
|
# GNU readline inserts some interesting characters at times...
|
|
line += self._read_line().replace(' \r', '')
|
|
|
|
def _write_cmd(self, cmd):
|
|
cmd_bytes = bytes(cmd, "utf-8")
|
|
num_written = self.lvm_shell.stdin.write(cmd_bytes)
|
|
assert (num_written == len(cmd_bytes))
|
|
self.lvm_shell.stdin.flush()
|
|
|
|
def _lvm_echos(self):
|
|
echo = False
|
|
cmd = "version\n"
|
|
self._write_cmd(cmd)
|
|
line = self._read_line()
|
|
|
|
if line == cmd:
|
|
echo = True
|
|
|
|
self._read_until_prompt()
|
|
|
|
return echo
|
|
|
|
def __init__(self):
|
|
self.re = re.compile(".*(\[(-?[0-9]+)\] lvm> $)", re.DOTALL)
|
|
|
|
# run the lvm shell
|
|
self.lvm_shell = subprocess.Popen(
|
|
[LVM_CMD], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE, close_fds=True)
|
|
flags = fcntl(self.lvm_shell.stdout, F_GETFL)
|
|
fcntl(self.lvm_shell.stdout, F_SETFL, flags | O_NONBLOCK)
|
|
flags = fcntl(self.lvm_shell.stderr, F_GETFL)
|
|
fcntl(self.lvm_shell.stderr, F_SETFL, flags | O_NONBLOCK)
|
|
|
|
# wait for the first prompt
|
|
self._read_until_prompt()
|
|
|
|
# Check to see if the version of LVM we are using is running with
|
|
# gnu readline which will echo our writes from stdin to stdout
|
|
self.echo = self._lvm_echos()
|
|
|
|
def call_lvm(self, argv, debug=False):
|
|
# create the command string
|
|
cmd = " ".join(_quote_arg(arg) for arg in argv)
|
|
cmd += "\n"
|
|
|
|
# run the command by writing it to the shell's STDIN
|
|
self._write_cmd(cmd)
|
|
|
|
# If lvm is utilizing gnu readline, it echos stdin to stdout
|
|
if self.echo:
|
|
self._discard_echo(cmd)
|
|
|
|
# read everything from the STDOUT to the next prompt
|
|
stdout, exit_code = self._read_until_prompt()
|
|
|
|
# read everything from STDERR if there's something (we waited for the
|
|
# prompt on STDOUT so there should be all or nothing at this point on
|
|
# STDERR)
|
|
stderr = None
|
|
try:
|
|
t_error = self.lvm_shell.stderr.read()
|
|
if t_error:
|
|
stderr = t_error.decode("utf-8")
|
|
except IOError:
|
|
# nothing on STDERR
|
|
pass
|
|
|
|
if exit_code is not None:
|
|
rc = exit_code
|
|
else:
|
|
# LVM does write to stderr even when it did complete successfully,
|
|
# so without having the exit code in the prompt we can never be
|
|
# sure.
|
|
if stderr:
|
|
rc = 1
|
|
else:
|
|
rc = 0
|
|
|
|
if debug or rc != 0:
|
|
log_error(('CMD: %s' % cmd))
|
|
log_error(("EC = %d" % rc))
|
|
log_error(("STDOUT=\n %s\n" % stdout))
|
|
log_error(("STDERR=\n %s\n" % stderr))
|
|
|
|
return (rc, stdout, stderr)
|
|
|
|
def __del__(self):
|
|
self.lvm_shell.terminate()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
shell = LVMShellProxy()
|
|
in_line = "start"
|
|
try:
|
|
while in_line:
|
|
in_line = input("lvm> ")
|
|
if in_line:
|
|
ret, out, err, = shell.call_lvm(in_line.split())
|
|
print(("RET: %d" % ret))
|
|
print(("OUT:\n%s" % out))
|
|
print(("ERR:\n%s" % err))
|
|
except KeyboardInterrupt:
|
|
pass
|
|
except EOFError:
|
|
pass
|
|
except Exception:
|
|
traceback.print_exc(file=sys.stdout)
|
|
finally:
|
|
print()
|