summaryrefslogtreecommitdiff
path: root/extensions/nfsboot.write
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/nfsboot.write')
-rwxr-xr-xextensions/nfsboot.write202
1 files changed, 202 insertions, 0 deletions
diff --git a/extensions/nfsboot.write b/extensions/nfsboot.write
new file mode 100755
index 00000000..d928775e
--- /dev/null
+++ b/extensions/nfsboot.write
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+# Copyright (C) 2013-2015 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, see <http://www.gnu.org/licenses/>.
+
+
+'''A Morph deployment write extension for deploying to an nfsboot server
+
+*** DO NOT USE ***
+- This was written before 'proper' deployment mechanisms were in place
+It is unlikely to work at all and will not work correctly
+
+Use the pxeboot write extension instead
+
+***
+
+
+
+An nfsboot server is defined as a baserock system that has tftp and nfs
+servers running, the tftp server is exporting the contents of
+/srv/nfsboot/tftp/ and the user has sufficient permissions to create nfs roots
+in /srv/nfsboot/nfs/
+
+'''
+
+
+import cliapp
+import os
+import glob
+
+import morphlib.writeexts
+
+
+class NFSBootWriteExtension(morphlib.writeexts.WriteExtension):
+
+ '''Create an NFS root and kernel on TFTP during Morph's deployment.
+
+ The location command line argument is the hostname of the nfsboot server.
+ The user is expected to provide the location argument
+ using the following syntax:
+
+ HOST
+
+ where:
+
+ * HOST is the host of the nfsboot server
+
+ The extension will connect to root@HOST via ssh to copy the kernel and
+ rootfs, and configure the nfs server.
+
+ It requires root because it uses systemd, and reads/writes to /etc.
+
+ '''
+
+ _nfsboot_root = '/srv/nfsboot'
+
+ def process_args(self, args):
+ if len(args) != 2:
+ raise cliapp.AppException('Wrong number of command line args')
+
+ temp_root, location = args
+
+ version_label = os.getenv('VERSION_LABEL', 'factory')
+ hostname = os.environ['HOSTNAME']
+
+ versioned_root = os.path.join(self._nfsboot_root, hostname, 'systems',
+ version_label)
+
+ self.copy_rootfs(temp_root, location, versioned_root, hostname)
+ self.copy_kernel(temp_root, location, versioned_root, version_label,
+ hostname)
+ self.configure_nfs(location, hostname)
+
+ def create_local_state(self, location, hostname):
+ statedir = os.path.join(self._nfsboot_root, hostname, 'state')
+ subdirs = [os.path.join(statedir, 'home'),
+ os.path.join(statedir, 'opt'),
+ os.path.join(statedir, 'srv')]
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['mkdir', '-p'] + subdirs)
+
+ def copy_kernel(self, temp_root, location, versioned_root, version,
+ hostname):
+ bootdir = os.path.join(temp_root, 'boot')
+ image_names = ['vmlinuz', 'zImage', 'uImage']
+ for name in image_names:
+ try_path = os.path.join(bootdir, name)
+ if os.path.exists(try_path):
+ kernel_src = try_path
+ break
+ else:
+ raise cliapp.AppException(
+ 'Could not find a kernel in the system: none of '
+ '%s found' % ', '.join(image_names))
+
+ kernel_dest = os.path.join(versioned_root, 'orig', 'kernel')
+ rsync_dest = 'root@%s:%s' % (location, kernel_dest)
+ self.status(msg='Copying kernel')
+ cliapp.runcmd(
+ ['rsync', '-s', kernel_src, rsync_dest])
+
+ # Link the kernel to the right place
+ self.status(msg='Creating links to kernel in tftp directory')
+ tftp_dir = os.path.join(self._nfsboot_root , 'tftp')
+ versioned_kernel_name = "%s-%s" % (hostname, version)
+ kernel_name = hostname
+ try:
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['ln', '-f', kernel_dest,
+ os.path.join(tftp_dir, versioned_kernel_name)])
+
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['ln', '-sf', versioned_kernel_name,
+ os.path.join(tftp_dir, kernel_name)])
+ except cliapp.AppException:
+ raise cliapp.AppException('Could not create symlinks to the '
+ 'kernel at %s in %s on %s'
+ % (kernel_dest, tftp_dir, location))
+
+ def copy_rootfs(self, temp_root, location, versioned_root, hostname):
+ rootfs_src = temp_root + '/'
+ orig_path = os.path.join(versioned_root, 'orig')
+ run_path = os.path.join(versioned_root, 'run')
+
+ self.status(msg='Creating destination directories')
+ try:
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['mkdir', '-p', orig_path, run_path])
+ except cliapp.AppException:
+ raise cliapp.AppException('Could not create dirs %s and %s on %s'
+ % (orig_path, run_path, location))
+
+ self.status(msg='Creating \'orig\' rootfs')
+ cliapp.runcmd(
+ ['rsync', '-asXSPH', '--delete', rootfs_src,
+ 'root@%s:%s' % (location, orig_path)])
+
+ self.status(msg='Creating \'run\' rootfs')
+ try:
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['rm', '-rf', run_path])
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['cp', '-al', orig_path, run_path])
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['rm', '-rf', os.path.join(run_path, 'etc')])
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['cp', '-a', os.path.join(orig_path, 'etc'),
+ os.path.join(run_path, 'etc')])
+ except cliapp.AppException:
+ raise cliapp.AppException('Could not create \'run\' rootfs'
+ ' from \'orig\'')
+
+ self.status(msg='Linking \'default\' to latest system')
+ try:
+ cliapp.ssh_runcmd('root@%s' % location,
+ ['ln', '-sfn', versioned_root,
+ os.path.join(self._nfsboot_root, hostname, 'systems',
+ 'default')])
+ except cliapp.AppException:
+ raise cliapp.AppException('Could not link \'default\' to %s'
+ % versioned_root)
+
+ def configure_nfs(self, location, hostname):
+ exported_path = os.path.join(self._nfsboot_root, hostname)
+ exports_path = '/etc/exports'
+ # If that path is not already exported:
+ try:
+ cliapp.ssh_runcmd(
+ 'root@%s' % location, ['grep', '-q', exported_path,
+ exports_path])
+ except cliapp.AppException:
+ ip_mask = '*'
+ options = 'rw,no_subtree_check,no_root_squash,async'
+ exports_string = '%s %s(%s)\n' % (exported_path, ip_mask, options)
+ exports_append_sh = '''\
+set -eu
+target="$1"
+temp=$(mktemp)
+cat "$target" > "$temp"
+cat >> "$temp"
+mv "$temp" "$target"
+'''
+ cliapp.ssh_runcmd(
+ 'root@%s' % location,
+ ['sh', '-c', exports_append_sh, '--', exports_path],
+ feed_stdin=exports_string)
+ cliapp.ssh_runcmd(
+ 'root@%s' % location, ['systemctl', 'restart',
+ 'nfs-server.service'])
+
+
+NFSBootWriteExtension().run()