2023-07-22 23:59:49 +03:00
|
|
|
"""
|
|
|
|
Execute shell commands on guest via guest agent.
|
|
|
|
|
|
|
|
Usage: na-vmexec [options] <machine> <command>
|
|
|
|
|
|
|
|
Options:
|
|
|
|
-c, --config <file> Config file [default: /etc/node-agent/config.yaml]
|
2023-07-29 15:10:30 +03:00
|
|
|
-l, --loglvl <lvl> Logging level
|
2023-07-22 23:59:49 +03:00
|
|
|
-s, --shell <shell> Guest shell [default: /bin/sh]
|
|
|
|
-t, --timeout <sec> QEMU timeout in seconds to stop polling command status [default: 60]
|
|
|
|
"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import pathlib
|
|
|
|
import logging
|
|
|
|
|
|
|
|
from docopt import docopt
|
|
|
|
|
2023-07-29 14:29:37 +03:00
|
|
|
from ..main import LibvirtSession
|
2023-07-29 15:35:36 +03:00
|
|
|
from ..vm import QemuAgent, QemuAgentError, VMNotFound
|
2023-07-22 23:59:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
levels = logging.getLevelNamesMapping()
|
|
|
|
|
|
|
|
|
|
|
|
class Color:
|
|
|
|
RED = '\033[31m'
|
|
|
|
GREEN = '\033[32m'
|
|
|
|
YELLOW = '\033[33m'
|
|
|
|
NONE = '\033[0m'
|
|
|
|
|
|
|
|
|
|
|
|
def cli():
|
|
|
|
args = docopt(__doc__)
|
|
|
|
config = pathlib.Path(args['--config']) or None
|
2023-07-29 15:10:30 +03:00
|
|
|
loglvl = None
|
2023-07-28 01:01:32 +03:00
|
|
|
machine = args['<machine>']
|
2023-07-22 23:59:49 +03:00
|
|
|
|
2023-07-29 15:10:30 +03:00
|
|
|
if args['--loglvl']:
|
|
|
|
loglvl = args['--loglvl'].upper()
|
|
|
|
|
2023-07-22 23:59:49 +03:00
|
|
|
if loglvl in levels:
|
|
|
|
logging.basicConfig(level=levels[loglvl])
|
|
|
|
|
|
|
|
with LibvirtSession(config) as session:
|
|
|
|
shell = args['--shell']
|
|
|
|
cmd = args['<command>']
|
|
|
|
|
|
|
|
try:
|
2023-07-28 01:01:32 +03:00
|
|
|
ga = QemuAgent(session, machine)
|
2023-07-22 23:59:49 +03:00
|
|
|
exited, exitcode, stdout, stderr = ga.shellexec(
|
|
|
|
cmd,
|
|
|
|
executable=shell,
|
|
|
|
capture_output=True,
|
|
|
|
decode_output=True,
|
|
|
|
timeout=int(args['--timeout']),
|
|
|
|
)
|
|
|
|
except QemuAgentError as qemuerr:
|
|
|
|
errmsg = f'{Color.RED}{err}{Color.NONE}'
|
|
|
|
if str(err).startswith('Polling command pid='):
|
|
|
|
errmsg = (
|
|
|
|
errmsg + Color.YELLOW
|
|
|
|
+ '\n[NOTE: command may still running]'
|
|
|
|
+ Color.NONE
|
|
|
|
)
|
|
|
|
sys.exit(errmsg)
|
|
|
|
except VMNotFound as err:
|
|
|
|
sys.exit(
|
2023-07-28 01:01:32 +03:00
|
|
|
f'{Color.RED}VM {machine} not found.{Color.NONE}'
|
2023-07-22 23:59:49 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if not exited:
|
|
|
|
print(
|
|
|
|
Color.YELLOW
|
|
|
|
+'[NOTE: command may still running]'
|
2023-07-28 01:01:32 +03:00
|
|
|
+ Color.NONE,
|
|
|
|
file=sys.stderr
|
2023-07-22 23:59:49 +03:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
if exitcode == 0:
|
|
|
|
exitcolor = Color.GREEN
|
|
|
|
else:
|
|
|
|
exitcolor = Color.RED
|
|
|
|
print(
|
|
|
|
exitcolor
|
|
|
|
+ f'[command exited with exit code {exitcode}]'
|
2023-07-28 01:01:32 +03:00
|
|
|
+ Color.NONE,
|
|
|
|
file=sys.stderr
|
2023-07-22 23:59:49 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
if stderr:
|
2023-07-28 01:01:32 +03:00
|
|
|
print(Color.RED + stderr.strip() + Color.NONE, file=sys.stderr)
|
2023-07-22 23:59:49 +03:00
|
|
|
if stdout:
|
2023-07-28 01:01:32 +03:00
|
|
|
print(Color.GREEN + stdout.strip() + Color.NONE, file=sys.stdout)
|
2023-07-29 15:10:30 +03:00
|
|
|
sys.exit(exitcode)
|
2023-07-22 23:59:49 +03:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
cli()
|