[elbe-devel] [PATCH 3/3] elbepack: initvmaction: use elbepack.cli error handling
Thomas Weißschuh
thomas.weissschuh at linutronix.de
Thu Jul 18 14:47:26 CEST 2024
With the new elbepack.cli-based error handling the ad-hoc calls to
sys.exit() can be replaced by exceptions.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh at linutronix.de>
---
elbepack/initvmaction.py | 308 ++++++++++++++++-------------------------------
1 file changed, 102 insertions(+), 206 deletions(-)
diff --git a/elbepack/initvmaction.py b/elbepack/initvmaction.py
index 51a618cee431..f3274989a9db 100644
--- a/elbepack/initvmaction.py
+++ b/elbepack/initvmaction.py
@@ -10,9 +10,11 @@ import shutil
import socket
import subprocess
import sys
+import textwrap
import time
import elbepack
+from elbepack.cli import CliError, with_cli_details
from elbepack.config import cfg
from elbepack.directories import run_elbe
from elbepack.elbexml import ElbeXML, ValidationError, ValidationMode
@@ -112,30 +114,26 @@ class InitVMAction:
break
if not self.conn:
- print('', file=sys.stderr)
- print('Accessing libvirt provider system not possible.', file=sys.stderr)
- print('Even after waiting 180 seconds.', file=sys.stderr)
- print("Make sure that package 'libvirt-daemon-system' is", file=sys.stderr)
- print('installed, and the service is running properly', file=sys.stderr)
- sys.exit(118)
+ raise CliError(118, textwrap.dedent("""
+ Accessing libvirt provider system not possible.
+ Even after waiting 180 seconds.
+ Make sure that package 'libvirt-daemon-system' is
+ installed, and the service is running properly."""))
elif verr.args[0].startswith('authentication unavailable'):
- print('', file=sys.stderr)
- print('Accessing libvirt provider system not allowed.', file=sys.stderr)
- print('Users which want to use elbe'
- "need to be members of the 'libvirt' group.", file=sys.stderr)
- print("'gpasswd -a <user> libvirt' and logging in again,", file=sys.stderr)
- print('should fix the problem.', file=sys.stderr)
- sys.exit(119)
+ raise CliError(119, textwrap.dedent("""
+ Accessing libvirt provider system not allowed.
+ Users which want to use elbe'
+ need to be members of the 'libvirt' group.
+ 'gpasswd -a <user> libvirt' and logging in again,
+ should fix the problem."""))
elif verr.args[0].startswith('error from service: CheckAuthorization'):
- print('', file=sys.stderr)
- print('Accessing libvirt failed.', file=sys.stderr)
- print('Probably entering the password for accssing libvirt', file=sys.stderr)
- print("timed out. If this occured after 'elbe initvm create'", file=sys.stderr)
- print("it should be safe to use 'elbe initvm start' to", file=sys.stderr)
- print('continue.', file=sys.stderr)
- sys.exit(120)
+ raise CliError(120, textwrap.dedent("""
+ Accessing libvirt failed.
+ Probably entering the password for accssing libvirt
+ timed out. If this occured after 'elbe initvm create'
+ it should be safe to use 'elbe initvm start' to continue."""))
else:
# In case we get here, the exception is unknown, and we want to see it
@@ -148,7 +146,7 @@ class InitVMAction:
self.initvm = d
if not self.initvm and initvmNeeded:
- sys.exit(121)
+ raise CliError(121, 'No initvm available')
def execute(self, _initvmdir, _opt, _args):
raise NotImplementedError('execute() not implemented')
@@ -186,9 +184,8 @@ def test_soap_communication(sleep=10, wait=120):
if ps.returncode == 0:
break
if time.time() > stop:
- print(f'Waited for {wait/60} minutes and the daemon is still not active.',
- file=sys.stderr)
- sys.exit(123)
+ raise CliError(123,
+ f'Waited for {wait/60} minutes and the daemon is still not active.')
print('*', end='', flush=True)
time.sleep(sleep)
@@ -200,8 +197,7 @@ def check_initvm_dir(initvmdir):
print('Using default initvm directory "./initvm".')
initvmdir = './initvm'
else:
- print('No initvm found!')
- sys.exit(207)
+ raise CliError(207, 'No initvm found!')
return initvmdir
@@ -243,15 +239,14 @@ class StartAction(InitVMAction):
print('This initvm is already running.')
else:
# If no unix socket file is found, assume another VM is bound to the soap port.
- print('There is already another running initvm.\nPlease stop this VM first.')
- sys.exit(211)
+ raise CliError(211, 'There is already another running initvm.\n'
+ 'Please stop this VM first.')
else:
# Try to start the QEMU VM for the given directory.
try:
subprocess.Popen(['make', 'run_qemu'], cwd=initvmdir)
except Exception as e:
- print(f'Running QEMU failed: {e}')
- sys.exit(211)
+ raise with_cli_details(e, 211, f'Running QEMU failed: {e}')
# This will sys.exit on error.
test_soap_communication(sleep=1, wait=60)
@@ -261,8 +256,7 @@ class StartAction(InitVMAction):
import libvirt
if self.initvm_state() == libvirt.VIR_DOMAIN_RUNNING:
- print('Initvm already running.')
- sys.exit(122)
+ raise CliError(122, 'Initvm already running.')
elif self.initvm_state() == libvirt.VIR_DOMAIN_SHUTOFF:
self._attach_disk_fds()
@@ -298,8 +292,7 @@ class EnsureAction(InitVMAction):
# use port bind test in case of if QEMU mode
if opt.qemu_mode:
if not is_soap_port_reachable():
- print('Elbe initvm in bad state.\nNo process found on soap port.')
- sys.exit(206)
+ raise CliError(206, 'Elbe initvm in bad state.\nNo process found on soap port.')
return
import libvirt
@@ -309,8 +302,7 @@ class EnsureAction(InitVMAction):
elif self.initvm_state() == libvirt.VIR_DOMAIN_RUNNING:
test_soap_communication()
else:
- print('Elbe initvm in bad state.')
- sys.exit(124)
+ raise CliError(124, 'Elbe initvm in bad state.')
@InitVMAction.register('stop')
@@ -329,8 +321,7 @@ class StopAction(InitVMAction):
# Test if QEMU monitor unix-socket file exists, and error exit if not.
if not os.path.exists(socket_path):
- print('No unix socket found for this vm!\nunable to shutdown this vm.')
- sys.exit(212)
+ raise CliError(212, 'No unix socket found for this vm!\nunable to shutdown this vm.')
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
@@ -355,8 +346,7 @@ class StopAction(InitVMAction):
import libvirt
if self.initvm_state() != libvirt.VIR_DOMAIN_RUNNING:
- print('Initvm is not running.')
- sys.exit(125)
+ raise CliError(125, 'Initvm is not running.')
while True:
sys.stdout.write('*')
@@ -404,8 +394,8 @@ class AttachAction(InitVMAction):
# Test if socat command is available.
if shutil.which('socat') is None:
- print('The command "socat" is required.\nPlease install socat: sudo apt install socat')
- sys.exit(208)
+ raise CliError(208, 'The command "socat" is required.\n'
+ 'Please install socat: sudo apt install socat')
# Connect to socket file, if it exists.
if os.path.exists(os.path.join(initvmdir, 'vm-serial-socket')):
@@ -413,17 +403,16 @@ class AttachAction(InitVMAction):
'unix-connect:vm-serial-socket'],
cwd=initvmdir, check=False)
else:
- print('No unix socket found for the console of this vm!\nUnable to attach.')
+ msg = 'No unix socket found for the console of this vm!\nUnable to attach.'
if is_soap_port_reachable():
- print('There seems to be another initvm running. The soap port is in use.')
- sys.exit(212)
+ msg += '\nThere seems to be another initvm running. The soap port is in use.'
+ raise CliError(212, msg)
def _attach_libvirt_vm(self):
import libvirt
if self.initvm_state() != libvirt.VIR_DOMAIN_RUNNING:
- print('Error: Initvm not running properly.')
- sys.exit(126)
+ raise CliError(126, 'Error: Initvm not running properly.')
print('Attaching to initvm console.')
subprocess.run(['virsh', '--connect', 'qemu:///system', 'console', cfg['initvm_domain']],
@@ -443,9 +432,7 @@ def submit_with_repodir_and_dl_result(xmlfile, cdrom, opt):
with Repodir(xmlfile, preprocess_xmlfile):
submit_and_dl_result(preprocess_xmlfile, cdrom, opt)
except RepodirError as err:
- print('elbe repodir failed', file=sys.stderr)
- print(err, file=sys.stderr)
- sys.exit(127)
+ raise with_cli_details(err, 127, 'elbe repodir failed')
finally:
os.remove(preprocess_xmlfile)
@@ -454,22 +441,13 @@ def submit_and_dl_result(xmlfile, cdrom, opt):
with preprocess_file(xmlfile, opt.variants) as xmlfile:
- ps = run_elbe(['control', 'create_project'], capture_output=True, encoding='utf-8')
- if ps.returncode != 0:
- print('elbe control create_project failed.', file=sys.stderr)
- print(ps.stderr, file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(128)
+ ps = run_elbe(['control', 'create_project'],
+ capture_output=True, encoding='utf-8', check=True)
prjdir = ps.stdout.strip()
ps = run_elbe(['control', 'set_xml', prjdir, xmlfile],
- capture_output=True, encoding='utf-8')
- if ps.returncode != 0:
- print('elbe control set_xml failed2', file=sys.stderr)
- print(ps.stderr, file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(129)
+ capture_output=True, encoding='utf-8', check=True)
if opt.writeproject:
with open(opt.writeproject, 'w') as wpf:
@@ -477,12 +455,7 @@ def submit_and_dl_result(xmlfile, cdrom, opt):
if cdrom is not None:
print('Uploading CDROM. This might take a while')
- try:
- run_elbe(['control', 'set_cdrom', prjdir, cdrom], check=True)
- except subprocess.CalledProcessError:
- print('elbe control set_cdrom Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(131)
+ run_elbe(['control', 'set_cdrom', prjdir, cdrom], check=True)
print('Upload finished')
@@ -494,46 +467,29 @@ def submit_and_dl_result(xmlfile, cdrom, opt):
if cdrom:
build_opts.append('--skip-pbuilder')
- try:
- run_elbe(['control', 'build', prjdir, *build_opts], check=True)
- except subprocess.CalledProcessError:
- print('elbe control build Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(132)
+ run_elbe(['control', 'build', prjdir, *build_opts], check=True)
print('Build started, waiting till it finishes')
try:
run_elbe(['control', 'wait_busy', prjdir], check=True)
- except subprocess.CalledProcessError:
- print('elbe control wait_busy Failed', file=sys.stderr)
- print('', file=sys.stderr)
- print('The project will not be deleted from the initvm.',
- file=sys.stderr)
- print('The files, that have been built, can be downloaded using:',
- file=sys.stderr)
- print(
- f'{prog} control get_files --output "{opt.outdir}" "{prjdir}"',
- file=sys.stderr)
- print('', file=sys.stderr)
- print('The project can then be removed using:',
- file=sys.stderr)
- print(f'{prog} control del_project "{prjdir}"',
- file=sys.stderr)
- print('', file=sys.stderr)
- sys.exit(133)
+ except subprocess.CalledProcessError as e:
+ raise with_cli_details(e, 133, textwrap.dedent(f"""
+ elbe control wait_busy Failed
+
+ The project will not be deleted from the initvm.
+ The files, that have been built, can be downloaded using:
+ {prog} control get_files --output "{opt.outdir}" "{prjdir}"
+
+ The project can then be removed using:
+ {prog} control del_project "{prjdir}" """))
print('')
print('Build finished !')
print('')
if opt.build_sdk:
- try:
- run_elbe(['control', 'build_sdk', prjdir], check=True)
- except subprocess.CalledProcessError:
- print('elbe control build_sdk Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(134)
+ run_elbe(['control', 'build_sdk', prjdir], check=True)
print('SDK Build started, waiting till it finishes')
@@ -582,12 +538,7 @@ def submit_and_dl_result(xmlfile, cdrom, opt):
print('')
print('Listing available files:')
print('')
- try:
- run_elbe(['control', 'get_files', prjdir], check=True)
- except subprocess.CalledProcessError:
- print('elbe control get_files Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(137)
+ run_elbe(['control', 'get_files', prjdir], check=True)
print('')
print(f'Get Files with: elbe control get_file "{prjdir}" <filename>')
@@ -598,20 +549,10 @@ def submit_and_dl_result(xmlfile, cdrom, opt):
ensure_outdir(opt)
- try:
- run_elbe(['control', 'get_files', '--output', opt.outdir, prjdir], check=True)
- except subprocess.CalledProcessError:
- print('elbe control get_files Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(138)
+ run_elbe(['control', 'get_files', '--output', opt.outdir, prjdir], check=True)
if not opt.keep_files:
- try:
- run_elbe(['control', 'del_project', prjdir], check=True)
- except subprocess.CalledProcessError:
- print('remove project from initvm failed',
- file=sys.stderr)
- sys.exit(139)
+ run_elbe(['control', 'del_project', prjdir], check=True)
def extract_cdrom(cdrom):
@@ -635,31 +576,18 @@ def extract_cdrom(cdrom):
print('', file=sys.stderr)
if not tmp.isfile('source.xml'):
- print(
- 'Iso image does not contain a source.xml file',
- file=sys.stderr)
- print(
- "This is not supported by 'elbe initvm'",
- file=sys.stderr)
- print('', file=sys.stderr)
- print('Exiting !!!', file=sys.stderr)
- sys.exit(140)
+ raise CliError(140, textwrap.dedent("""
+ Iso image does not contain a source.xml file.
+ This is not supported by 'elbe initvm'."""))
try:
exml = ElbeXML(
tmp.fname('source.xml'),
url_validation=ValidationMode.NO_CHECK)
except ValidationError as e:
- print(
- 'Iso image does contain a source.xml file.',
- file=sys.stderr)
- print(
- 'But that xml does not validate correctly',
- file=sys.stderr)
- print('', file=sys.stderr)
- print('Exiting !!!', file=sys.stderr)
- print(e)
- sys.exit(141)
+ raise with_cli_details(e, 141, textwrap.dedent("""
+ Iso image does contain a source.xml file.
+ But that xml does not validate correctly."""))
print('Iso Image with valid source.xml detected !')
print(f'Image was generated using Elbe Version {exml.get_elbe_version()}')
@@ -676,27 +604,27 @@ class CreateAction(InitVMAction):
def execute(self, initvmdir, opt, args):
if self.initvm is not None and not opt.qemu_mode:
- print(f"Initvm is already defined for the libvirt domain '{cfg['initvm_domain']}'.\n")
- print('If you want to build in your old initvm, use `elbe initvm submit <xml>`.')
- print('If you want to remove your old initvm from libvirt run `elbe initvm destroy`.\n')
- print('You can specify another libvirt domain by setting the '
- 'ELBE_INITVM_DOMAIN environment variable to an unused domain name.\n')
- print('Note:')
- print('\t1) You can reimport your old initvm via '
- '`virsh --connect qemu:///system define <file>`')
- print('\t where <file> is the corresponding libvirt.xml')
- print('\t2) virsh --connect qemu:///system undefine does not delete the image '
- 'of your old initvm.')
- sys.exit(142)
+ raise CliError(142, textwrap.dedent(f"""
+ Initvm is already defined for the libvirt domain '{cfg['initvm_domain']}'.
+ If you want to build in your old initvm, use `elbe initvm submit <xml>`.')
+ If you want to remove your old initvm from libvirt run `elbe initvm destroy`.
+ You can specify another libvirt domain by setting the
+ ELBE_INITVM_DOMAIN environment variable to an unused domain name.
+ Note:
+ \t1) You can reimport your old initvm via
+ `virsh --connect qemu:///system define <file>`
+ \t where <file> is the corresponding libvirt.xml
+ \t2) virsh --connect qemu:///system undefine does not delete the image
+ of your old initvm."""))
# Upgrade from older versions which used tmux
try:
subprocess.run(['tmux', 'has-session', '-t', 'ElbeInitVMSession'],
stderr=subprocess.DEVNULL, check=True)
- print('ElbeInitVMSession exists in tmux. '
- 'It may belong to an old elbe version. '
- 'Please stop it to prevent interfering with this version.', file=sys.stderr)
- sys.exit(143)
+ raise CliError(143, textwrap.dedent("""
+ ElbeInitVMSession exists in tmux.
+ It may belong to an old elbe version.
+ Please stop it to prevent interfering with this version."""))
except (subprocess.CalledProcessError, FileNotFoundError):
pass
@@ -723,41 +651,32 @@ class CreateAction(InitVMAction):
xmlfile = tmp.fname('source.xml')
cdrom = args[0]
else:
- print(
- 'Unknown file ending (use either xml or iso)',
- file=sys.stderr)
- sys.exit(144)
+ raise CliError(144, 'Unknown file ending (use either xml or iso)')
else:
# No xml File was specified, build the default elbe-init-with-ssh
xmlfile = os.path.join(
elbepack.__path__[0],
'init/default-init.xml')
- try:
- init_opts = []
+ init_opts = []
- if not opt.build_bin:
- init_opts.append('--skip-build-bin')
+ if not opt.build_bin:
+ init_opts.append('--skip-build-bin')
- if not opt.build_sources:
- init_opts.append('--skip-build-source')
+ if not opt.build_sources:
+ init_opts.append('--skip-build-source')
- if opt.fail_on_warning:
- init_opts.append('--fail-on-warning')
-
- if cdrom:
- cdrom_opts = ['--cdrom', cdrom]
- else:
- cdrom_opts = []
+ if opt.fail_on_warning:
+ init_opts.append('--fail-on-warning')
- with preprocess_file(xmlfile, opt.variants) as preproc:
- run_elbe(['init', *init_opts, '--directory', initvmdir, *cdrom_opts, preproc],
- check=True)
+ if cdrom:
+ cdrom_opts = ['--cdrom', cdrom]
+ else:
+ cdrom_opts = []
- except subprocess.CalledProcessError:
- print("'elbe init' Failed", file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(145)
+ with preprocess_file(xmlfile, opt.variants) as preproc:
+ run_elbe(['init', *init_opts, '--directory', initvmdir, *cdrom_opts, preproc],
+ check=True)
# Skip libvirt VM creation in QEMU mode.
if not opt.qemu_mode:
@@ -770,42 +689,27 @@ class CreateAction(InitVMAction):
# Register initvm in libvirt.
try:
self.conn.defineXML(xml)
- except subprocess.CalledProcessError:
- print('Registering initvm in libvirt failed', file=sys.stderr)
- print('Try `elbe initvm destroy` to delete existing initvm',
- file=sys.stderr)
- sys.exit(146)
+ except subprocess.CalledProcessError as e:
+ raise with_cli_details(e, 146, textwrap.dedent("""
+ Registering initvm in libvirt failed.
+ Try `elbe initvm destroy` to delete existing initvm."""))
# Build initvm
- try:
- subprocess.run(['make'], cwd=initvmdir, check=True)
- except subprocess.CalledProcessError:
- print('Building the initvm Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(147)
+ subprocess.run(['make'], cwd=initvmdir, check=True)
# In case of QEMU mode, we need to forward the additional parameters.
additional_params = []
if opt.qemu_mode:
additional_params = ['--qemu', f'--directory={initvmdir}']
- ps = run_elbe(['initvm', 'start', *additional_params], capture_output=False,
- encoding='utf-8')
- if ps.returncode != 0:
- print('Starting the initvm Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(148)
+ run_elbe(['initvm', 'start', *additional_params], check=True)
if len(args) == 1:
# If provided xml file has no initvm section xmlfile is set to a
# default initvm XML file. But we need the original file here.
if args[0].endswith('.xml'):
# Stop here if no project node was specified.
- try:
- x = etree(args[0])
- except ValidationError as e:
- print(f'XML file is invalid: {e}')
- sys.exit(149)
+ x = etree(args[0])
if not x.has('project'):
print("elbe initvm ready: use 'elbe initvm submit "
"myproject.xml' to build a project")
@@ -839,12 +743,7 @@ class SubmitAction(InitVMAction):
if opt.qemu_mode:
additional_params = ['--qemu', f'--directory={initvmdir}']
- ps = run_elbe(['initvm', 'ensure', *additional_params], capture_output=True,
- encoding='utf-8')
- if ps.returncode != 0:
- print('Starting the initvm Failed', file=sys.stderr)
- print('Giving up', file=sys.stderr)
- sys.exit(150)
+ run_elbe(['initvm', 'ensure', *additional_params], check=True)
# Init cdrom to None, if we detect it, we set it
cdrom = None
@@ -860,10 +759,7 @@ class SubmitAction(InitVMAction):
xmlfile = tmp.fname('source.xml')
cdrom = args[0]
else:
- print(
- 'Unknown file ending (use either xml or iso)',
- file=sys.stderr)
- sys.exit(151)
+ raise CliError(151, 'Unknown file ending (use either xml or iso)')
submit_with_repodir_and_dl_result(xmlfile, cdrom, opt)
--
2.45.2
More information about the elbe-devel
mailing list