diff --git a/src/middlewared/middlewared/main.py b/src/middlewared/middlewared/main.py index 1912e0e18d17..95f27ca6db8a 100644 --- a/src/middlewared/middlewared/main.py +++ b/src/middlewared/middlewared/main.py @@ -1262,7 +1262,7 @@ def _loop_monitor_thread(self): if last == current: frame = sys._current_frames()[self.__thread_id] stack = traceback.format_stack(frame, limit=-10) - self.logger.warn(''.join(['Task seems blocked:'] + stack)) + self.logger.warn(''.join(['Task seems blocked:\n'] + stack)) last = current def run(self): diff --git a/src/middlewared/middlewared/plugins/update.py b/src/middlewared/middlewared/plugins/update.py index 24498d7a4c73..0f82fe31538d 100644 --- a/src/middlewared/middlewared/plugins/update.py +++ b/src/middlewared/middlewared/plugins/update.py @@ -2,6 +2,7 @@ from middlewared.schema import accepts, Bool, Dict, Str from middlewared.service import job, private, CallError, Service +from datetime import datetime import enum import errno import os @@ -72,11 +73,12 @@ def compare_trains(t1, t2): ): return CompareTrainsResult.MINOR_UPGRADE - if ( - isinstance(v1[1], int) and isinstance(v2[1], int) and v1[1] > v2[1] or - not isinstance(v2[1], int) and v1[1] > 0 - ): - return CompareTrainsResult.MINOR_DOWNGRADE + if isinstance(v1[1], int): + if ( + isinstance(v2[1], int) and v1[1] > v2[1] or + not isinstance(v2[1], int) and v1[1] > 0 + ): + return CompareTrainsResult.MINOR_DOWNGRADE class CheckUpdateHandler(object): @@ -456,6 +458,15 @@ async def update(self, job, attrs=None): ) await self.middleware.call('cache.put', 'update.applied', True) + if ( + await self.middleware.call_sync('system.is_freenas') or + ( + await self.middleware.call('failover.licensed') and + await self.middleware.call('failover.status') != 'BACKUP' + ) + ): + await self.middleware.call('update.take_systemdataset_samba4_snapshot') + if attrs.get('reboot'): await self.middleware.call('system.reboot', {'delay': 10}) return True @@ -645,3 +656,27 @@ def destroy_upload_location(self): ) if cp.returncode != 0: raise CallError(f'Could not destroy memory device: {cp.stderr}') + + @private + def take_systemdataset_samba4_snapshot(self): + basename = self.middleware.call_sync('systemdataset.config')['basename'] + if basename is None: + self.logger.warning('System dataset is not available, not taking snapshot') + return + + dataset = f'{basename}/samba4' + + proc = subprocess.run(['zfs', 'list', '-t', 'snapshot', '-H', '-o', 'name', '-s', 'name', '-d', '1', dataset], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', errors='ignore') + if proc.returncode != 0: + self.logger.warning('Unable to list dataset %s snapshots: %s', dataset, proc.stderr) + return + + snapshots = [s.split('@')[1] for s in proc.stdout.strip().split()] + for snapshot in [s for s in snapshots if s.startswith('update--')][:-4]: + self.logger.info('Deleting dataset %s snapshot %s', dataset, snapshot) + subprocess.run(['zfs', 'destroy', f'{dataset}@{snapshot}']) + + current_version = "-".join(self.middleware.call_sync("system.info")["version"].split("-")[1:]) + snapshot = f'update--{datetime.utcnow().strftime("%Y-%m-%d-%H-%M")}--{current_version}' + subprocess.run(['zfs', 'snapshot', f'{dataset}@{snapshot}']) diff --git a/src/middlewared/middlewared/pytest/unit/plugins/test_update.py b/src/middlewared/middlewared/pytest/unit/plugins/test_update.py index 161ed5e7ccd6..95c7b3635dc0 100644 --- a/src/middlewared/middlewared/pytest/unit/plugins/test_update.py +++ b/src/middlewared/middlewared/pytest/unit/plugins/test_update.py @@ -20,6 +20,7 @@ ("FreeNAS-11.2-STABLE", "FreeNAS-11-STABLE", CompareTrainsResult.MINOR_DOWNGRADE), ("FreeNAS-9.10-STABLE", "FreeNAS-9.10-STABLE", None), + ("FreeNAS-11-Nightlies", "FreeNAS-11-Nightlies", None), ]) def test__compare_trains(t1, t2, result): assert compare_trains(t1, t2) == result