summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmorphlib/exts/ssh-rsync.write179
1 files changed, 102 insertions, 77 deletions
diff --git a/morphlib/exts/ssh-rsync.write b/morphlib/exts/ssh-rsync.write
index 775619ec..d26db392 100755
--- a/morphlib/exts/ssh-rsync.write
+++ b/morphlib/exts/ssh-rsync.write
@@ -18,6 +18,7 @@
'''A Morph deployment write extension for upgrading systems over ssh.'''
+import contextlib
import cliapp
import os
import sys
@@ -45,107 +46,131 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension):
'''
- def process_args(self, args):
- if len(args) != 2:
- raise cliapp.AppException('Wrong number of command line args')
-
- temp_root, location = args
-
- self.upgrade_remote_system(location, temp_root)
-
- def upgrade_remote_system(self, location, temp_root):
- root_disk = self.find_root_disk(location)
- uuid = cliapp.ssh_runcmd(location, ['blkid', '-s', 'UUID', '-o',
- 'value', root_disk]).strip()
-
- self.complete_fstab_for_btrfs_layout(temp_root, uuid)
+ def find_root_disk(self, location):
+ '''Read /proc/mounts on location to find which device contains "/"'''
- version_label = os.environ['VERSION_LABEL']
- autostart = self.get_environment_boolean('AUTOSTART')
+ self.status(msg='Finding device that contains "/"')
+ contents = cliapp.ssh_runcmd(location, ['cat', '/proc/mounts'])
+ for line in contents.splitlines():
+ line_words = line.split()
+ if (line_words[1] == '/' and line_words[0] != 'rootfs'):
+ return line_words[0]
+ @contextlib.contextmanager
+ def _remote_mount_point(self, location):
self.status(msg='Creating remote mount point')
remote_mnt = cliapp.ssh_runcmd(location, ['mktemp', '-d']).strip()
try:
- self.status(msg='Mounting root disk')
- cliapp.ssh_runcmd(location, ['mount', root_disk, remote_mnt])
- except Exception as e:
- ssh_runcmd_ignore_failure(location, ['rmdir', remote_mnt])
- raise e
+ yield remote_mnt
+ finally:
+ self.status(msg='Removing remote mount point')
+ cliapp.ssh_runcmd(location, ['rmdir', remote_mnt])
+ @contextlib.contextmanager
+ def _remote_mount(self, location, root_disk, mountpoint):
+ self.status(msg='Mounting root disk')
+ cliapp.ssh_runcmd(location, ['mount', root_disk, mountpoint])
try:
- version_root = os.path.join(remote_mnt, 'systems', version_label)
- orig_dir = os.path.join(version_root, 'orig')
-
- self.status(msg='Creating %s' % version_root)
- cliapp.ssh_runcmd(location, ['mkdir', version_root])
-
- self.create_remote_orig(location, version_root, remote_mnt,
- temp_root)
-
- # Use the system-version-manager from the new system we just
- # installed, so that we can upgrade from systems that don't have
- # it installed.
- self.status(msg='Calling system-version-manager to deploy upgrade')
- deployment = os.path.join('/systems', version_label, 'orig')
- system_config_sync = os.path.join(
- remote_mnt, 'systems', version_label, 'orig', 'usr', 'bin',
- 'baserock-system-config-sync')
- system_version_manager = os.path.join(
- remote_mnt, 'systems', version_label, 'orig', 'usr', 'bin',
- 'system-version-manager')
- cliapp.ssh_runcmd(location,
- ['env', 'BASEROCK_SYSTEM_CONFIG_SYNC='+system_config_sync,
- system_version_manager, 'deploy', deployment])
-
- self.status(msg='Setting %s as the new default system' %
- version_label)
- cliapp.ssh_runcmd(location,
- [system_version_manager, 'set-default', version_label])
- except Exception as e:
- self.status(msg='Deployment failed')
- ssh_runcmd_ignore_failure(
- location, ['btrfs', 'subvolume', 'delete', orig_dir])
- ssh_runcmd_ignore_failure(
- location, ['rm', '-rf', version_root])
- raise e
+ yield
finally:
- self.status(msg='Removing temporary mounts')
- cliapp.ssh_runcmd(location, ['umount', remote_mnt])
- cliapp.ssh_runcmd(location, ['rmdir', remote_mnt])
+ self.status(msg='Unmounting root disk')
+ cliapp.ssh_runcmd(location, ['umount', mountpoint])
+
+ @contextlib.contextmanager
+ def _created_version_root(self, location, remote_mnt, version_label):
+ version_root = os.path.join(remote_mnt, 'systems', version_label)
+ self.status(msg='Creating %(root)s', root=version_root)
+ cliapp.ssh_runcmd(location, ['mkdir', version_root])
+ try:
+ yield version_root
+ except BaseException as e:
+ # catch all, we always want to clean up
+ self.status(msg='Cleaning up %(root)s', root=version_root)
+ ssh_runcmd_ignore_failure(location, ['rmdir', version_root])
- if autostart:
- self.status(msg="Rebooting into new system ...")
- ssh_runcmd_ignore_failure(location, ['reboot'])
+ def get_old_orig(self, location, remote_mnt):
+ '''Identify which subvolume to snapshot from'''
- def create_remote_orig(self, location, version_root, remote_mnt,
- temp_root):
- '''Create the subvolume version_root/orig on location'''
+ # rawdisk upgrades use 'factory'
+ return os.path.join(remote_mnt, 'systems', 'factory', 'orig')
+ @contextlib.contextmanager
+ def _created_orig_subvolume(self, location, remote_mnt, version_root):
self.status(msg='Creating "orig" subvolume')
old_orig = self.get_old_orig(location, remote_mnt)
new_orig = os.path.join(version_root, 'orig')
cliapp.ssh_runcmd(location, ['btrfs', 'subvolume', 'snapshot',
old_orig, new_orig])
+ try:
+ yield new_orig
+ except BaseException as e:
+ ssh_runcmd_ignore_failure(
+ location, ['btrfs', 'subvolume', 'delete', new_orig])
+ def populate_remote_orig(self, location, new_orig, temp_root):
+ '''Populate the subvolume version_root/orig on location'''
+
+ self.status(msg='Populating "orig" subvolume')
cliapp.runcmd(['rsync', '-as', '--checksum', '--numeric-ids',
'--delete', temp_root + os.path.sep,
'%s:%s' % (location, new_orig)])
- def get_old_orig(self, location, remote_mnt):
- '''Identify which subvolume to snapshot from'''
+ @contextlib.contextmanager
+ def _deployed_version(self, location, version_label,
+ system_config_sync, system_version_manager):
+ self.status(msg='Calling system-version-manager to deploy upgrade')
+ deployment = os.path.join('/systems', version_label, 'orig')
+ cliapp.ssh_runcmd(location,
+ ['env', 'BASEROCK_SYSTEM_CONFIG_SYNC='+system_config_sync,
+ system_version_manager, 'deploy', deployment])
+ try:
+ yield deployment
+ except BaseException as e:
+ self.status(msg='Cleaning up failed version installation')
+ cliapp.ssh_runcmd(location,
+ [system_version_manager, 'remove', version_label])
- # rawdisk upgrades use 'factory'
- return os.path.join(remote_mnt, 'systems', 'factory', 'orig')
+ def upgrade_remote_system(self, location, temp_root):
+ root_disk = self.find_root_disk(location)
+ uuid = cliapp.ssh_runcmd(location, ['blkid', '-s', 'UUID', '-o',
+ 'value', root_disk]).strip()
- def find_root_disk(self, location):
- '''Read /proc/mounts on location to find which device contains "/"'''
+ self.complete_fstab_for_btrfs_layout(temp_root, uuid)
- self.status(msg='Finding device that contains "/"')
- contents = cliapp.ssh_runcmd(location, ['cat', '/proc/mounts'])
- for line in contents.splitlines():
- line_words = line.split()
- if (line_words[1] == '/' and line_words[0] != 'rootfs'):
- return line_words[0]
+ version_label = os.environ['VERSION_LABEL']
+ autostart = self.get_environment_boolean('AUTOSTART')
+
+ with self._remote_mount_point(location) as remote_mnt, \
+ self._remote_mount(location, root_disk, remote_mnt), \
+ self._created_version_root(location, remote_mnt,
+ version_label) as version_root, \
+ self._created_orig_subvolume(location, remote_mnt,
+ version_root) as orig:
+ self.populate_remote_orig(location, orig, temp_root)
+ system_config_sync = os.path.join(
+ remote_mnt, 'systems', version_label, 'orig',
+ 'usr', 'bin', 'baserock-system-config-sync')
+ system_version_manager = os.path.join(
+ remote_mnt, 'systems', version_label, 'orig',
+ 'usr', 'bin', 'system-version-manager')
+ with self._deployed_version(location, version_label,
+ system_config_sync, system_version_manager):
+ self.status(msg='Setting %(v)s as the new default system',
+ v=version_label)
+ cliapp.ssh_runcmd(location, [system_version_manager,
+ 'set-default', version_label])
+
+ if autostart:
+ self.status(msg="Rebooting into new system ...")
+ ssh_runcmd_ignore_failure(location, ['reboot'])
+
+ def process_args(self, args):
+ if len(args) != 2:
+ raise cliapp.AppException('Wrong number of command line args')
+
+ temp_root, location = args
+
+ self.upgrade_remote_system(location, temp_root)
SshRsyncWriteExtension().run()