From dd54d491057b378d20eea7cdd449753996d0ea4b Mon Sep 17 00:00:00 2001 From: themylogin Date: Thu, 19 Sep 2024 12:25:01 +0200 Subject: [PATCH 1/8] Shutdown/reboot audit --- .../debian/ix-freebsd-to-scale-update.service | 25 ----------- debian/debian/rules | 1 - .../middlewared/api/v25_04_0/__init__.py | 3 +- .../middlewared/api/v25_04_0/smartctl.py | 2 - .../api/v25_04_0/system_lifecycle.py | 32 ++++++++++++++ src/middlewared/middlewared/plugins/config.py | 20 +++++---- .../middlewared/plugins/failover.py | 6 +-- .../plugins/service_/services/all.py | 2 - .../plugins/service_/services/pseudo/misc.py | 12 ------ .../middlewared/plugins/sysdataset.py | 4 +- .../middlewared/plugins/system/lifecycle.py | 40 +++++++++--------- src/middlewared/middlewared/plugins/update.py | 7 ++-- .../plugins/update_/freebsd_to_scale_linux.py | 42 ------------------- tests/api2/test_261_iscsi_cmd.py | 2 +- tests/api2/test_system_lifetime.py | 30 +++++++++++++ 15 files changed, 107 insertions(+), 121 deletions(-) delete mode 100644 debian/debian/ix-freebsd-to-scale-update.service create mode 100644 src/middlewared/middlewared/api/v25_04_0/system_lifecycle.py delete mode 100644 src/middlewared/middlewared/plugins/update_/freebsd_to_scale_linux.py create mode 100644 tests/api2/test_system_lifetime.py diff --git a/debian/debian/ix-freebsd-to-scale-update.service b/debian/debian/ix-freebsd-to-scale-update.service deleted file mode 100644 index d189b90bb4e6..000000000000 --- a/debian/debian/ix-freebsd-to-scale-update.service +++ /dev/null @@ -1,25 +0,0 @@ -[Unit] -Description=Update TrueNAS 12 to SCALE -DefaultDependencies=no - -Before=network-pre.target - -After=middlewared.service -Before=ix-etc.service -Before=ix-netif.service -Before=ix-preinit.service -Before=ix-syncdisks.service -Before=ix-zfs.service -Before=local-fs.target - -ConditionPathExists=/data/freebsd-to-scale-update - -[Service] -Type=oneshot -RemainAfterExit=yes -TimeoutStartSec=300 -ExecStart=midclt call --job update.freebsd_to_scale -StandardOutput=null - -[Install] -WantedBy=multi-user.target diff --git a/debian/debian/rules b/debian/debian/rules index ce0992820b0f..5b9950cd0c44 100755 --- a/debian/debian/rules +++ b/debian/debian/rules @@ -12,7 +12,6 @@ override_dh_installsystemd: dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-boot-core dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-cgroups dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-etc - dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-freebsd-to-scale-update dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-netif dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-postinit dh_installsystemd --no-start -r --no-restart-after-upgrade --name=ix-preinit diff --git a/src/middlewared/middlewared/api/v25_04_0/__init__.py b/src/middlewared/middlewared/api/v25_04_0/__init__.py index 20bbe0e865df..7a5ee4fdc6a1 100644 --- a/src/middlewared/middlewared/api/v25_04_0/__init__.py +++ b/src/middlewared/middlewared/api/v25_04_0/__init__.py @@ -7,7 +7,8 @@ from .group import * # noqa from .keychain import * # noqa from .privilege import * # noqa +from .smartctl import * # noqa from .system_reboot import * # noqa +from .system_lifecycle import * # noqa from .user import * # noqa from .vendor import * # noqa -from .smartctl import * # noqa diff --git a/src/middlewared/middlewared/api/v25_04_0/smartctl.py b/src/middlewared/middlewared/api/v25_04_0/smartctl.py index 3fe99344846b..16b4c8a3f3c4 100644 --- a/src/middlewared/middlewared/api/v25_04_0/smartctl.py +++ b/src/middlewared/middlewared/api/v25_04_0/smartctl.py @@ -1,5 +1,3 @@ -from typing import Any - from middlewared.api.base import BaseModel __all__ = ["AtaSelfTest", "NvmeSelfTest", "ScsiSelfTest"] diff --git a/src/middlewared/middlewared/api/v25_04_0/system_lifecycle.py b/src/middlewared/middlewared/api/v25_04_0/system_lifecycle.py new file mode 100644 index 000000000000..e85e7c9ee0f1 --- /dev/null +++ b/src/middlewared/middlewared/api/v25_04_0/system_lifecycle.py @@ -0,0 +1,32 @@ +from pydantic import Field + +from middlewared.api.base import BaseModel, NonEmptyString + +__all__ = ["SystemRebootArgs", "SystemRebootResult", + "SystemShutdownArgs", "SystemShutdownResult"] + + +class SystemRebootOptions(BaseModel): + delay: int | None = None + + +class SystemRebootArgs(BaseModel): + reason: NonEmptyString + options: SystemRebootOptions = Field(default=SystemRebootOptions()) + + +class SystemRebootResult(BaseModel): + result: None + + +class SystemShutdownOptions(BaseModel): + delay: int | None = None + + +class SystemShutdownArgs(BaseModel): + reason: NonEmptyString + options: SystemShutdownOptions = Field(default=SystemShutdownOptions()) + + +class SystemShutdownResult(BaseModel): + result: None diff --git a/src/middlewared/middlewared/plugins/config.py b/src/middlewared/middlewared/plugins/config.py index 64b77b5446c1..fcdec26d6411 100644 --- a/src/middlewared/middlewared/plugins/config.py +++ b/src/middlewared/middlewared/plugins/config.py @@ -9,7 +9,7 @@ import tempfile from middlewared.schema import accepts, Bool, Dict, returns -from middlewared.service import CallError, Service, job, private +from middlewared.service import CallError, Service, job, pass_app, private from middlewared.plugins.pwenc import PWENC_FILE_SECRET from middlewared.utils.db import FREENAS_DATABASE @@ -84,7 +84,8 @@ async def save(self, job, options): @accepts() @returns() @job(pipes=["input"]) - def upload(self, job): + @pass_app(rest=True) + def upload(self, app, job): """ Accepts a configuration file via job pipe. """ @@ -105,7 +106,10 @@ def upload(self, job): is_tar = tarfile.is_tarfile(stf.name) self.upload_impl(stf.name, is_tar_file=is_tar) - self.middleware.run_coroutine(self.middleware.call('system.reboot', {'delay': 10}), wait=False) + self.middleware.run_coroutine( + self.middleware.call('system.reboot', 'Configuration upload', {'delay': 10}, app=app), + wait=False, + ) @private def upload_impl(self, file_or_tar, is_tar_file=False): @@ -180,7 +184,7 @@ def upload_impl(self, file_or_tar, is_tar_file=False): 'failover.call_remote', 'core.call_hook', ['config.on_upload', [UPLOADED_DB_PATH]] ) self.middleware.run_coroutine( - self.middleware.call('failover.call_remote', 'system.reboot'), + self.middleware.call('failover.call_remote', 'system.reboot', 'Configuration upload'), wait=False, ) except Exception as e: @@ -193,7 +197,8 @@ def upload_impl(self, file_or_tar, is_tar_file=False): @accepts(Dict('options', Bool('reboot', default=True))) @returns() @job(lock='config_reset', logs=True) - def reset(self, job, options): + @pass_app(rest=True) + def reset(self, app, job, options): """ Reset database to configuration defaults. @@ -224,7 +229,7 @@ def reset(self, job, options): if options['reboot']: self.middleware.run_coroutine( - self.middleware.call('failover.call_remote', 'system.reboot'), + self.middleware.call('failover.call_remote', 'system.reboot', 'Configuration upload'), wait=False, ) except Exception as e: @@ -240,7 +245,8 @@ def reset(self, job, options): if options['reboot']: job.set_progress(95, 'Will reboot in 10 seconds') self.middleware.run_coroutine( - self.middleware.call('system.reboot', {'delay': 10}), wait=False, + self.middleware.call('system.reboot', 'Configuration reset', {'delay': 10}, app=app), + wait=False, ) @private diff --git a/src/middlewared/middlewared/plugins/failover.py b/src/middlewared/middlewared/plugins/failover.py index e876e43f963c..26d83b035da1 100644 --- a/src/middlewared/middlewared/plugins/failover.py +++ b/src/middlewared/middlewared/plugins/failover.py @@ -388,7 +388,7 @@ def sync_to_peer(self, options): ) if options['reboot']: - self.middleware.call_sync('failover.call_remote', 'system.reboot', [{'delay': 2}]) + self.middleware.call_sync('failover.call_remote', 'Failover sync to peer', 'system.reboot', [{'delay': 2}]) @accepts(roles=['FAILOVER_WRITE']) @returns() @@ -858,9 +858,7 @@ def callback(j, controller): rjob.result() self.middleware.call_sync( - 'failover.call_remote', 'system.reboot', - [{'delay': 5}], - {'job': True} + 'failover.call_remote', 'system.reboot', 'System upgrade', [{'delay': 5}], {'job': True}, ) except Exception: raise diff --git a/src/middlewared/middlewared/plugins/service_/services/all.py b/src/middlewared/middlewared/plugins/service_/services/all.py index 1061f46fa7e7..0286d7562185 100644 --- a/src/middlewared/middlewared/plugins/service_/services/all.py +++ b/src/middlewared/middlewared/plugins/service_/services/all.py @@ -36,7 +36,6 @@ RoutingService, SslService, SyslogdService, - SystemService, TimeservicesService, UserService, ) @@ -78,7 +77,6 @@ RoutingService, SslService, SyslogdService, - SystemService, TimeservicesService, TruecommandService, UserService, diff --git a/src/middlewared/middlewared/plugins/service_/services/pseudo/misc.py b/src/middlewared/middlewared/plugins/service_/services/pseudo/misc.py index f0a470cec662..c96fd458fdbb 100644 --- a/src/middlewared/middlewared/plugins/service_/services/pseudo/misc.py +++ b/src/middlewared/middlewared/plugins/service_/services/pseudo/misc.py @@ -172,18 +172,6 @@ class SyslogdService(SimpleService): systemd_unit = "syslog-ng" -class SystemService(PseudoServiceBase): - name = "system" - - restartable = True - - async def stop(self): - self.middleware.create_task(self.middleware.call("system.shutdown", {"delay": 3})) - - async def restart(self): - self.middleware.create_task(self.middleware.call("system.reboot", {"delay": 3})) - - class TimeservicesService(PseudoServiceBase): name = "timeservices" diff --git a/src/middlewared/middlewared/plugins/sysdataset.py b/src/middlewared/middlewared/plugins/sysdataset.py index 4c74ed7b6b15..eb66f383c999 100644 --- a/src/middlewared/middlewared/plugins/sysdataset.py +++ b/src/middlewared/middlewared/plugins/sysdataset.py @@ -286,7 +286,9 @@ async def do_update(self, job, data): if await self.middleware.call('failover.licensed'): if await self.middleware.call('failover.status') == 'MASTER': try: - await self.middleware.call('failover.call_remote', 'system.reboot') + await self.middleware.call( + 'failover.call_remote', 'system.reboot', 'Failover system dataset change', + ) except Exception as e: self.logger.debug('Failed to reboot standby storage controller after system dataset change: %s', e) diff --git a/src/middlewared/middlewared/plugins/system/lifecycle.py b/src/middlewared/middlewared/plugins/system/lifecycle.py index 7f056989570b..5029daae443a 100644 --- a/src/middlewared/middlewared/plugins/system/lifecycle.py +++ b/src/middlewared/middlewared/plugins/system/lifecycle.py @@ -1,8 +1,10 @@ import asyncio -from middlewared.schema import accepts, Bool, Dict, Int, returns, Str +from middlewared.api import api_method +from middlewared.api.current import SystemRebootArgs, SystemRebootResult, SystemShutdownArgs, SystemShutdownResult +from middlewared.schema import accepts, Bool, returns, Str from middlewared.service import job, private, Service, no_auth_required, pass_app -from middlewared.utils import Popen, run +from middlewared.utils import run from .utils import lifecycle_conf, RE_KDUMP_CONFIGURED @@ -19,7 +21,7 @@ async def first_boot(self): @pass_app() async def boot_id(self, app): """ - Returns an unique boot identifier. + Returns a unique boot identifier. It is supposed to be unique every system boot. """ @@ -53,41 +55,39 @@ async def state(self): return 'READY' return 'BOOTING' - @accepts(Dict('system-reboot', Int('delay', required=False), required=False)) - @returns() + @api_method(SystemRebootArgs, SystemRebootResult) @job() - async def reboot(self, job, options): + @pass_app(rest=True) + async def reboot(self, app, job, reason, options): """ Reboots the operating system. Emits an "added" event of name "system" and id "reboot". """ - if options is None: - options = {} + await self.middleware.log_audit_message(app, 'REBOOT', {'reason': reason}, True) - self.middleware.send_event('system.reboot', 'ADDED') + self.middleware.send_event('system.reboot', 'ADDED', fields={'reason': reason}) - delay = options.get('delay') - if delay: - await asyncio.sleep(delay) + if options['delay'] is not None: + await asyncio.sleep(options['delay']) await run(['/sbin/shutdown', '-r', 'now']) - @accepts(Dict('system-shutdown', Int('delay', required=False), required=False)) - @returns() + @api_method(SystemShutdownArgs, SystemShutdownResult) @job() - async def shutdown(self, job, options): + @pass_app(rest=True) + async def shutdown(self, app, job, reason, options): """ Shuts down the operating system. An "added" event of name "system" and id "shutdown" is emitted when shutdown is initiated. """ - if options is None: - options = {} + await self.middleware.log_audit_message(app, 'SHUTDOWN', {'reason': reason}, True) - delay = options.get('delay') - if delay: - await asyncio.sleep(delay) + self.middleware.send_event('system.shutdown', 'ADDED', fields={'reason': reason}) + + if options['delay'] is not None: + await asyncio.sleep(options['delay']) await run(['/sbin/poweroff']) diff --git a/src/middlewared/middlewared/plugins/update.py b/src/middlewared/middlewared/plugins/update.py index 5dd46df2a0cc..bae664834c60 100644 --- a/src/middlewared/middlewared/plugins/update.py +++ b/src/middlewared/middlewared/plugins/update.py @@ -1,5 +1,5 @@ from middlewared.schema import accepts, Bool, Dict, Str -from middlewared.service import job, private, CallError, Service +from middlewared.service import job, private, CallError, Service, pass_app import middlewared.sqlalchemy as sa from middlewared.plugins.update_.utils import UPLOAD_LOCATION from middlewared.utils import PRODUCT @@ -256,7 +256,8 @@ async def get_pending(self, path): Bool('reboot', default=False), )) @job(lock='update') - async def update(self, job, attrs): + @pass_app(rest=True) + async def update(self, app, job, attrs): """ Downloads (if not already in cache) and apply an update. @@ -288,7 +289,7 @@ async def update(self, job, attrs): await self.middleware.call_hook('update.post_update') if attrs['reboot']: - await self.middleware.call('system.reboot', {'delay': 10}) + await self.middleware.call('system.reboot', 'System upgrade', {'delay': 10}, app=app) return True diff --git a/src/middlewared/middlewared/plugins/update_/freebsd_to_scale_linux.py b/src/middlewared/middlewared/plugins/update_/freebsd_to_scale_linux.py deleted file mode 100644 index 20cf08d09f2f..000000000000 --- a/src/middlewared/middlewared/plugins/update_/freebsd_to_scale_linux.py +++ /dev/null @@ -1,42 +0,0 @@ -import logging -from pathlib import Path - -from middlewared.service import job, private, Service -from middlewared.utils import run - -logger = logging.getLogger(__name__) - - -class UpdateService(Service): - - @private - def remove_files(self): - for i in ("/data/freebsd-to-scale-update", "/var/lib/dbus/machine-id", "/etc/machine-id"): - try: - Path(i).unlink(missing_ok=True) - except Exception: - logger.error('Failed removing %r', i, exc_info=True) - - @private - @job() - async def freebsd_to_scale(self, job): - logger.info("Updating CORE installation to SCALE") - - await self.middleware.run_in_thread(self.remove_files) - await run(["systemd-machine-id-setup"], check=False) - await self.middleware.call("etc.generate", "fstab", "initial") - await run(["mount", "-a"]) - - config = await self.middleware.call("system.advanced.config") - if config["serialconsole"]: - cp = await run(["systemctl", "enable", f"serial-getty@{config['serialport']}.service"], check=False) - if cp.returncode: - self.logger.error( - "Failed to enable %r serial port service: %r", config["serialport"], cp.stderr.decode() - ) - - await self.middleware.call("etc.generate", "rc") - await self.middleware.call("boot.update_initramfs") - await self.middleware.call("etc.generate", "grub") - - await self.middleware.call("system.reboot") diff --git a/tests/api2/test_261_iscsi_cmd.py b/tests/api2/test_261_iscsi_cmd.py index 160a362d966e..272ab625519a 100644 --- a/tests/api2/test_261_iscsi_cmd.py +++ b/tests/api2/test_261_iscsi_cmd.py @@ -1782,7 +1782,7 @@ def _ha_reboot_master(delay=900): orig_master_node = _get_node() new_master_node = other_node(orig_master_node) - call('system.reboot') + call('system.reboot', 'iSCSI test') # First we'll loop until the node is no longer the orig_node new_master = False diff --git a/tests/api2/test_system_lifetime.py b/tests/api2/test_system_lifetime.py new file mode 100644 index 000000000000..8161abf4a33a --- /dev/null +++ b/tests/api2/test_system_lifetime.py @@ -0,0 +1,30 @@ +import time + +from middlewared.test.integration.utils import call + + +def test_system_reboot(): + boot_id = call("system.boot_id") + + call("system.reboot", "Integration test") + + for i in range(180): + try: + new_boot_id = call("system.boot_id") + except Exception: + pass + else: + if new_boot_id == boot_id: + break + + time.sleep(1) + else: + assert False, "System did not reboot" + + audit = call("audit.query", { + "services": ["MIDDLEWARE"], + "query-filters": [ + ["event", "=", "REBOOT"], + ], + }) + assert audit[-1]["event_data"] == {"reason": "Integration test"} From 220137372cde24dff44d820c39084efb0f3dc632 Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 14:43:49 +0200 Subject: [PATCH 2/8] Use constants for repeated reboot reasons --- src/middlewared/middlewared/plugins/config.py | 10 +++++++--- src/middlewared/middlewared/plugins/failover.py | 3 ++- src/middlewared/middlewared/plugins/update.py | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/middlewared/middlewared/plugins/config.py b/src/middlewared/middlewared/plugins/config.py index fcdec26d6411..120311e642f1 100644 --- a/src/middlewared/middlewared/plugins/config.py +++ b/src/middlewared/middlewared/plugins/config.py @@ -26,6 +26,8 @@ TRUENAS_ADMIN_KEYS_UPLOADED = '/data/truenas_admin_authorized_keys_uploaded' ROOT_KEYS_UPLOADED = '/data/root_authorized_keys_uploaded' DATABASE_NAME = os.path.basename(FREENAS_DATABASE) +CONFIGURATION_UPLOAD_REBOOT_REASON = 'Configuration upload' +CONFIGURATION_RESET_REBOOT_REASON = 'Configuration reset' class ConfigService(Service): @@ -184,7 +186,7 @@ def upload_impl(self, file_or_tar, is_tar_file=False): 'failover.call_remote', 'core.call_hook', ['config.on_upload', [UPLOADED_DB_PATH]] ) self.middleware.run_coroutine( - self.middleware.call('failover.call_remote', 'system.reboot', 'Configuration upload'), + self.middleware.call('failover.call_remote', 'system.reboot', CONFIGURATION_UPLOAD_REBOOT_REASON), wait=False, ) except Exception as e: @@ -229,7 +231,9 @@ def reset(self, app, job, options): if options['reboot']: self.middleware.run_coroutine( - self.middleware.call('failover.call_remote', 'system.reboot', 'Configuration upload'), + self.middleware.call( + 'failover.call_remote', 'system.reboot', CONFIGURATION_UPLOAD_REBOOT_REASON, + ), wait=False, ) except Exception as e: @@ -245,7 +249,7 @@ def reset(self, app, job, options): if options['reboot']: job.set_progress(95, 'Will reboot in 10 seconds') self.middleware.run_coroutine( - self.middleware.call('system.reboot', 'Configuration reset', {'delay': 10}, app=app), + self.middleware.call('system.reboot', CONFIGURATION_RESET_REBOOT_REASON, {'delay': 10}, app=app), wait=False, ) diff --git a/src/middlewared/middlewared/plugins/failover.py b/src/middlewared/middlewared/plugins/failover.py index 26d83b035da1..dd23ed442966 100644 --- a/src/middlewared/middlewared/plugins/failover.py +++ b/src/middlewared/middlewared/plugins/failover.py @@ -23,6 +23,7 @@ from middlewared.plugins.failover_.configure import HA_LICENSE_CACHE_KEY from middlewared.plugins.failover_.remote import NETWORK_ERRORS from middlewared.plugins.system.reboot import RebootReason +from middlewared.plugins.update import SYSTEM_UPGRADE_REBOOT_REASON from middlewared.plugins.update_.install import STARTING_INSTALLER from middlewared.plugins.update_.utils import DOWNLOAD_UPDATE_FILE from middlewared.plugins.update_.utils_linux import mount_update @@ -858,7 +859,7 @@ def callback(j, controller): rjob.result() self.middleware.call_sync( - 'failover.call_remote', 'system.reboot', 'System upgrade', [{'delay': 5}], {'job': True}, + 'failover.call_remote', 'system.reboot', SYSTEM_UPGRADE_REBOOT_REASON, [{'delay': 5}], {'job': True}, ) except Exception: raise diff --git a/src/middlewared/middlewared/plugins/update.py b/src/middlewared/middlewared/plugins/update.py index bae664834c60..f1da6deab7d5 100644 --- a/src/middlewared/middlewared/plugins/update.py +++ b/src/middlewared/middlewared/plugins/update.py @@ -13,6 +13,8 @@ import textwrap import pathlib +SYSTEM_UPGRADE_REBOOT_REASON = 'System upgrade' + def parse_train_name(name): split = (name + '-').split('-') From 19652ac370c49c924a4655fc61d7d141b5ffe076 Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 14:44:54 +0200 Subject: [PATCH 3/8] Fix typo --- src/middlewared/middlewared/plugins/failover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewared/middlewared/plugins/failover.py b/src/middlewared/middlewared/plugins/failover.py index dd23ed442966..6cb21a82ed68 100644 --- a/src/middlewared/middlewared/plugins/failover.py +++ b/src/middlewared/middlewared/plugins/failover.py @@ -389,7 +389,7 @@ def sync_to_peer(self, options): ) if options['reboot']: - self.middleware.call_sync('failover.call_remote', 'Failover sync to peer', 'system.reboot', [{'delay': 2}]) + self.middleware.call_sync('failover.call_remote', 'system.reboot', 'Failover sync to peer', [{'delay': 2}]) @accepts(roles=['FAILOVER_WRITE']) @returns() From 000bacf660779299ef4d3264975a2a78ef2e762f Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 14:52:31 +0200 Subject: [PATCH 4/8] Update src/middlewared/middlewared/plugins/config.py Co-authored-by: Caleb St. John <30729806+yocalebo@users.noreply.github.com> --- src/middlewared/middlewared/plugins/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewared/middlewared/plugins/config.py b/src/middlewared/middlewared/plugins/config.py index 120311e642f1..f647357dbf6d 100644 --- a/src/middlewared/middlewared/plugins/config.py +++ b/src/middlewared/middlewared/plugins/config.py @@ -109,7 +109,7 @@ def upload(self, app, job): self.upload_impl(stf.name, is_tar_file=is_tar) self.middleware.run_coroutine( - self.middleware.call('system.reboot', 'Configuration upload', {'delay': 10}, app=app), + self.middleware.call('system.reboot', CONFIGURATION_UPLOAD_REBOOT_REASON, {'delay': 10}, app=app), wait=False, ) From d64fc8154984114657b4d6ba32c131107a091670 Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 14:56:23 +0200 Subject: [PATCH 5/8] Fix test --- tests/api2/test_system_lifetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/api2/test_system_lifetime.py b/tests/api2/test_system_lifetime.py index 8161abf4a33a..8eaf13017fbc 100644 --- a/tests/api2/test_system_lifetime.py +++ b/tests/api2/test_system_lifetime.py @@ -14,7 +14,7 @@ def test_system_reboot(): except Exception: pass else: - if new_boot_id == boot_id: + if new_boot_id != boot_id: break time.sleep(1) From 92d528551d2bd38cb34820915166f2d9ea732a3a Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 15:07:31 +0200 Subject: [PATCH 6/8] Update src/middlewared/middlewared/plugins/update.py Co-authored-by: Caleb St. John <30729806+yocalebo@users.noreply.github.com> --- src/middlewared/middlewared/plugins/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewared/middlewared/plugins/update.py b/src/middlewared/middlewared/plugins/update.py index f1da6deab7d5..6a2a22117ae0 100644 --- a/src/middlewared/middlewared/plugins/update.py +++ b/src/middlewared/middlewared/plugins/update.py @@ -291,7 +291,7 @@ async def update(self, app, job, attrs): await self.middleware.call_hook('update.post_update') if attrs['reboot']: - await self.middleware.call('system.reboot', 'System upgrade', {'delay': 10}, app=app) + await self.middleware.call('system.reboot', SYSTEM_UPGRADE_REBOOT_REASON, {'delay': 10}, app=app) return True From 8ba58bb4a632d0da72409c184cb14b27799bb8ba Mon Sep 17 00:00:00 2001 From: themylogin Date: Fri, 20 Sep 2024 17:35:50 +0200 Subject: [PATCH 7/8] Fix tests --- tests/api2/test_system_lifetime.py | 49 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/tests/api2/test_system_lifetime.py b/tests/api2/test_system_lifetime.py index 8eaf13017fbc..ec8e47f32558 100644 --- a/tests/api2/test_system_lifetime.py +++ b/tests/api2/test_system_lifetime.py @@ -2,29 +2,34 @@ from middlewared.test.integration.utils import call +from auto_config import ha -def test_system_reboot(): - boot_id = call("system.boot_id") - call("system.reboot", "Integration test") +if not ha: + # This cannot be tested on a HA system since rebooting this node will just fail over to another node - for i in range(180): - try: - new_boot_id = call("system.boot_id") - except Exception: - pass + def test_system_reboot(): + boot_id = call("system.boot_id") + + call("system.reboot", "Integration test") + + for i in range(180): + try: + new_boot_id = call("system.boot_id") + except Exception: + pass + else: + if new_boot_id != boot_id: + break + + time.sleep(1) else: - if new_boot_id != boot_id: - break - - time.sleep(1) - else: - assert False, "System did not reboot" - - audit = call("audit.query", { - "services": ["MIDDLEWARE"], - "query-filters": [ - ["event", "=", "REBOOT"], - ], - }) - assert audit[-1]["event_data"] == {"reason": "Integration test"} + assert False, "System did not reboot" + + audit = call("audit.query", { + "services": ["MIDDLEWARE"], + "query-filters": [ + ["event", "=", "REBOOT"], + ], + }) + assert audit[-1]["event_data"] == {"reason": "Integration test"} From 72d819a9cd91fab139c2392973df59d19023f95d Mon Sep 17 00:00:00 2001 From: themylogin Date: Tue, 24 Sep 2024 13:47:36 +0200 Subject: [PATCH 8/8] Address review --- tests/api2/test_system_lifetime.py | 53 ++++++++++++++++-------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tests/api2/test_system_lifetime.py b/tests/api2/test_system_lifetime.py index ec8e47f32558..a1bd28227943 100644 --- a/tests/api2/test_system_lifetime.py +++ b/tests/api2/test_system_lifetime.py @@ -1,35 +1,38 @@ import time +import pytest + from middlewared.test.integration.utils import call from auto_config import ha -if not ha: - # This cannot be tested on a HA system since rebooting this node will just fail over to another node - - def test_system_reboot(): - boot_id = call("system.boot_id") - - call("system.reboot", "Integration test") +@pytest.mark.skipif( + ha, + reason="Cannot be tested on a HA system since rebooting this node will just fail over to another node", +) +def test_system_reboot(): + boot_id = call("system.boot_id") - for i in range(180): - try: - new_boot_id = call("system.boot_id") - except Exception: - pass - else: - if new_boot_id != boot_id: - break + call("system.reboot", "Integration test") - time.sleep(1) + for i in range(180): + try: + new_boot_id = call("system.boot_id") + except Exception: + pass else: - assert False, "System did not reboot" - - audit = call("audit.query", { - "services": ["MIDDLEWARE"], - "query-filters": [ - ["event", "=", "REBOOT"], - ], - }) - assert audit[-1]["event_data"] == {"reason": "Integration test"} + if new_boot_id != boot_id: + break + + time.sleep(1) + else: + assert False, "System did not reboot" + + audit = call("audit.query", { + "services": ["MIDDLEWARE"], + "query-filters": [ + ["event", "=", "REBOOT"], + ], + }) + assert audit[-1]["event_data"] == {"reason": "Integration test"}