summaryrefslogtreecommitdiff
path: root/ssh-rsync.write
diff options
context:
space:
mode:
authorTiago Gomes <tiago.gomes@codethink.co.uk>2013-06-10 16:17:33 +0100
committerTiago Gomes <tiago.gomes@codethink.co.uk>2013-06-10 16:17:33 +0100
commit5f04dbce83423475a9327fa72142a3e20aaea095 (patch)
tree6c3725830f8c5a2aacf7e1ea1b2b125637bcd998 /ssh-rsync.write
parent21c5c335597e802f9a2f53f1a11d08514e69c506 (diff)
downloaddefinitions-5f04dbce83423475a9327fa72142a3e20aaea095.tar.gz
Revert "Remove deployment extensions that are in morph"a
This reverts commit 21c5c335597e802f9a2f53f1a11d08514e69c506. We still need these extensions to be able to work with the latests changes in the morphs repository, using baserock-8 version of morph.
Diffstat (limited to 'ssh-rsync.write')
-rwxr-xr-xssh-rsync.write181
1 files changed, 181 insertions, 0 deletions
diff --git a/ssh-rsync.write b/ssh-rsync.write
new file mode 100755
index 00000000..6fe1153d
--- /dev/null
+++ b/ssh-rsync.write
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+# Copyright (C) 2013 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+'''A Morph deployment write extension for upgrading systems over ssh.'''
+
+
+import cliapp
+import os
+import sys
+import time
+import tempfile
+
+import morphlib.writeexts
+
+class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension):
+
+ '''Upgrade a running baserock system with ssh and rsync.
+
+ It assumes the system is baserock-based and has a btrfs partition.
+
+ The location command line argument is the 'user@hostname' string
+ that will be passed to ssh and rsync
+
+ '''
+
+ def process_args(self, args):
+ if len(args) != 2:
+ raise cliapp.AppException('Wrong number of command line args')
+
+ temp_root, location = args
+
+ self.check_valid_target(location)
+ self.upgrade_remote_system(location, temp_root)
+
+ def upgrade_remote_system(self, location, temp_root):
+ root_disk = self.find_root_disk(location)
+ version_label = os.environ.get('VERSION_LABEL')
+
+ try:
+ self.status(msg='Creating remote mount point')
+ remote_mnt = cliapp.ssh_runcmd(location, ['mktemp', '-d']).strip()
+
+ self.status(msg='Mounting root disk')
+ cliapp.ssh_runcmd(location, ['mount', root_disk, remote_mnt])
+
+ version_root = os.path.join(remote_mnt, 'systems', version_label)
+ run_dir = os.path.join(version_root, 'run')
+ orig_dir = os.path.join(version_root, 'orig')
+ try:
+ 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)
+
+ self.status(msg='Creating "run" subvolume')
+ cliapp.ssh_runcmd(location, ['btrfs', 'subvolume',
+ 'snapshot', orig_dir, run_dir])
+
+ self.install_remote_kernel(location, version_root, temp_root)
+ except Exception as e:
+ try:
+ cliapp.ssh_runcmd(location,
+ ['btrfs', 'subvolume', 'delete', run_dir])
+ cliapp.ssh_runcmd(location,
+ ['btrfs', 'subvolume', 'delete', orig_dir])
+ cliapp.ssh_runcmd(location, ['rm', '-rf', version_root])
+ except:
+ pass
+ raise e
+
+ if self.bootloader_is_wanted():
+ self.update_remote_extlinux(location, remote_mnt,
+ version_label)
+ except:
+ raise
+ else:
+ self.status(msg='Removing temporary mounts')
+ cliapp.ssh_runcmd(location, ['umount', root_disk])
+ cliapp.ssh_runcmd(location, ['rmdir', remote_mnt])
+
+ def update_remote_extlinux(self, location, remote_mnt, version_label):
+ '''Install/reconfigure extlinux on location'''
+
+ self.status(msg='Creating extlinux.conf')
+ config = os.path.join(remote_mnt, 'extlinux.conf')
+ temp_file = tempfile.mkstemp()[1]
+ with open(temp_file, 'w') as f:
+ f.write('default linux\n')
+ f.write('timeout 1\n')
+ f.write('label linux\n')
+ f.write('kernel /systems/' + version_label + '/kernel\n')
+ f.write('append root=/dev/sda '
+ 'rootflags=subvol=systems/' + version_label + '/run '
+ 'init=/sbin/init rw\n')
+
+ cliapp.ssh_runcmd(location, ['mv', config, config+'~'])
+
+ try:
+ cliapp.runcmd(['rsync', '-a', temp_file,
+ '%s:%s' % (location, config)])
+ except Exception as e:
+ try:
+ cliapp.ssh_runcmd(location, ['mv', config+'~', config])
+ except:
+ pass
+ raise e
+
+ def create_remote_orig(self, location, version_root, remote_mnt,
+ temp_root):
+ '''Create the subvolume version_root/orig on location'''
+
+ 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])
+
+ cliapp.runcmd(['rsync', '-a', '--checksum', '--numeric-ids',
+ '--delete', temp_root, '%s:%s' % (location, new_orig)])
+
+ def get_old_orig(self, location, remote_mnt):
+ '''Identify which subvolume to snapshot from'''
+
+ # rawdisk upgrades use 'factory'
+ return os.path.join(remote_mnt, 'systems', 'factory', 'orig')
+
+ def find_root_disk(self, location):
+ '''Read /proc/mounts on location to find which device contains "/"'''
+
+ 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]
+
+ def install_remote_kernel(self, location, version_root, temp_root):
+ '''Install the kernel in temp_root inside version_root on location'''
+
+ self.status(msg='Installing kernel')
+ image_names = ['vmlinuz', 'zImage', 'uImage']
+ kernel_dest = os.path.join(version_root, 'kernel')
+ for name in image_names:
+ try_path = os.path.join(temp_root, 'boot', name)
+ if os.path.exists(try_path):
+ cliapp.runcmd(['rsync', '-a', try_path,
+ '%s:%s' % (location, kernel_dest)])
+
+ def check_valid_target(self, location):
+ try:
+ cliapp.ssh_runcmd(location, ['true'])
+ except Exception as e:
+ raise cliapp.AppException('%s does not respond to ssh:\n%s'
+ % (location, e))
+
+ try:
+ cliapp.ssh_runcmd(location, ['test', '-d', '/baserock'])
+ except:
+ raise cliapp.AppException('%s is not a baserock system' % location)
+
+ try:
+ cliapp.ssh_runcmd(location, ['which', 'rsync'])
+ except:
+ raise cliapp.AppException('%s does not have rsync')
+
+SshRsyncWriteExtension().run()