From e4f27e978c28c73040c356a497df1c4180edc60f Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Sun, 9 Jun 2013 21:04:46 +0000 Subject: Sync write extensions with morph This adds improved autostart logic for kvm, the new filesystem layout, a working tar.write, network config for virtualbox and allowing paths with spaces in. --- kvm.write | 17 +++++++++---- nfsboot.write | 22 ++++++++-------- rawdisk.write | 11 +++++++- ssh-rsync.write | 28 ++++++++++++++------ tar.write | 2 +- virtualbox-ssh.write | 72 +++++++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 119 insertions(+), 33 deletions(-) diff --git a/kvm.write b/kvm.write index ae287fe..67ac40e 100755 --- a/kvm.write +++ b/kvm.write @@ -94,7 +94,7 @@ class KvmPlusSshWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Transferring disk image') target = '%s:%s' % (ssh_host, vm_path) with open(raw_disk, 'rb') as f: - cliapp.runcmd(['rsync', '-zS', raw_disk, target]) + cliapp.runcmd(['rsync', '-szS', raw_disk, target]) def create_libvirt_guest(self, ssh_host, vm_name, vm_path, autostart): '''Create the libvirt virtual machine.''' @@ -106,16 +106,23 @@ class KvmPlusSshWriteExtension(morphlib.writeexts.WriteExtension): for disk in attach_disks: attach_opts.extend(['--disk', 'path=%s' % disk]) + if 'NIC_CONFIG' in os.environ: + nics = os.environ['NIC_CONFIG'].split() + for nic in nics: + attach_opts.extend(['--network', nic]) + ram_mebibytes = str(self.get_ram_size() / (1024**2)) - cmdline = ['ssh', ssh_host, - 'virt-install', '--connect qemu:///system', '--import', + cmdline = ['virt-install', '--connect', 'qemu:///system', '--import', '--name', vm_name, '--vnc', '--ram=%s' % ram_mebibytes, - '--disk path=%s,bus=ide' % vm_path] + attach_opts + '--disk', 'path=%s,bus=ide' % vm_path] + attach_opts if not autostart: cmdline += ['--noreboot'] - cliapp.runcmd(cmdline) + cliapp.ssh_runcmd(ssh_host, cmdline) + if autostart: + cliapp.ssh_runcmd(ssh_host, + ['virsh', '--connect', 'qemu:///system', 'autostart', vm_name]) KvmPlusSshWriteExtension().run() diff --git a/nfsboot.write b/nfsboot.write index 61c5306..34a7297 100755 --- a/nfsboot.write +++ b/nfsboot.write @@ -66,15 +66,15 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): 'with hostname "baserock"') self.test_good_server(location) - version = os.environ['VERSION'] or 'version1' + version_label = os.getenv('VERSION_LABEL', 'factory') versioned_root = os.path.join(self._nfsboot_root, hostname, 'systems', - version) + version_label) if self.version_exists(versioned_root, location): raise cliapp.AppException('Version %s already exists on' ' this device. Deployment aborted' - % version) + % version_label) self.copy_rootfs(temp_root, location, versioned_root, hostname) - self.copy_kernel(temp_root, location, versioned_root, version, + self.copy_kernel(temp_root, location, versioned_root, version_label, hostname) self.configure_nfs(location, hostname) @@ -118,7 +118,7 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): rsync_dest = 'root@%s:%s' % (location, kernel_dest) self.status(msg='Copying kernel') cliapp.runcmd( - ['rsync', kernel_src, rsync_dest]) + ['rsync', '-s', kernel_src, rsync_dest]) # Link the kernel to the right place self.status(msg='Creating links to kernel in tftp directory') @@ -153,7 +153,7 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Creating \'orig\' rootfs') cliapp.runcmd( - ['rsync', '-aXSPH', '--delete', rootfs_src, + ['rsync', '-asXSPH', '--delete', rootfs_src, 'root@%s:%s' % (location, orig_path)]) self.status(msg='Creating \'run\' rootfs') @@ -171,15 +171,15 @@ class NFSBootWriteExtension(morphlib.writeexts.WriteExtension): raise cliapp.AppException('Could not create \'run\' rootfs' ' from \'orig\'') - self.status(msg='Linking \'default-run\' to latest system') + self.status(msg='Linking \'default\' to latest system') try: cliapp.ssh_runcmd('root@%s' % location, - ['ln', '-sfn', run_path, + ['ln', '-sfn', versioned_root, os.path.join(self._nfsboot_root, hostname, 'systems', - 'default-run')]) + 'default')]) except cliapp.AppException: - raise cliapp.AppException('Could not link \'default-run\' to %s' - % run_path) + 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) diff --git a/rawdisk.write b/rawdisk.write index a43a9cc..a74d690 100755 --- a/rawdisk.write +++ b/rawdisk.write @@ -70,9 +70,18 @@ class RawDiskWriteExtension(morphlib.writeexts.WriteExtension): self.create_run(version_root) + default_path = os.path.join(mp, 'systems', 'default') + if os.path.exists(default_path): + os.remove(default_path) + else: + # we are upgrading and old system that does + # not have an updated extlinux config file + if self.bootloader_is_wanted(): + self.install_extlinux(mp) + os.symlink(version_label, default_path) + if self.bootloader_is_wanted(): self.install_kernel(version_root, temp_root) - self.install_extlinux(mp, version_label) self.unmount(mp) diff --git a/ssh-rsync.write b/ssh-rsync.write index 6fe1153..fba550c 100755 --- a/ssh-rsync.write +++ b/ssh-rsync.write @@ -72,6 +72,21 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): 'snapshot', orig_dir, run_dir]) self.install_remote_kernel(location, version_root, temp_root) + default_path = os.path.join(remote_mnt, 'systems', 'default') + if self.bootloader_is_wanted(): + output = cliapp.ssh_runcmd(location, ['sh', '-c', + 'test -e "$1" && stat -c %F "$1"' + ' || ' + 'echo missing file', + '-', default_path]) + if output != "symbolic link": + # we are upgrading and old system that does + # not have an updated extlinux config file + self.update_remote_extlinux(location, remote_mnt, + version_label) + cliapp.ssh_runcmd(location, ['ln', '-s', '-f', + version_label, + default_path]) except Exception as e: try: cliapp.ssh_runcmd(location, @@ -83,9 +98,6 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): pass raise e - if self.bootloader_is_wanted(): - self.update_remote_extlinux(location, remote_mnt, - version_label) except: raise else: @@ -103,15 +115,15 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): 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('kernel /systems/default/kernel\n') f.write('append root=/dev/sda ' - 'rootflags=subvol=systems/' + version_label + '/run ' + 'rootflags=subvol=systems/default/run ' 'init=/sbin/init rw\n') cliapp.ssh_runcmd(location, ['mv', config, config+'~']) try: - cliapp.runcmd(['rsync', '-a', temp_file, + cliapp.runcmd(['rsync', '-as', temp_file, '%s:%s' % (location, config)]) except Exception as e: try: @@ -130,7 +142,7 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): cliapp.ssh_runcmd(location, ['btrfs', 'subvolume', 'snapshot', old_orig, new_orig]) - cliapp.runcmd(['rsync', '-a', '--checksum', '--numeric-ids', + cliapp.runcmd(['rsync', '-as', '--checksum', '--numeric-ids', '--delete', temp_root, '%s:%s' % (location, new_orig)]) def get_old_orig(self, location, remote_mnt): @@ -158,7 +170,7 @@ class SshRsyncWriteExtension(morphlib.writeexts.WriteExtension): 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, + cliapp.runcmd(['rsync', '-as', try_path, '%s:%s' % (location, kernel_dest)]) def check_valid_target(self, location): diff --git a/tar.write b/tar.write index 7a2f01e..333626b 100755 --- a/tar.write +++ b/tar.write @@ -18,4 +18,4 @@ set -eu -tar -C "$1" -cf "$2" +tar -C "$1" -cf "$2" . diff --git a/virtualbox-ssh.write b/virtualbox-ssh.write index cb17b69..3ee2eae 100755 --- a/virtualbox-ssh.write +++ b/virtualbox-ssh.write @@ -101,24 +101,26 @@ class VirtualBoxPlusSshWriteExtension(morphlib.writeexts.WriteExtension): self.status(msg='Transfer disk and convert to VDI') with open(raw_disk, 'rb') as f: - cliapp.runcmd( - ['ssh', ssh_host, - 'VBoxManage', 'convertfromraw', 'stdin', vdi_path, + cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'convertfromraw', 'stdin', vdi_path, str(os.path.getsize(raw_disk))], stdin=f) def create_virtualbox_guest(self, ssh_host, vm_name, vdi_path, autostart): '''Create the VirtualBox virtual machine.''' - + self.status(msg='Create VirtualBox virtual machine') ram_mebibytes = str(self.get_ram_size() / (1024**2)) + hostonly_iface = self.get_host_interface(ssh_host) + commands = [ ['createvm', '--name', vm_name, '--ostype', 'Linux26_64', '--register'], ['modifyvm', vm_name, '--ioapic', 'on', '--memory', ram_mebibytes, - '--nic1', 'nat'], + '--nic1', 'hostonly', '--hostonlyadapter1', hostonly_iface, + '--nic2', 'nat', '--natnet2', 'default'], ['storagectl', vm_name, '--name', '"SATA Controller"', '--add', 'sata', '--bootable', 'on', '--sataportcount', '2'], ['storageattach', vm_name, '--storagectl', '"SATA Controller"', @@ -140,9 +142,65 @@ class VirtualBoxPlusSshWriteExtension(morphlib.writeexts.WriteExtension): commands.append(['startvm', vm_name]) for command in commands: - argv = ['ssh', ssh_host, 'VBoxManage'] + command - cliapp.runcmd(argv) + argv = ['VBoxManage'] + command + cliapp.ssh_runcmd(ssh_host, argv) + + def get_host_interface(self, ssh_host): + host_ipaddr = os.environ.get('HOST_IPADDR') + netmask = os.environ.get('NETMASK') + network_config = os.environ.get("NETWORK_CONFIG") + if network_config is None: + raise cliapp.AppException('NETWORK_CONFIG was not given') + + if "eth0:" not in network_config: + raise cliapp.AppException( + 'NETWORK_CONFIG does not contain ' + 'the eth0 configuration') + + if "eth1:" not in network_config: + raise cliapp.AppException( + 'NETWORK_CONFIG does not contain ' + 'the eth1 configuration') + + if host_ipaddr is None: + raise cliapp.AppException('HOST_IPADDR was not given') + + if netmask is None: + raise cliapp.AppException('NETMASK was not given') + + # 'VBoxManage list hostonlyifs' retrieves a list with the hostonly + # interfaces on the host. For each interface, the following lines + # are shown on top: + # + # Name: vboxnet0 + # GUID: 786f6276-656e-4074-8000-0a0027000000 + # Dhcp: Disabled + # IPAddress: 192.168.100.1 + # + # The following command tries to retrieve the hostonly interface + # name (e.g. vboxnet0) associated with the given ip address. + iface = None + lines = cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'list', 'hostonlyifs']).splitlines() + for i, v in enumerate(lines): + if host_ipaddr in v: + iface = lines[i-3].split()[1] + break + + if iface is None: + iface = cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'hostonlyif', 'create']) + # 'VBoxManage hostonlyif create' shows the name of the + # created hostonly interface inside single quotes + iface = iface[iface.find("'") + 1 : iface.rfind("'")] + cliapp.ssh_runcmd(ssh_host, + ['VBoxManage', 'hostonlyif', + 'ipconfig', iface, + '--ip', host_ipaddr, + '--netmask', netmask]) + + return iface VirtualBoxPlusSshWriteExtension().run() -- cgit v1.2.1