mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-18 11:32:04 +00:00
fix: make messages say what was verified (source questions from the Italian review)
This commit is contained in:
@@ -228,16 +228,18 @@ def apply(root, primary_id, vmid, images, acceleration, render_device, gfx_overr
|
||||
|
||||
|
||||
def revert(root, primary_id, vmid, images, previous, was_running):
|
||||
"""Give the container and the records the choice they had."""
|
||||
"""Give the container and the records the choice they had. Returns
|
||||
whether they have it again."""
|
||||
try:
|
||||
if oci_stack_modify.is_running(vmid):
|
||||
stop(vmid)
|
||||
apply(root, primary_id, vmid, images, *previous)
|
||||
except (OSError, ValueError, KeyError, RuntimeError, subprocess.SubprocessError) as error:
|
||||
msg_warn(f"{translate('The previous recognition choice could not be put back:')} {error}")
|
||||
return
|
||||
return False
|
||||
if was_running:
|
||||
subprocess.run(['pct', 'start', str(vmid)], capture_output=True, check=False)
|
||||
return True
|
||||
|
||||
|
||||
def change(root, primary_id, acceleration, render_device=None, gfx_override=None):
|
||||
@@ -262,17 +264,17 @@ def change(root, primary_id, acceleration, render_device=None, gfx_override=None
|
||||
msg_info(translate('Preparing the machine learning container for the new choice...'))
|
||||
apply(root, primary_id, vmid, images, acceleration, render_device, gfx_override)
|
||||
msg_ok(translate('Machine learning container prepared'))
|
||||
except BaseException:
|
||||
revert(root, primary_id, vmid, images, previous, was_running)
|
||||
except BaseException as error:
|
||||
error.recognition_kept = revert(root, primary_id, vmid, images, previous, was_running)
|
||||
raise
|
||||
try:
|
||||
# The image of the new choice replaces the one in use, as an update does.
|
||||
oci_stack_native.run(primary_id, acknowledge_external_data=True)
|
||||
except BaseException:
|
||||
except BaseException as error:
|
||||
with instances.locked(root):
|
||||
# An update left halfway keeps its own record of what to restore.
|
||||
if not instances.read(root, primary_id).get('pending_stack_transaction'):
|
||||
revert(root, primary_id, vmid, images, previous, was_running)
|
||||
error.recognition_kept = (not instances.read(root, primary_id).get('pending_stack_transaction')
|
||||
and revert(root, primary_id, vmid, images, previous, was_running))
|
||||
raise
|
||||
if was_running and not oci_stack_modify.is_running(vmid):
|
||||
# An update leaves each container as it found it, and this one was
|
||||
@@ -297,7 +299,12 @@ def main():
|
||||
return 1
|
||||
except (OSError, ValueError, KeyError, RuntimeError, StopIteration, subprocess.SubprocessError) as error:
|
||||
if not getattr(error, 'oci_reported', False):
|
||||
msg_error(f"{translate('The recognition of Immich was not changed:')} {error}")
|
||||
# Only an error raised before anything was touched, or one that
|
||||
# was put back, leaves the previous choice in place.
|
||||
kept = getattr(error, 'recognition_kept', True)
|
||||
message = (translate('The recognition of Immich was not changed:') if kept
|
||||
else translate('The recognition change of Immich did not complete:'))
|
||||
msg_error(f"{message} {error}")
|
||||
return 1
|
||||
msg_ok(translate('Recognition of Immich changed; the application was updated with the new choice.'))
|
||||
return 0
|
||||
|
||||
@@ -768,29 +768,33 @@ def release_stage(state, discard=False):
|
||||
"""After a commit the holder CT only keeps its own rootfs: every parked
|
||||
volume went back to the application. After a verified recovery it holds
|
||||
the disks of the failed attempt, which the restored backup replaced:
|
||||
`discard` removes them with it. Otherwise anything still attached keeps it."""
|
||||
`discard` removes them with it. Otherwise anything still attached keeps it.
|
||||
Returns whether the holder CT was removed."""
|
||||
stage = state.get('stage')
|
||||
if not stage:
|
||||
return
|
||||
return False
|
||||
try:
|
||||
config = owned(stage, 'proxmenux-transaction=' + state['id'])
|
||||
except (ValueError, RuntimeError, subprocess.CalledProcessError):
|
||||
return
|
||||
return False
|
||||
held = mounts(config) or any(re.fullmatch(r'unused[0-9]+', key) for key in parse_config(config))
|
||||
if held and not discard:
|
||||
return
|
||||
return False
|
||||
if discard:
|
||||
stop(stage)
|
||||
run('pct', 'destroy', str(stage))
|
||||
return True
|
||||
|
||||
|
||||
def discard_stage(state):
|
||||
"""Remove the holder CT of an operation that ended in a verified recovery.
|
||||
A failure here leaves it in place and does not undo the recovery."""
|
||||
A failure here leaves it in place and does not undo the recovery.
|
||||
Returns whether the holder CT was removed."""
|
||||
try:
|
||||
release_stage(state, discard=True)
|
||||
return release_stage(state, discard=True)
|
||||
except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as error:
|
||||
log(f'cleanup: {error}')
|
||||
return False
|
||||
|
||||
|
||||
def gib(size):
|
||||
@@ -1139,8 +1143,13 @@ def complete_recovery(root, journal, state):
|
||||
checkpoint(journal, state, 'rolled-back')
|
||||
if show:
|
||||
# A member of a stack is released when the whole stack is back.
|
||||
discard_stage(state)
|
||||
msg_ok(translate('Recovery completed. The disks of the failed attempt were removed; the backup is kept.'))
|
||||
if discard_stage(state):
|
||||
msg_ok(translate('Recovery completed. The disks of the failed attempt were removed; the backup is kept.'))
|
||||
else:
|
||||
msg_ok(translate('Recovery completed; the backup is kept.'))
|
||||
if state.get('stage'):
|
||||
msg_warn(f"{translate('The disks of the failed attempt could not be removed and stay in the temporary container:')} "
|
||||
f"CT {state['stage']}")
|
||||
if state.get('original_host_sources') or state.get('desired_host_sources'):
|
||||
msg_info2(translate('Shared host files are kept as they are; the backup does not restore their content.'))
|
||||
|
||||
|
||||
@@ -943,7 +943,7 @@ def start_order(entry):
|
||||
return int(order[1]) if order else 9999
|
||||
|
||||
|
||||
def recover(root, plan, start=False, host_firewall=False):
|
||||
def recover(root, plan, start=False, host_firewall=()):
|
||||
for bridge, subnet in plan['bridges'].items():
|
||||
msg_info(f"{translate('Creating the private network...')} {bridge}")
|
||||
create_bridge(bridge, subnet)
|
||||
@@ -974,7 +974,8 @@ def recover(root, plan, start=False, host_firewall=False):
|
||||
if not rule:
|
||||
continue
|
||||
text = f"TCP {rule['port']} {translate('from')} {rule['source']}"
|
||||
if not host_firewall:
|
||||
# Each rule is confirmed on its own; one answer does not cover the rest.
|
||||
if rule['vmid'] not in host_firewall:
|
||||
plan['notes'].append(f"CT {entry['vmid']}: {translate('its host firewall rule was not added:')} {text}")
|
||||
elif add_firewall_rule(rule):
|
||||
msg_ok(f"{translate('Host firewall rule added:')} {text}")
|
||||
@@ -1020,7 +1021,8 @@ def main():
|
||||
parser.add_argument('action', choices=('list', 'plan', 'recover'))
|
||||
parser.add_argument('--root', type=Path, default=instances.ROOT)
|
||||
parser.add_argument('--start', action='store_true')
|
||||
parser.add_argument('--host-firewall', action='store_true')
|
||||
parser.add_argument('--host-firewall', type=int, action='append', default=[], metavar='VMID',
|
||||
help='Add the host firewall rule of this container; one option for each confirmed rule')
|
||||
parser.add_argument('--automatic', action='store_true',
|
||||
help='Only the applications that need no question: the ones the cluster keeps a record of')
|
||||
args = parser.parse_args()
|
||||
@@ -1041,13 +1043,13 @@ def main():
|
||||
failed = 0
|
||||
if args.automatic:
|
||||
plans = [plan for plan in plans if automatic(plan)]
|
||||
args.start = args.host_firewall = False
|
||||
args.start, args.host_firewall = False, []
|
||||
for plan in plans:
|
||||
containers = ', '.join(f"CT {entry['vmid']}" for entry in plan['restored'])
|
||||
msg_info2(f"{plan['title']} · {containers}")
|
||||
if plan['blockers']:
|
||||
failed += 1
|
||||
msg_error(translate('This application cannot be recovered yet; nothing was changed:'))
|
||||
msg_error(translate('This application cannot be recovered yet:'))
|
||||
for line in plan['blockers']:
|
||||
msg_info2(f" - {line}")
|
||||
lost = [entry for entry in plan['restored'] if entry.get('unrecoverable')]
|
||||
|
||||
Reference in New Issue
Block a user