summaryrefslogtreecommitdiff
path: root/morphlib/exts/nfsboot.write
diff options
context:
space:
mode:
authorJonathan Maw <jonathan.maw@codethink.co.uk>2013-03-26 14:11:34 +0000
committerJonathan Maw <jonathan.maw@codethink.co.uk>2013-04-05 14:55:01 +0000
commit4082a4ddc6675ce0cba8581a115c6a1a78f5e4eb (patch)
tree75725324a551328e5299b1f4920753191f8b1693 /morphlib/exts/nfsboot.write
parent2040748226870c24abadcb3972f46a6d30ec596b (diff)
downloadmorph-4082a4ddc6675ce0cba8581a115c6a1a78f5e4eb.tar.gz
Add nfsboot write extension
The 'location' command-line argument refers to the hostname of the 'nfsboot server', a baserock system that has an nfs server, the directory '/srv/nfsboot/nfs', and a tftp server that hosts files from the directory '/srv/nfsboot/tftp'. The write extension will read the hostname of the target system and copy its root filesystem to /srv/nfsboot/nfs/<hostname> and its kernel to /srv/nfsboot/tftp/<hostname>. It will then configure the nfs server to export that nfs root.
Diffstat (limited to 'morphlib/exts/nfsboot.write')
-rwxr-xr-xmorphlib/exts/nfsboot.write161
1 files changed, 161 insertions, 0 deletions
diff --git a/morphlib/exts/nfsboot.write b/morphlib/exts/nfsboot.write
new file mode 100755
index 00000000..60b4d00d
--- /dev/null
+++ b/morphlib/exts/nfsboot.write
@@ -0,0 +1,161 @@
+#!/usr/bin/python
+# Copyright (C) 2012-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 deploying to an nfsboot server
+
+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.
+
+ '''
+
+ def process_args(self, args):
+ if len(args) != 2:
+ raise cliapp.AppException('Wrong number of command line args')
+
+ temp_root, location = args
+ hostname = self.get_hostname(temp_root)
+ if hostname == 'baserock':
+ raise cliapp.AppException('It is forbidden to nfsboot a system '
+ 'with hostname "baserock"')
+
+ self.test_good_server(location)
+ self.copy_kernel(temp_root, location, hostname)
+ self.copy_rootfs(temp_root, location, hostname)
+ self.configure_nfs(location, hostname)
+
+ def get_hostname(self, temp_root):
+ hostnamepath = os.path.join(temp_root, 'etc', 'hostname')
+ with open(hostnamepath) as f:
+ return f.readline().strip()
+
+ def copy_kernel(self, temp_root, location, 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('/srv/nfsboot/tftp', hostname)
+ rsync_dest = 'root@%s:%s' % (location, kernel_dest)
+ cliapp.runcmd(
+ ['rsync', kernel_src, rsync_dest])
+
+ def copy_rootfs(self, temp_root, location, hostname):
+ rootfs_src = temp_root + '/'
+ rootfs_dest = os.path.join('/srv/nfsboot/nfs', hostname)
+ rsync_dest = 'root@%s:%s' % (location, rootfs_dest)
+ cliapp.runcmd(
+ ['rsync', '-a', rootfs_src, rsync_dest])
+
+ def configure_nfs(self, location, hostname):
+ rootfs_dest = os.path.join('/srv/nfsboot/nfs', hostname)
+ exports_path = '/etc/exports'
+ # If that path is not already exported:
+ try:
+ cliapp.ssh_runcmd(
+ 'root@%s' % location, ['grep', '-q', rootfs_dest,
+ exports_path])
+ except cliapp.AppException:
+ ip_mask = '*'
+ options = 'rw,no_subtree_check,no_root_squash,async'
+ exports_string = '%s %s(%s)\n' % (rootfs_dest, 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', 'reload',
+ 'nfs-server.service'])
+
+ def test_good_server(self, server):
+ # Can be ssh'ed into
+ try:
+ cliapp.ssh_runcmd('root@%s' % server, ['true'])
+ except cliapp.AppException:
+ raise cliapp.AppException('You are unable to ssh into server %s'
+ % server)
+
+ # Is an NFS server
+ try:
+ cliapp.ssh_runcmd(
+ 'root@%s' % server, ['test', '-e', '/etc/exports'])
+ except cliapp.AppException:
+ raise cliapp.AppException('server %s is not an nfs server'
+ % server)
+ try:
+ cliapp.ssh_runcmd(
+ 'root@%s' % server, ['systemctl', 'is-enabled',
+ 'nfs-server.service'])
+
+ except cliapp.AppException:
+ raise cliapp.AppException('server %s does not control its '
+ 'nfs server by systemd' % server)
+
+ # TFTP server exports /srv/nfsboot/tftp
+ try:
+ cliapp.ssh_runcmd(
+ 'root@%s' % server, ['test' , '-d', '/srv/nfsboot/tftp'])
+ except cliapp.AppException:
+ raise cliapp.AppException('server %s does not export '
+ '/srv/nfsboot/tftp' % server)
+
+NFSBootWriteExtension().run()
+