From c808549169a7704cdd5928e0d75a30ecc8036487 Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Mon, 17 Feb 2014 16:17:00 +0000 Subject: deploy: Check the --upgrade flag has been used correctly. Most write extensions don't handle both initial deployments and upgrades of a system. --- kvm.check | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 kvm.check (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check new file mode 100755 index 00000000..be7c51c2 --- /dev/null +++ b/kvm.check @@ -0,0 +1,35 @@ +#!/usr/bin/python +# Copyright (C) 2014 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. + +'''Preparatory checks for Morph 'kvm' write extension''' + +import cliapp + +import morphlib.writeexts + + +class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): + def process_args(self, args): + if len(args) != 1: + raise cliapp.AppException('Wrong number of command line args') + + upgrade = self.get_environment_boolean('UPGRADE') + if upgrade: + raise cliapp.AppException( + 'Use the `ssh-rsync` write extension to deploy upgrades to an ' + 'existing remote system.') + +KvmPlusSshCheckExtension().run() -- cgit v1.2.1 From 0328bdbf7c3d2def974ca3279fe8732f6f8fa968 Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Fri, 11 Apr 2014 12:29:32 +0000 Subject: deploy: Check SSH connection for KVM deployment before starting Slight duplication is necessary, but it's only a few lines. We could move the duplicated code into the base class in 'morphlib.writeexts' if there was more duplication. --- kvm.check | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index be7c51c2..04c25069 100755 --- a/kvm.check +++ b/kvm.check @@ -17,11 +17,16 @@ '''Preparatory checks for Morph 'kvm' write extension''' import cliapp +import re +import urlparse import morphlib.writeexts class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): + + location_pattern = '^/(?P[^/]+)(?P/.+)$' + def process_args(self, args): if len(args) != 1: raise cliapp.AppException('Wrong number of command line args') @@ -32,4 +37,28 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): 'Use the `ssh-rsync` write extension to deploy upgrades to an ' 'existing remote system.') + location = args[0] + ssh_host, vm_name, vm_path = self.check_and_parse_location(location) + + try: + cliapp.ssh_runcmd(ssh_host, ['true']) + except cliapp.AppException: + raise cliapp.AppException('Unable to SSH to %s' % ssh_host) + + def check_and_parse_location(self, location): + '''Check and parse the location argument to get relevant data.''' + + x = urlparse.urlparse(location) + + if x.scheme != 'kvm+ssh': + raise cliapp.AppException( + 'URL schema must be kvm+ssh in %s' % location) + + m = re.match(self.location_pattern, x.path) + if not m: + raise cliapp.AppException('Cannot parse location %s' % location) + + return x.netloc, m.group('guest'), m.group('path') + + KvmPlusSshCheckExtension().run() -- cgit v1.2.1 From 7223d97177b570020941562c9c054584d41c5d7c Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Mon, 14 Apr 2014 12:35:26 +0000 Subject: deploy: Share SSH connectivity check in the common writeexts.py code Also, change it to log the real error message in morph.log before raising a more general exception to the user. --- kvm.check | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 04c25069..6ce52e7e 100755 --- a/kvm.check +++ b/kvm.check @@ -40,10 +40,7 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): location = args[0] ssh_host, vm_name, vm_path = self.check_and_parse_location(location) - try: - cliapp.ssh_runcmd(ssh_host, ['true']) - except cliapp.AppException: - raise cliapp.AppException('Unable to SSH to %s' % ssh_host) + self.check_ssh_connectivity(ssh_host) def check_and_parse_location(self, location): '''Check and parse the location argument to get relevant data.''' -- cgit v1.2.1 From 8bb0d71ba1ada3c26b6bfa553c2ceb831c161818 Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Mon, 14 Apr 2014 13:19:20 +0000 Subject: deploy: Extra checks for KVM deployments Test that a VM with the given name does not already exist, and check that the files specified in ATTACH_DISKS do already exist. --- kvm.check | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 6ce52e7e..957d0893 100755 --- a/kvm.check +++ b/kvm.check @@ -41,6 +41,8 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): ssh_host, vm_name, vm_path = self.check_and_parse_location(location) self.check_ssh_connectivity(ssh_host) + self.check_no_existing_libvirt_vm(ssh_host, vm_name) + self.check_extra_disks_exist(ssh_host, self.parse_attach_disks()) def check_and_parse_location(self, location): '''Check and parse the location argument to get relevant data.''' @@ -57,5 +59,24 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): return x.netloc, m.group('guest'), m.group('path') + def check_no_existing_libvirt_vm(self, ssh_host, vm_name): + try: + cliapp.ssh_runcmd(ssh_host, + ['virsh', '--connect', 'qemu:///system', 'domstate', vm_name]) + except cliapp.AppException as e: + pass + else: + raise cliapp.AppException( + 'Host %s already has a VM named %s. You can use the ssh-rsync ' + 'write extension to deploy upgrades to existing machines.' % + (ssh_host, vm_name)) + + def check_extra_disks_exist(self, ssh_host, filename_list): + for filename in filename_list: + try: + cliapp.ssh_runcmd(ssh_host, ['ls', filename]) + except cliapp.AppException as e: + raise cliapp.AppException('Did not find file %s on host %s' % + (filename, ssh_host)) KvmPlusSshCheckExtension().run() -- cgit v1.2.1 From cc059a443b1dbcd214075dcaf95d601e47ca655f Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Tue, 10 Jun 2014 17:18:37 +0000 Subject: Check for presence of btrfs before trying to use it If btrfs is not present in the kernel we end up with strange output like this: Error creating disk image2014-06-10 16:00:40 [devel-system-x86_64-generic][my-raw-disk-image][rawdisk.write]Failure to create disk image at /src/tmp/testdev.img ERROR: Command failed: mount -o loop /src/tmp/testdev.img /src/tmp/deployments/tmpQ7wXO1/tmp4lVDcu/tmpvHSzDE mount: mounting /dev/loop0 on /src/tmp/deployments/tmpQ7wXO1/tmp4lVDcu/tmpvHSzDE failed: Device or resource busy To avoid this confusing error, Morph should explicitly check first. --- kvm.check | 2 ++ 1 file changed, 2 insertions(+) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 957d0893..1bb4007a 100755 --- a/kvm.check +++ b/kvm.check @@ -31,6 +31,8 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): if len(args) != 1: raise cliapp.AppException('Wrong number of command line args') + self.require_btrfs_in_deployment_host_kernel() + upgrade = self.get_environment_boolean('UPGRADE') if upgrade: raise cliapp.AppException( -- cgit v1.2.1 From 0e6d9749b509382f65177337ec6465cdbea44b7b Mon Sep 17 00:00:00 2001 From: Richard Ipsum Date: Wed, 28 Jan 2015 11:32:07 +0000 Subject: Check file can be created at location --- kvm.check | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 1bb4007a..3c6accbf 100755 --- a/kvm.check +++ b/kvm.check @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright (C) 2014 Codethink Limited +# Copyright (C) 2014-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 @@ -43,6 +43,7 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): ssh_host, vm_name, vm_path = self.check_and_parse_location(location) self.check_ssh_connectivity(ssh_host) + self.check_can_create_file_at_given_path(ssh_host, vm_path) self.check_no_existing_libvirt_vm(ssh_host, vm_name) self.check_extra_disks_exist(ssh_host, self.parse_attach_disks()) @@ -73,6 +74,26 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): 'write extension to deploy upgrades to existing machines.' % (ssh_host, vm_name)) + def check_can_create_file_at_given_path(self, ssh_host, vm_path): + + def check_can_write_to_given_path(): + try: + cliapp.ssh_runcmd(ssh_host, ['touch', vm_path]) + except cliapp.AppException as e: + raise cliapp.AppException("Can't write to location %s on %s" + % (vm_path, ssh_host)) + else: + cliapp.ssh_runcmd(ssh_host, ['rm', vm_path]) + + try: + cliapp.ssh_runcmd(ssh_host, ['test', '-e', vm_path]) + except cliapp.AppException as e: + # vm_path doesn't already exist, so let's test we can write + check_can_write_to_given_path() + else: + raise cliapp.AppException('%s already exists on %s' + % (vm_path, ssh_host)) + def check_extra_disks_exist(self, ssh_host, filename_list): for filename in filename_list: try: -- cgit v1.2.1 From ca9e43bf901d10bd1c0ec580255f6096bb53c65f Mon Sep 17 00:00:00 2001 From: Richard Ipsum Date: Wed, 28 Jan 2015 17:28:00 +0000 Subject: Add check for virtual networks An exception will be raised if any needed networks are not started --- kvm.check | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 3c6accbf..b8877a89 100755 --- a/kvm.check +++ b/kvm.check @@ -17,6 +17,7 @@ '''Preparatory checks for Morph 'kvm' write extension''' import cliapp +import os import re import urlparse @@ -46,6 +47,7 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): self.check_can_create_file_at_given_path(ssh_host, vm_path) self.check_no_existing_libvirt_vm(ssh_host, vm_name) self.check_extra_disks_exist(ssh_host, self.parse_attach_disks()) + self.check_virtual_networks_are_started(ssh_host) def check_and_parse_location(self, location): '''Check and parse the location argument to get relevant data.''' @@ -102,4 +104,50 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): raise cliapp.AppException('Did not find file %s on host %s' % (filename, ssh_host)) + def check_virtual_networks_are_started(self, ssh_host): + + def check_virtual_network_is_started(network_name): + cmd = ['virsh', '-c', 'qemu:///system', 'net-info', network_name] + net_info = cliapp.ssh_runcmd(ssh_host, cmd).split('\n') + + def pretty_concat(lines): + return '\n'.join(['\t%s' % line for line in lines]) + + for line in net_info: + m = re.match('^Active:\W*(\w+)\W*', line) + if m: + break + else: + raise cliapp.AppException( + "Got unexpected output parsing output of `%s':\n%s" + % (' '.join(cmd), pretty_concat(net_info))) + + network_active = m.group(1) == 'yes' + + if not network_active: + raise cliapp.AppException("Network '%s' is not started" + % network_name) + + def name(nic_entry): + if ',' in nic_entry: + # NETWORK_NAME,mac=12:34,model=e1000... + return nic_entry[:nic_entry.find(',')] + else: + return nic_entry # NETWORK_NAME + + if 'NIC_CONFIG' in os.environ: + nics = os.environ['NIC_CONFIG'].split() + + # --network bridge= is used to specify a bridge + # --network user is used to specify a form of NAT + # (see the virt-install(1) man page) + networks = [name(n) for n in nics if not n.startswith('bridge=') + and not n.startswith('user')] + else: + networks = ['default'] + + for network in networks: + check_virtual_network_is_started(network) + + KvmPlusSshCheckExtension().run() -- cgit v1.2.1 From ed741d8d090086e2380f7b9d68ddc3bd122acb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jard=C3=B3n?= Date: Fri, 13 Mar 2015 18:18:55 +0000 Subject: Use the modern way of the GPL copyright header: URL instead real address Change-Id: I992dc0c1d40f563ade56a833162d409b02be90a0 --- kvm.check | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index b8877a89..62d76453 100755 --- a/kvm.check +++ b/kvm.check @@ -11,8 +11,7 @@ # 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. +# with this program. If not, see . '''Preparatory checks for Morph 'kvm' write extension''' -- cgit v1.2.1 From a69b342f542f4895ef2bfd9f18a0e56d38b4bc7d Mon Sep 17 00:00:00 2001 From: Richard Ipsum Date: Mon, 30 Mar 2015 13:34:40 +0000 Subject: Fix: strip 'network=' from NIC_CONFIG Also ensure NIC_CONFIG begins with 'network=', 'bridge=' or is 'user' Change-Id: I3bcbd25eb2c9a05b7fa276697f97a1080cb0316e --- kvm.check | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 62d76453..83562e44 100755 --- a/kvm.check +++ b/kvm.check @@ -129,14 +129,22 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): def name(nic_entry): if ',' in nic_entry: - # NETWORK_NAME,mac=12:34,model=e1000... - return nic_entry[:nic_entry.find(',')] + # network=NETWORK_NAME,mac=12:34,model=e1000... + return nic_entry[:nic_entry.find(',')].lstrip('network=') else: - return nic_entry # NETWORK_NAME + return nic_entry.lstrip('network=') # NETWORK_NAME if 'NIC_CONFIG' in os.environ: nics = os.environ['NIC_CONFIG'].split() + for n in nics: + if not (n.startswith('network=') + or n.startswith('bridge=') + or n == 'user'): + raise cliapp.AppException('malformed NIC_CONFIG: %s\n' + " (expected 'bridge=BRIDGE' 'network=NAME'" + " or 'user')" % n) + # --network bridge= is used to specify a bridge # --network user is used to specify a form of NAT # (see the virt-install(1) man page) -- cgit v1.2.1 From 2b35eb5ebef0de0ad78466f41ccfd7792fbf2e40 Mon Sep 17 00:00:00 2001 From: Richard Ipsum Date: Fri, 3 Apr 2015 20:05:09 +0100 Subject: Make kvm deploy check that host has virt-install This allows us to catch a case where virt-install hasn't been installed on the host we're deploying to much earlier in the deployment process. Change-Id: I413ad804a7e8bef4fc2d1231411e01d30d0cb9e8 --- kvm.check | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'kvm.check') diff --git a/kvm.check b/kvm.check index 83562e44..67cb3d38 100755 --- a/kvm.check +++ b/kvm.check @@ -47,6 +47,7 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): self.check_no_existing_libvirt_vm(ssh_host, vm_name) self.check_extra_disks_exist(ssh_host, self.parse_attach_disks()) self.check_virtual_networks_are_started(ssh_host) + self.check_host_has_virtinstall(ssh_host) def check_and_parse_location(self, location): '''Check and parse the location argument to get relevant data.''' @@ -156,5 +157,13 @@ class KvmPlusSshCheckExtension(morphlib.writeexts.WriteExtension): for network in networks: check_virtual_network_is_started(network) + def check_host_has_virtinstall(self, ssh_host): + try: + cliapp.ssh_runcmd(ssh_host, ['which', 'virt-install']) + except cliapp.AppException: + raise cliapp.AppException( + 'virt-install does not seem to be installed on host %s' + % ssh_host) + KvmPlusSshCheckExtension().run() -- cgit v1.2.1