diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py
new file mode 100644
index 000000000000..085a61bc9195
--- /dev/null
+++ b/nova/tests/fake_libvirt_utils.py
@@ -0,0 +1,104 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import StringIO
+
+files = {}
+disk_sizes = {}
+disk_backing_files = {}
+
+
+def create_image(disk_format, path, size):
+ pass
+
+
+def create_cow_image(backing_file, path):
+ pass
+
+
+def get_disk_size(path):
+ return disk_sizes.get(path, 1024 * 1024 * 20)
+
+
+def get_backing_file(path):
+ return disk_backing_files.get(path, None)
+
+
+def copy_image(src, dest):
+ pass
+
+
+def mkfs(fs, path):
+ pass
+
+
+def ensure_tree(path):
+ pass
+
+
+def write_to_file(path, contents, umask=None):
+ pass
+
+
+def chown(path, owner):
+ pass
+
+
+def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt):
+ files[out_path] = ''
+
+
+class File(object):
+ def __init__(self, path, mode=None):
+ self.fp = StringIO.StringIO(files[path])
+
+ def __enter__(self):
+ return self.fp
+
+ def __exit__(self, *args):
+ return
+
+
+def file_open(path, mode=None):
+ return File(path, mode)
+
+
+def load_file(path):
+ return ''
+
+
+def file_delete(path):
+ return True
+
+
+def get_open_port(start_port, end_port):
+ # Return the port in the middle
+ return int((start_port + end_port) / 2)
+
+
+def run_ajaxterm(cmd, token, port):
+ pass
+
+
+def get_fs_info(path):
+ return {'total': 128 * (1024 ** 3),
+ 'used': 44 * (1024 ** 3),
+ 'free': 84 * (1024 ** 3)}
+
+
+def fetch_image(context, target, image_id, user_id, project_id,
+ size=None):
+ pass
diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py
new file mode 100644
index 000000000000..624c95c6418f
--- /dev/null
+++ b/nova/tests/fakelibvirt.py
@@ -0,0 +1,779 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2010 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from xml.etree.ElementTree import fromstring as xml_to_tree
+from xml.etree.ElementTree import ParseError
+import uuid
+
+# Allow passing None to the various connect methods
+# (i.e. allow the client to rely on default URLs)
+allow_default_uri_connection = True
+
+# string indicating the CPU arch
+node_arch = 'x86_64' # or 'i686' (or whatever else uname -m might return)
+
+# memory size in kilobytes
+node_kB_mem = 4096
+
+# the number of active CPUs
+node_cpus = 2
+
+# expected CPU frequency
+node_mhz = 800
+
+# the number of NUMA cell, 1 for unusual NUMA topologies or uniform
+# memory access; check capabilities XML for the actual NUMA topology
+node_nodes = 1 # NUMA nodes
+
+# number of CPU sockets per node if nodes > 1, total number of CPU
+# sockets otherwise
+node_sockets = 1
+
+# number of cores per socket
+node_cores = 2
+
+# number of threads per core
+node_threads = 1
+
+# CPU model
+node_cpu_model = "Penryn"
+
+# CPU vendor
+node_cpu_vendor = "Intel"
+
+
+def _reset():
+ global allow_default_uri_connection
+ allow_default_uri_connection = True
+
+# virDomainState
+VIR_DOMAIN_NOSTATE = 0
+VIR_DOMAIN_RUNNING = 1
+VIR_DOMAIN_BLOCKED = 2
+VIR_DOMAIN_PAUSED = 3
+VIR_DOMAIN_SHUTDOWN = 4
+VIR_DOMAIN_SHUTOFF = 5
+VIR_DOMAIN_CRASHED = 6
+
+VIR_CPU_COMPARE_ERROR = -1
+VIR_CPU_COMPARE_INCOMPATIBLE = 0
+VIR_CPU_COMPARE_IDENTICAL = 1
+VIR_CPU_COMPARE_SUPERSET = 2
+
+VIR_CRED_AUTHNAME = 2
+VIR_CRED_NOECHOPROMPT = 7
+
+# libvirtError enums
+# (Intentionally different from what's in libvirt. We do this to check,
+# that consumers of the library are using the symbolic names rather than
+# hardcoding the numerical values)
+VIR_FROM_QEMU = 100
+VIR_FROM_DOMAIN = 200
+VIR_FROM_NWFILTER = 330
+VIR_ERR_XML_DETAIL = 350
+VIR_ERR_NO_DOMAIN = 420
+VIR_ERR_NO_NWFILTER = 620
+
+
+def _parse_disk_info(element):
+ disk_info = {}
+ disk_info['type'] = element.get('type', 'file')
+ disk_info['device'] = element.get('device', 'disk')
+
+ driver = element.find('./driver')
+ if driver is not None:
+ disk_info['driver_name'] = driver.get('name')
+ disk_info['driver_type'] = driver.get('type')
+
+ source = element.find('./source')
+ if source is not None:
+ disk_info['source'] = source.get('file')
+ if not disk_info['source']:
+ disk_info['source'] = source.get('dev')
+
+ if not disk_info['source']:
+ disk_info['source'] = source.get('path')
+
+ target = element.find('./target')
+ if target is not None:
+ disk_info['target_dev'] = target.get('dev')
+ disk_info['target_bus'] = target.get('bus')
+
+ return disk_info
+
+
+class libvirtError(Exception):
+ def __init__(self, error_code, error_domain, msg):
+ self.error_code = error_code
+ self.error_domain = error_domain
+ Exception(self, msg)
+
+ def get_error_code(self):
+ return self.error_code
+
+ def get_error_domain(self):
+ return self.error_domain
+
+
+class NWFilter(object):
+ def __init__(self, connection, xml):
+ self._connection = connection
+
+ self._xml = xml
+ self._parse_xml(xml)
+
+ def _parse_xml(self, xml):
+ tree = xml_to_tree(xml)
+ root = tree.find('.')
+ self._name = root.get('name')
+
+ def undefine(self):
+ self._connection._remove_filter(self)
+
+
+class Domain(object):
+ def __init__(self, connection, xml, running=False, transient=False):
+ self._connection = connection
+ if running:
+ connection._mark_running(self)
+
+ self._state = running and VIR_DOMAIN_RUNNING or VIR_DOMAIN_SHUTOFF
+ self._transient = transient
+ self._def = self._parse_definition(xml)
+ self._has_saved_state = False
+ self._snapshots = {}
+
+ def _parse_definition(self, xml):
+ try:
+ tree = xml_to_tree(xml)
+ except ParseError:
+ raise libvirtError(VIR_ERR_XML_DETAIL, VIR_FROM_DOMAIN,
+ "Invalid XML.")
+
+ definition = {}
+
+ name = tree.find('./name')
+ if name is not None:
+ definition['name'] = name.text
+
+ uuid_elem = tree.find('./uuid')
+ if uuid_elem is not None:
+ definition['uuid'] = uuid_elem.text
+ else:
+ definition['uuid'] = str(uuid.uuid4())
+
+ vcpu = tree.find('./vcpu')
+ if vcpu is not None:
+ definition['vcpu'] = int(vcpu.text)
+
+ memory = tree.find('./memory')
+ if memory is not None:
+ definition['memory'] = int(memory.text)
+
+ os = {}
+ os_type = tree.find('./os/type')
+ if os_type is not None:
+ os['type'] = os_type.text
+ os['arch'] = os_type.get('arch', node_arch)
+
+ os_kernel = tree.find('./os/kernel')
+ if os_kernel is not None:
+ os['kernel'] = os_kernel.text
+
+ os_initrd = tree.find('./os/initrd')
+ if os_initrd is not None:
+ os['initrd'] = os_initrd.text
+
+ os_cmdline = tree.find('./os/cmdline')
+ if os_cmdline is not None:
+ os['cmdline'] = os_cmdline.text
+
+ os_boot = tree.find('./os/boot')
+ if os_boot is not None:
+ os['boot_dev'] = os_boot.get('dev')
+
+ definition['os'] = os
+
+ features = {}
+
+ acpi = tree.find('./features/acpi')
+ if acpi is not None:
+ features['acpi'] = True
+
+ definition['features'] = features
+
+ devices = {}
+
+ device_nodes = tree.find('./devices')
+ if device_nodes is not None:
+ disks_info = []
+ disks = device_nodes.findall('./disk')
+ for disk in disks:
+ disks_info += [_parse_disk_info(disk)]
+ devices['disks'] = disks_info
+
+ nics_info = []
+ nics = device_nodes.findall('./interface')
+ for nic in nics:
+ nic_info = {}
+ nic_info['type'] = nic.get('type')
+
+ mac = nic.find('./mac')
+ if mac is not None:
+ nic_info['mac'] = mac.get('address')
+
+ source = nic.find('./source')
+ if source is not None:
+ if nic_info['type'] == 'network':
+ nic_info['source'] = source.get('network')
+ elif nic_info['type'] == 'bridge':
+ nic_info['source'] = source.get('bridge')
+
+ nics_info += [nic_info]
+
+ devices['nics'] = nics_info
+
+ definition['devices'] = devices
+
+ return definition
+
+ def create(self):
+ self.createWithFlags(0)
+
+ def createWithFlags(self, flags):
+ # FIXME: Not handling flags at the moment
+ self._state = VIR_DOMAIN_RUNNING
+ self._connection._mark_running(self)
+ self._has_saved_state = False
+
+ def isActive(self):
+ return int(self._state == VIR_DOMAIN_RUNNING)
+
+ def undefine(self):
+ self._connection._undefine(self)
+
+ def destroy(self):
+ self._state = VIR_DOMAIN_SHUTOFF
+ self._connection._mark_not_running(self)
+
+ def name(self):
+ return self._def['name']
+
+ def UUIDString(self):
+ return self._def['uuid']
+
+ def interfaceStats(self, device):
+ return [10000242400, 1234, 0, 2, 213412343233, 34214234, 23, 3]
+
+ def blockStats(self, device):
+ return [2, 10000242400, 234, 2343424234, 34]
+
+ def suspend(self):
+ self._state = VIR_DOMAIN_PAUSED
+
+ def info(self):
+ return [VIR_DOMAIN_RUNNING,
+ long(self._def['memory']),
+ long(self._def['memory']),
+ self._def['vcpu'],
+ 123456789L]
+
+ def attachDevice(self, xml):
+ disk_info = _parse_disk_info(xml_to_tree(xml))
+ disk_info['_attached'] = True
+ self._def['devices']['disks'] += [disk_info]
+ return True
+
+ def detachDevice(self, xml):
+ disk_info = _parse_disk_info(xml_to_tree(xml))
+ disk_info['_attached'] = True
+ return disk_info in self._def['devices']['disks']
+
+ def XMLDesc(self, flags):
+ disks = ''
+ for disk in self._def['devices']['disks']:
+ disks += '''
+
+
+
+
+ ''' % disk
+
+ nics = ''
+ for nic in self._def['devices']['nics']:
+ nics += '''
+
+
+
+ ''' % nic
+
+ return '''
+ %(name)s
+ %(uuid)s
+ %(memory)s
+ %(memory)s
+ %(vcpu)s
+
+ hvm
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ restart
+
+ /usr/bin/kvm
+ %(disks)s
+
+
+
+ %(nics)s
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+''' % {'name': self._def['name'],
+ 'uuid': self._def['uuid'],
+ 'memory': self._def['memory'],
+ 'vcpu': self._def['vcpu'],
+ 'arch': self._def['os']['arch'],
+ 'disks': disks,
+ 'nics': nics}
+
+ def managedSave(self, flags):
+ self._connection._mark_not_running(self)
+ self._has_saved_state = True
+
+ def managedSaveRemove(self, flags):
+ self._has_saved_state = False
+
+ def hasManagedSaveImage(self, flags):
+ return int(self._has_saved_state)
+
+ def resume(self):
+ self._state = VIR_DOMAIN_RUNNING
+
+ def snapshotCreateXML(self, xml, flags):
+ tree = xml_to_tree(xml)
+ name = tree.find('./name').text
+ snapshot = DomainSnapshot(name, self)
+ self._snapshots[name] = snapshot
+ return snapshot
+
+
+class DomainSnapshot(object):
+ def __init__(self, name, domain):
+ self._name = name
+ self._domain = domain
+
+ def delete(self, flags):
+ del self._domain._snapshots[self._name]
+
+
+class Connection(object):
+ def __init__(self, uri, readonly):
+ if not uri:
+ if allow_default_uri_connection:
+ uri = 'qemu:///session'
+ else:
+ raise Exception("URI was None, but fake libvirt is configured"
+ " to not accept this.")
+
+ uri_whitelist = ['qemu:///system',
+ 'qemu:///session',
+ 'xen:///system',
+ 'uml:///system']
+
+ if uri not in uri_whitelist:
+ raise libvirtError(5, 0,
+ "libvir: error : no connection driver "
+ "available for No connection for URI %s" % uri)
+
+ self.readonly = readonly
+ self._uri = uri
+ self._vms = {}
+ self._running_vms = {}
+ self._id_counter = 0
+ self._nwfilters = {}
+
+ def _add_filter(self, nwfilter):
+ self._nwfilters[nwfilter._name] = nwfilter
+
+ def _remove_filter(self, nwfilter):
+ del self._nwfilters[nwfilter._name]
+
+ def _mark_running(self, dom):
+ self._running_vms[self._id_counter] = dom
+ self._id_counter += 1
+
+ def _mark_not_running(self, dom):
+ if dom._transient:
+ self._undefine(dom)
+
+ for (k, v) in self._running_vms.iteritems():
+ if v == dom:
+ del self._running_vms[k]
+ return
+
+ def _undefine(self, dom):
+ del self._vms[dom.name()]
+
+ def getInfo(self):
+ return [node_arch,
+ node_kB_mem,
+ node_cpus,
+ node_mhz,
+ node_nodes,
+ node_sockets,
+ node_cores,
+ node_threads]
+
+ def listDomainsID(self):
+ return self._running_vms.keys()
+
+ def lookupByID(self, id):
+ if id in self._running_vms:
+ return self._running_vms[id]
+ raise libvirtError(VIR_ERR_NO_DOMAIN, VIR_FROM_QEMU,
+ 'Domain not found: no domain with matching '
+ 'id %d' % id)
+
+ def lookupByName(self, name):
+ if name in self._vms:
+ return self._vms[name]
+ raise libvirtError(VIR_ERR_NO_DOMAIN, VIR_FROM_QEMU,
+ 'Domain not found: no domain with matching '
+ 'name "%s"' % name)
+
+ def defineXML(self, xml):
+ dom = Domain(connection=self, running=False, transient=False, xml=xml)
+ self._vms[dom.name()] = dom
+ return dom
+
+ def createXML(self, xml, flags):
+ dom = Domain(connection=self, running=True, transient=True, xml=xml)
+ self._vms[dom.name()] = dom
+ return dom
+
+ def getType(self):
+ if self._uri == 'qemu:///system':
+ return 'QEMU'
+
+ def getVersion(self):
+ return 14000
+
+ def getCapabilities(self):
+ return '''
+
+ cef19ce0-0ca2-11df-855d-b19fbce37686
+
+ x86_64
+ Penryn
+ Intel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tcp
+
+
+
+ apparmor
+ 0
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu
+ pc-0.14
+ pc
+ pc-0.13
+ pc-0.12
+ pc-0.11
+ pc-0.10
+ isapc
+
+
+
+ /usr/bin/kvm
+ pc-0.14
+ pc
+ pc-0.13
+ pc-0.12
+ pc-0.11
+ pc-0.10
+ isapc
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hvm
+
+ 64
+ /usr/bin/qemu-system-x86_64
+ pc-0.14
+ pc
+ pc-0.13
+ pc-0.12
+ pc-0.11
+ pc-0.10
+ isapc
+
+
+
+ /usr/bin/kvm
+ pc-0.14
+ pc
+ pc-0.13
+ pc-0.12
+ pc-0.11
+ pc-0.10
+ isapc
+
+
+
+
+
+
+
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu-system-arm
+ integratorcp
+ vexpress-a9
+ syborg
+ musicpal
+ mainstone
+ n800
+ n810
+ n900
+ cheetah
+ sx1
+ sx1-v1
+ beagle
+ beaglexm
+ tosa
+ akita
+ spitz
+ borzoi
+ terrier
+ connex
+ verdex
+ lm3s811evb
+ lm3s6965evb
+ realview-eb
+ realview-eb-mpcore
+ realview-pb-a8
+ realview-pbx-a9
+ versatilepb
+ versatileab
+
+
+
+
+
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu-system-mips
+ malta
+ mipssim
+ magnum
+ pica61
+ mips
+
+
+
+
+
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu-system-mipsel
+ malta
+ mipssim
+ magnum
+ pica61
+ mips
+
+
+
+
+
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu-system-sparc
+ SS-5
+ leon3_generic
+ SS-10
+ SS-600MP
+ SS-20
+ Voyager
+ LX
+ SS-4
+ SPARCClassic
+ SPARCbook
+ SS-1000
+ SS-2000
+ SS-2
+
+
+
+
+
+
+ hvm
+
+ 32
+ /usr/bin/qemu-system-ppc
+ g3beige
+ virtex-ml507
+ mpc8544ds
+ bamboo
+ bamboo-0.13
+ bamboo-0.12
+ ref405ep
+ taihu
+ mac99
+ prep
+
+
+
+
+
+
+
+
+'''
+
+ def compareCPU(self, xml, flags):
+ tree = xml_to_tree(xml)
+
+ arch_node = tree.find('./arch')
+ if arch_node is not None:
+ if arch_node.text not in ['x86_64', 'i686']:
+ return VIR_CPU_COMPARE_INCOMPATIBLE
+
+ model_node = tree.find('./model')
+ if model_node is not None:
+ if model_node.text != node_cpu_model:
+ return VIR_CPU_COMPARE_INCOMPATIBLE
+
+ vendor_node = tree.find('./vendor')
+ if vendor_node is not None:
+ if vendor_node.text != node_cpu_vendor:
+ return VIR_CPU_COMPARE_INCOMPATIBLE
+
+ # The rest of the stuff libvirt implements is rather complicated
+ # and I don't think it adds much value to replicate it here.
+
+ return VIR_CPU_COMPARE_IDENTICAL
+
+ def nwfilterLookupByName(self, name):
+ try:
+ return self._nwfilters[name]
+ except KeyError:
+ raise libvirtError(VIR_ERR_NO_NWFILTER, VIR_FROM_NWFILTER,
+ "no nwfilter with matching name %s" % name)
+
+ def nwfilterDefineXML(self, xml):
+ nwfilter = NWFilter(self, xml)
+ self._add_filter(nwfilter)
+
+
+def openReadOnly(uri):
+ return Connection(uri, readonly=True)
+
+
+def openAuth(uri, auth, flags):
+ if flags != 0:
+ raise Exception(_("Please extend mock libvirt module to support "
+ "flags"))
+
+ if auth != [[VIR_CRED_AUTHNAME, VIR_CRED_NOECHOPROMPT],
+ 'root',
+ None]:
+ raise Exception(_("Please extend fake libvirt module to support "
+ "this auth method"))
+
+ return Connection(uri, readonly=False)
diff --git a/nova/tests/test_fakelibvirt.py b/nova/tests/test_fakelibvirt.py
new file mode 100644
index 000000000000..2ceb57888246
--- /dev/null
+++ b/nova/tests/test_fakelibvirt.py
@@ -0,0 +1,403 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2010 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from nova import test
+
+from xml.etree.ElementTree import fromstring as xml_to_tree
+
+import fakelibvirt as libvirt
+
+
+def get_vm_xml(name="testname", uuid=None, source_type='file',
+ interface_type='bridge'):
+ uuid_tag = ''
+ if uuid:
+ uuid_tag = '%s' % (uuid,)
+
+ return '''
+ %(name)s
+%(uuid_tag)s
+ 128000
+ 1
+
+ hvm
+ /somekernel
+ root=/dev/sda
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+''' % {'name': name,
+ 'uuid_tag': uuid_tag,
+ 'source_type': source_type,
+ 'interface_type': interface_type}
+
+
+class FakeLibvirtTests(test.TestCase):
+ def setUp(self):
+ super(FakeLibvirtTests, self).setUp()
+ libvirt._reset()
+
+ def get_openReadOnly_curry_func(self):
+ return lambda uri: libvirt.openReadOnly(uri)
+
+ def get_openAuth_curry_func(self):
+ return lambda uri: libvirt.openAuth(uri,
+ [[libvirt.VIR_CRED_AUTHNAME,
+ libvirt.VIR_CRED_NOECHOPROMPT],
+ 'root',
+ None], 0)
+
+ def _test_connect_method_accepts_None_uri_by_default(self, conn_method):
+ conn = conn_method(None)
+ self.assertNotEqual(conn, None, "Connecting to fake libvirt failed")
+
+ def test_openReadOnly_accepts_None_uri_by_default(self):
+ conn_method = self.get_openReadOnly_curry_func()
+ self._test_connect_method_accepts_None_uri_by_default(conn_method)
+
+ def test_openAuth_accepts_None_uri_by_default(self):
+ conn_method = self.get_openAuth_curry_func()
+ self._test_connect_method_accepts_None_uri_by_default(conn_method)
+
+ def _test_connect_method_can_refuse_None_uri(self, conn_method):
+ libvirt.allow_default_uri_connection = False
+ self.assertRaises(Exception, conn_method, None)
+
+ def test_openReadOnly_can_refuse_None_uri(self):
+ conn_method = self.get_openReadOnly_curry_func()
+ self._test_connect_method_can_refuse_None_uri(conn_method)
+
+ def test_openAuth_can_refuse_None_uri(self):
+ conn_method = self.get_openAuth_curry_func()
+ self._test_connect_method_can_refuse_None_uri(conn_method)
+
+ def _test_connect_method_refuses_invalid_URI(self, conn_method):
+ self.assertRaises(libvirt.libvirtError, conn_method, 'blah')
+
+ def test_openReadOnly_refuses_invalid_URI(self):
+ conn_method = self.get_openReadOnly_curry_func()
+ self._test_connect_method_refuses_invalid_URI(conn_method)
+
+ def test_openAuth_refuses_invalid_URI(self):
+ conn_method = self.get_openAuth_curry_func()
+ self._test_connect_method_refuses_invalid_URI(conn_method)
+
+ def test_getInfo(self):
+ conn = libvirt.openReadOnly(None)
+ res = conn.getInfo()
+ self.assertIn(res[0], ('i686', 'x86_64'))
+ self.assertTrue(1024 <= res[1] <= 16384,
+ "Memory unusually high or low.")
+ self.assertTrue(1 <= res[2] <= 32,
+ "Active CPU count unusually high or low.")
+ self.assertTrue(800 <= res[3] <= 4500,
+ "CPU speed unusually high or low.")
+ self.assertTrue(res[2] <= (res[5] * res[6]),
+ "More active CPUs than num_sockets*cores_per_socket")
+
+ def test_createXML_detects_invalid_xml(self):
+ self._test_XML_func_detects_invalid_xml('createXML', [0])
+
+ def test_defineXML_detects_invalid_xml(self):
+ self._test_XML_func_detects_invalid_xml('defineXML', [])
+
+ def _test_XML_func_detects_invalid_xml(self, xmlfunc_name, args):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ try:
+ getattr(conn, xmlfunc_name)("this is not valid ", *args)
+ except libvirt.libvirtError, e:
+ self.assertEqual(e.get_error_code(), libvirt.VIR_ERR_XML_DETAIL)
+ self.assertEqual(e.get_error_domain(), libvirt.VIR_FROM_DOMAIN)
+ return
+ raise self.failureException("Invalid XML didn't raise libvirtError")
+
+ def test_defineXML_defines_domain(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.defineXML(get_vm_xml())
+ dom = conn.lookupByName('testname')
+ self.assertEqual('testname', dom.name())
+ self.assertEqual(0, dom.isActive())
+ dom.undefine()
+ self.assertRaises(libvirt.libvirtError,
+ conn.lookupByName,
+ 'testname')
+
+ def test_blockStats(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.createXML(get_vm_xml(), 0)
+ dom = conn.lookupByName('testname')
+ blockstats = dom.blockStats('vda')
+ self.assertEqual(len(blockstats), 5)
+ for x in blockstats:
+ self.assertTrue(type(x) in [int, long])
+
+ def test_attach_detach(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.createXML(get_vm_xml(), 0)
+ dom = conn.lookupByName('testname')
+ xml = '''
+
+
+
+ '''
+ self.assertTrue(dom.attachDevice(xml))
+ self.assertTrue(dom.detachDevice(xml))
+
+ def test_info(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.createXML(get_vm_xml(), 0)
+ dom = conn.lookupByName('testname')
+ info = dom.info()
+ self.assertEqual(info[0], libvirt.VIR_DOMAIN_RUNNING)
+ self.assertEqual(info[1], 128000)
+ self.assertTrue(info[2] <= 128000)
+ self.assertEqual(info[3], 1)
+ self.assertTrue(type(info[4]) in [int, long])
+
+ def test_createXML_runs_domain(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.createXML(get_vm_xml(), 0)
+ dom = conn.lookupByName('testname')
+ self.assertEqual('testname', dom.name())
+ self.assertEqual(1, dom.isActive())
+ dom.destroy()
+ try:
+ dom = conn.lookupByName('testname')
+ except libvirt.libvirtError as e:
+ self.assertEqual(e.get_error_code(), libvirt.VIR_ERR_NO_DOMAIN)
+ self.assertEqual(e.get_error_domain(), libvirt.VIR_FROM_QEMU)
+ return
+ self.fail("lookupByName succeeded for destroyed non-defined VM")
+
+ def test_defineXML_remembers_uuid(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ uuid = 'b21f957d-a72f-4b93-b5a5-45b1161abb02'
+ conn.defineXML(get_vm_xml(uuid=uuid))
+ dom = conn.lookupByName('testname')
+ self.assertEquals(dom.UUIDString(), uuid)
+
+ def test_createWithFlags(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.defineXML(get_vm_xml())
+ dom = conn.lookupByName('testname')
+ self.assertFalse(dom.isActive(), 'Defined domain was running.')
+ dom.createWithFlags(0)
+ self.assertTrue(dom.isActive(),
+ 'Domain wasn\'t running after createWithFlags')
+
+ def test_managedSave(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ conn.defineXML(get_vm_xml())
+ dom = conn.lookupByName('testname')
+ self.assertFalse(dom.isActive(), 'Defined domain was running.')
+ dom.createWithFlags(0)
+ self.assertEquals(dom.hasManagedSaveImage(0), 0)
+ dom.managedSave(0)
+ self.assertEquals(dom.hasManagedSaveImage(0), 1)
+ dom.managedSaveRemove(0)
+ self.assertEquals(dom.hasManagedSaveImage(0), 0)
+
+ def test_listDomainsId_and_lookupById(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertEquals(conn.listDomainsID(), [])
+ conn.defineXML(get_vm_xml())
+ dom = conn.lookupByName('testname')
+ dom.createWithFlags(0)
+ self.assertEquals(len(conn.listDomainsID()), 1)
+
+ dom_id = conn.listDomainsID()[0]
+ self.assertEquals(conn.lookupByID(dom_id), dom)
+
+ dom_id = conn.listDomainsID()[0]
+ try:
+ conn.lookupByID(dom_id + 1)
+ except libvirt.libvirtError, e:
+ self.assertEqual(e.get_error_code(), libvirt.VIR_ERR_NO_DOMAIN)
+ self.assertEqual(e.get_error_domain(), libvirt.VIR_FROM_QEMU)
+ return
+ raise self.failureException("Looking up an invalid domain ID didn't "
+ "raise libvirtError")
+
+ def test_define_and_retrieve(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertEquals(conn.listDomainsID(), [])
+ conn.defineXML(get_vm_xml())
+ dom = conn.lookupByName('testname')
+ xml = dom.XMLDesc(0)
+ xml_to_tree(xml)
+
+ def _test_accepts_source_type(self, source_type):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertEquals(conn.listDomainsID(), [])
+ conn.defineXML(get_vm_xml(source_type=source_type))
+ dom = conn.lookupByName('testname')
+ xml = dom.XMLDesc(0)
+ tree = xml_to_tree(xml)
+ elem = tree.find('./devices/disk/source')
+ self.assertEquals(elem.get('file'), '/somefile')
+
+ def test_accepts_source_dev(self):
+ self._test_accepts_source_type('dev')
+
+ def test_accepts_source_path(self):
+ self._test_accepts_source_type('path')
+
+ def test_network_type_bridge_sticks(self):
+ self._test_network_type_sticks('bridge')
+
+ def test_network_type_network_sticks(self):
+ self._test_network_type_sticks('network')
+
+ def _test_network_type_sticks(self, network_type):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertEquals(conn.listDomainsID(), [])
+ conn.defineXML(get_vm_xml(interface_type=network_type))
+ dom = conn.lookupByName('testname')
+ xml = dom.XMLDesc(0)
+ tree = xml_to_tree(xml)
+ elem = tree.find('./devices/interface')
+ self.assertEquals(elem.get('type'), network_type)
+ elem = elem.find('./source')
+ self.assertEquals(elem.get(network_type), 'br100')
+
+ def test_getType(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertEquals(conn.getType(), 'QEMU')
+
+ def test_getVersion(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ self.assertTrue(type(conn.getVersion()) is int)
+
+ def test_getCapabilities(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ xml_to_tree(conn.getCapabilities())
+
+ def test_nwfilter_define_undefine(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+ # Will raise an exception if it's not valid XML
+ xml = '''
+ 946878c6-3ad3-82b2-87f3-c709f3807f58
+ '''
+
+ conn.nwfilterDefineXML(xml)
+ nwfilter = conn.nwfilterLookupByName('nova-instance-instance-789')
+ nwfilter.undefine()
+ try:
+ conn.nwfilterLookupByName('nova-instance-instance-789320334')
+ except libvirt.libvirtError, e:
+ self.assertEqual(e.get_error_code(), libvirt.VIR_ERR_NO_NWFILTER)
+ self.assertEqual(e.get_error_domain(), libvirt.VIR_FROM_NWFILTER)
+ return
+ raise self.failureException("Invalid NWFilter name didn't"
+ " raise libvirtError")
+
+ def test_compareCPU_compatible(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+
+ xml = '''
+ %s
+ %s
+ %s
+
+ ''' % (libvirt.node_arch,
+ libvirt.node_cpu_model,
+ libvirt.node_cpu_vendor,
+ libvirt.node_sockets,
+ libvirt.node_cores,
+ libvirt.node_threads)
+ self.assertEqual(conn.compareCPU(xml, 0),
+ libvirt.VIR_CPU_COMPARE_IDENTICAL)
+
+ def test_compareCPU_incompatible_vendor(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+
+ xml = '''
+ %s
+ %s
+ %s
+
+ ''' % (libvirt.node_arch,
+ libvirt.node_cpu_model,
+ "AnotherVendor",
+ libvirt.node_sockets,
+ libvirt.node_cores,
+ libvirt.node_threads)
+ self.assertEqual(conn.compareCPU(xml, 0),
+ libvirt.VIR_CPU_COMPARE_INCOMPATIBLE)
+
+ def test_compareCPU_incompatible_arch(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+
+ xml = '''
+ %s
+ %s
+ %s
+
+ ''' % ('not-a-valid-arch',
+ libvirt.node_cpu_model,
+ libvirt.node_cpu_vendor,
+ libvirt.node_sockets,
+ libvirt.node_cores,
+ libvirt.node_threads)
+ self.assertEqual(conn.compareCPU(xml, 0),
+ libvirt.VIR_CPU_COMPARE_INCOMPATIBLE)
+
+ def test_compareCPU_incompatible_model(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+
+ xml = '''
+ %s
+ %s
+ %s
+
+ ''' % (libvirt.node_arch,
+ "AnotherModel",
+ libvirt.node_cpu_vendor,
+ libvirt.node_sockets,
+ libvirt.node_cores,
+ libvirt.node_threads)
+ self.assertEqual(conn.compareCPU(xml, 0),
+ libvirt.VIR_CPU_COMPARE_INCOMPATIBLE)
+
+ def test_compareCPU_compatible_unspecified_model(self):
+ conn = self.get_openAuth_curry_func()('qemu:///system')
+
+ xml = '''
+ %s
+ %s
+
+ ''' % (libvirt.node_arch,
+ libvirt.node_cpu_vendor,
+ libvirt.node_sockets,
+ libvirt.node_cores,
+ libvirt.node_threads)
+ self.assertEqual(conn.compareCPU(xml, 0),
+ libvirt.VIR_CPU_COMPARE_IDENTICAL)
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index c1fae1df28c6..f27905ed4032 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -36,12 +36,16 @@ from nova import utils
from nova.api.ec2 import cloud
from nova.compute import power_state
from nova.compute import vm_states
+from nova.virt import disk
+from nova.virt import images
from nova.virt import driver
from nova.virt.libvirt import connection
from nova.virt.libvirt import firewall
from nova.virt.libvirt import volume
from nova.volume import driver as volume_driver
+from nova.virt.libvirt import utils as libvirt_utils
from nova.tests import fake_network
+from nova.tests import fake_libvirt_utils
try:
@@ -187,6 +191,11 @@ class CacheConcurrencyTestCase(test.TestCase):
self.stubs.Set(os.path, 'exists', fake_exists)
self.stubs.Set(utils, 'execute', fake_execute)
+ connection.libvirt_utils = fake_libvirt_utils
+
+ def tearDown(self):
+ connection.libvirt_utils = libvirt_utils
+ super(CacheConcurrencyTestCase, self).tearDown()
def test_same_fname_concurrency(self):
"""Ensures that the same fname cache runs at a sequentially"""
@@ -260,6 +269,11 @@ class LibvirtConnTestCase(test.TestCase):
self.context = context.get_admin_context()
self.flags(instances_path='')
self.call_libvirt_dependant_setup = False
+ connection.libvirt_utils = fake_libvirt_utils
+
+ def tearDown(self):
+ connection.libvirt_utils = libvirt_utils
+ super(LibvirtConnTestCase, self).tearDown()
test_instance = {'memory_kb': '1024000',
'basepath': '/some/path',
@@ -905,10 +919,6 @@ class LibvirtConnTestCase(test.TestCase):
# Preparing mocks
# qemu-img should be mockd since test environment might not have
# large disk space.
- self.mox.StubOutWithMock(utils, "execute")
- utils.execute('qemu-img', 'create', '-f', 'raw',
- '%s/%s/disk' % (tmpdir, instance_ref.name), '10G')
-
self.mox.ReplayAll()
conn = connection.LibvirtConnection(False)
conn.pre_block_migration(self.context, instance_ref,
@@ -937,12 +947,6 @@ class LibvirtConnTestCase(test.TestCase):
""
"")
- ret = ("image: /test/disk\nfile format: raw\n"
- "virtual size: 20G (21474836480 bytes)\ndisk size: 3.1G\n"
- "disk size: 102M\n"
- "cluster_size: 2097152\n"
- "backing file: /test/dummy (actual path: /backing/file)\n")
-
# Preparing mocks
vdmock = self.mox.CreateMock(libvirt.virDomain)
self.mox.StubOutWithMock(vdmock, "XMLDesc")
@@ -953,27 +957,23 @@ class LibvirtConnTestCase(test.TestCase):
return vdmock
self.create_fake_libvirt_mock(lookupByName=fake_lookup)
- self.mox.StubOutWithMock(os.path, "getsize")
- # based on above testdata, one is raw image, so getsize is mocked.
- os.path.getsize("/test/disk").AndReturn(10 * 1024 * 1024 * 1024)
- # another is qcow image, so qemu-img should be mocked.
- self.mox.StubOutWithMock(utils, "execute")
- utils.execute('qemu-img', 'info', '/test/disk.local').\
- AndReturn((ret, ''))
-
+ GB = 1024 * 1024 * 1024
+ fake_libvirt_utils.disk_sizes['/test/disk'] = 10 * GB
+ fake_libvirt_utils.disk_sizes['/test/disk.local'] = 20 * GB
+ fake_libvirt_utils.disk_backing_files['/test/disk.local'] = 'file'
self.mox.ReplayAll()
conn = connection.LibvirtConnection(False)
info = conn.get_instance_disk_info(self.context, instance_ref)
info = utils.loads(info)
- self.assertTrue(info[0]['type'] == 'raw' and
- info[1]['type'] == 'qcow2' and
- info[0]['path'] == '/test/disk' and
- info[1]['path'] == '/test/disk.local' and
- info[0]['local_gb'] == '10G' and
- info[1]['local_gb'] == '20G' and
- info[0]['backing_file'] == "" and
- info[1]['backing_file'] == "file")
+ self.assertEquals(info[0]['type'], 'raw')
+ self.assertEquals(info[1]['type'], 'qcow2')
+ self.assertEquals(info[0]['path'], '/test/disk')
+ self.assertEquals(info[1]['path'], '/test/disk.local')
+ self.assertEquals(info[0]['local_gb'], '10G')
+ self.assertEquals(info[1]['local_gb'], '20G')
+ self.assertEquals(info[0]['backing_file'], "")
+ self.assertEquals(info[1]['backing_file'], "file")
db.instance_destroy(self.context, instance_ref['id'])
@@ -1011,8 +1011,13 @@ class LibvirtConnTestCase(test.TestCase):
', not int'))
count = (0 <= str(e.message).find('Unexpected method call'))
- shutil.rmtree(os.path.join(FLAGS.instances_path, instance.name))
- shutil.rmtree(os.path.join(FLAGS.instances_path, '_base'))
+ path = os.path.join(FLAGS.instances_path, instance.name)
+ if os.path.isdir(path):
+ shutil.rmtree(path)
+
+ path = os.path.join(FLAGS.instances_path, '_base')
+ if os.path.isdir(path):
+ shutil.rmtree(os.path.join(FLAGS.instances_path, '_base'))
def test_get_host_ip_addr(self):
conn = connection.LibvirtConnection(False)
@@ -1055,50 +1060,6 @@ class LibvirtConnTestCase(test.TestCase):
_assert_volume_in_mapping('sdg', False)
_assert_volume_in_mapping('sdh1', False)
- def test_reboot_signature(self):
- """Test that libvirt driver method sig matches interface"""
- def fake_reboot_with_correct_sig(ignore, instance,
- network_info, reboot_type):
- pass
-
- def fake_destroy(instance, network_info, cleanup=False):
- pass
-
- def fake_plug_vifs(instance, network_info):
- pass
-
- def fake_create_new_domain(xml):
- return
-
- def fake_none(self, instance):
- return
-
- instance = db.instance_create(self.context, self.test_instance)
- network_info = _fake_network_info(self.stubs, 1)
-
- self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
- connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
-
- conn = connection.LibvirtConnection(False)
- self.stubs.Set(conn, 'destroy', fake_destroy)
- self.stubs.Set(conn, 'plug_vifs', fake_plug_vifs)
- self.stubs.Set(conn.firewall_driver,
- 'setup_basic_filtering',
- fake_none)
- self.stubs.Set(conn.firewall_driver,
- 'prepare_instance_filter',
- fake_none)
- self.stubs.Set(conn, '_create_new_domain', fake_create_new_domain)
- self.stubs.Set(conn.firewall_driver,
- 'apply_instance_filter',
- fake_none)
-
- args = [instance, network_info, 'SOFT']
- conn.reboot(*args)
-
- compute_driver = driver.ComputeDriver()
- self.assertRaises(NotImplementedError, compute_driver.reboot, *args)
-
@test.skip_if(missing_libvirt(), "Test requires libvirt")
def test_immediate_delete(self):
conn = connection.LibvirtConnection(False)
@@ -1732,3 +1693,181 @@ class NWFilterTestCase(test.TestCase):
self.assertEqual(original_filter_count - len(fakefilter.filters), 2)
db.instance_destroy(admin_ctxt, instance_ref['id'])
+
+
+class LibvirtUtilsTestCase(test.TestCase):
+ def test_create_image(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('qemu-img', 'create', '-f', 'raw',
+ '/some/path', '10G')
+ utils.execute('qemu-img', 'create', '-f', 'qcow2',
+ '/some/stuff', '1234567891234')
+ # Start test
+ self.mox.ReplayAll()
+ libvirt_utils.create_image('raw', '/some/path', '10G')
+ libvirt_utils.create_image('qcow2', '/some/stuff', '1234567891234')
+
+ def test_create_cow_image(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('qemu-img', 'create', '-f', 'qcow2',
+ '-o', 'cluster_size=2M,backing_file=/some/path',
+ '/the/new/cow')
+ # Start test
+ self.mox.ReplayAll()
+ libvirt_utils.create_cow_image('/some/path', '/the/new/cow')
+
+ def test_get_disk_size(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('qemu-img',
+ 'info',
+ '/some/path').AndReturn(('''image: 00000001
+file format: raw
+virtual size: 4.4M (4592640 bytes)
+disk size: 4.4M''', ''))
+
+ # Start test
+ self.mox.ReplayAll()
+ self.assertEquals(libvirt_utils.get_disk_size('/some/path'), 4592640)
+
+ def test_copy_image(self):
+ dst_fd, dst_path = tempfile.mkstemp()
+ try:
+ os.close(dst_fd)
+
+ src_fd, src_path = tempfile.mkstemp()
+ try:
+ with os.fdopen(src_fd, 'w') as fp:
+ fp.write('canary')
+
+ libvirt_utils.copy_image(src_path, dst_path)
+ with open(dst_path, 'r') as fp:
+ self.assertEquals(fp.read(), 'canary')
+ finally:
+ os.unlink(src_path)
+ finally:
+ os.unlink(dst_path)
+
+ def test_mkfs(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('mkfs', '-t', 'ext4', '/my/block/dev')
+ utils.execute('mkswap', '/my/swap/block/dev')
+ self.mox.ReplayAll()
+
+ libvirt_utils.mkfs('ext4', '/my/block/dev')
+ libvirt_utils.mkfs('swap', '/my/swap/block/dev')
+
+ def test_ensure_tree(self):
+ tmpdir = tempfile.mkdtemp()
+ try:
+ testdir = '%s/foo/bar/baz' % (tmpdir,)
+ libvirt_utils.ensure_tree(testdir)
+ self.assertTrue(os.path.isdir(testdir))
+ finally:
+ shutil.rmtree(tmpdir)
+
+ def test_write_to_file(self):
+ dst_fd, dst_path = tempfile.mkstemp()
+ try:
+ os.close(dst_fd)
+
+ libvirt_utils.write_to_file(dst_path, 'hello')
+ with open(dst_path, 'r') as fp:
+ self.assertEquals(fp.read(), 'hello')
+ finally:
+ os.unlink(dst_path)
+
+ def test_write_to_file_with_umask(self):
+ dst_fd, dst_path = tempfile.mkstemp()
+ try:
+ os.close(dst_fd)
+ os.unlink(dst_path)
+
+ libvirt_utils.write_to_file(dst_path, 'hello', umask=0277)
+ with open(dst_path, 'r') as fp:
+ self.assertEquals(fp.read(), 'hello')
+ mode = os.stat(dst_path).st_mode
+ self.assertEquals(mode & 0277, 0)
+ finally:
+ os.unlink(dst_path)
+
+ def test_chown(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('chown', 'soren', '/some/path', run_as_root=True)
+ self.mox.ReplayAll()
+ libvirt_utils.chown('/some/path', 'soren')
+
+ def test_extract_snapshot(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ utils.execute('qemu-img', 'convert', '-f', 'qcow2', '-O', 'raw',
+ '-s', 'snap1', '/path/to/disk/image', '/extracted/snap')
+
+ # Start test
+ self.mox.ReplayAll()
+ libvirt_utils.extract_snapshot('/path/to/disk/image', 'qcow2',
+ 'snap1', '/extracted/snap', 'raw')
+
+ def test_load_file(self):
+ dst_fd, dst_path = tempfile.mkstemp()
+ try:
+ os.close(dst_fd)
+
+ # We have a test for write_to_file. If that is sound, this suffices
+ libvirt_utils.write_to_file(dst_path, 'hello')
+ self.assertEquals(libvirt_utils.load_file(dst_path), 'hello')
+ finally:
+ os.unlink(dst_path)
+
+ def test_file_open(self):
+ dst_fd, dst_path = tempfile.mkstemp()
+ try:
+ os.close(dst_fd)
+
+ # We have a test for write_to_file. If that is sound, this suffices
+ libvirt_utils.write_to_file(dst_path, 'hello')
+ with libvirt_utils.file_open(dst_path, 'r') as fp:
+ self.assertEquals(fp.read(), 'hello')
+ finally:
+ os.unlink(dst_path)
+
+ def test_run_ajaxterm(self):
+ self.mox.StubOutWithMock(utils, 'execute')
+ token = 's3cr3tt0ken'
+ shell_cmd = 'shell-cmd.py'
+ port = 2048
+ utils.execute(mox.IgnoreArg(),
+ '--command', shell_cmd,
+ '-t', token,
+ '-p', port)
+
+ # Start test
+ self.mox.ReplayAll()
+ libvirt_utils.run_ajaxterm(shell_cmd, token, port)
+
+ def test_get_fs_info(self):
+ stdout, stderr = utils.execute('df', '-B1', '/tmp')
+ info_line = ' '.join(stdout.split('\n')[1:])
+ _dev, total, used, free, _percentage, _mntpnt = info_line.split()
+
+ fs_info = libvirt_utils.get_fs_info('/tmp')
+ self.assertEquals(int(total), fs_info['total'])
+ self.assertEquals(int(free), fs_info['free'])
+ self.assertEquals(int(used), fs_info['used'])
+
+ def test_fetch_image(self):
+ self.mox.StubOutWithMock(images, 'fetch')
+ self.mox.StubOutWithMock(disk, 'extend')
+
+ context = 'opaque context'
+ target = '/tmp/targetfile'
+ image_id = '4'
+ user_id = 'fake'
+ project_id = 'fake'
+ images.fetch(context, image_id, target, user_id, project_id)
+ images.fetch(context, image_id, target, user_id, project_id)
+ disk.extend(target, '10G')
+
+ self.mox.ReplayAll()
+ libvirt_utils.fetch_image(context, target, image_id,
+ user_id, project_id)
+ libvirt_utils.fetch_image(context, target, image_id,
+ user_id, project_id, size='10G')
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 1550d64f36bd..137d6c9ca8fa 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -59,6 +59,15 @@ class _VirtDriverTestCase(test.TestCase):
self.ctxt = test_utils.get_test_admin_context()
self.image_service = image.get_default_image_service()
+ def _get_running_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ image_info = test_utils.get_test_image_info(None, instance_ref)
+ self.connection.spawn(self.ctxt, instance=instance_ref,
+ image_meta=image_info,
+ network_info=network_info)
+ return instance_ref, network_info
+
@catch_notimplementederror
def test_init_host(self):
self.connection.init_host('myhostname')
@@ -73,10 +82,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_spawn(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
-
+ instance_ref, network_info = self._get_running_instance()
domains = self.connection.list_instances()
self.assertIn(instance_ref['name'], domains)
@@ -93,18 +99,14 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_snapshot_running(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.snapshot(self.ctxt, instance_ref, img_ref['id'])
@catch_notimplementederror
def test_reboot(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
reboot_type = "SOFT"
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.reboot(instance_ref, network_info, reboot_type)
@catch_notimplementederror
@@ -119,54 +121,40 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_resize_running(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.resize(instance_ref, 7)
@catch_notimplementederror
def test_set_admin_password(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.set_admin_password(instance_ref, 'p4ssw0rd')
@catch_notimplementederror
def test_inject_file(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.inject_file(instance_ref,
base64.b64encode('/testfile'),
base64.b64encode('testcontents'))
@catch_notimplementederror
def test_agent_update(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.agent_update(instance_ref, 'http://www.openstack.org/',
'd41d8cd98f00b204e9800998ecf8427e')
@catch_notimplementederror
def test_rescue(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.rescue(self.ctxt, instance_ref, network_info)
@catch_notimplementederror
def test_unrescue_unrescued_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.unrescue(instance_ref, network_info)
@catch_notimplementederror
def test_unrescue_rescued_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.rescue(self.ctxt, instance_ref, network_info)
self.connection.unrescue(instance_ref, network_info)
@@ -184,53 +172,39 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_migrate_disk_and_power_off(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.migrate_disk_and_power_off(
self.ctxt, instance_ref, 'dest_host')
@catch_notimplementederror
def test_pause(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.pause(instance_ref)
@catch_notimplementederror
def test_unpause_unpaused_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.unpause(instance_ref)
@catch_notimplementederror
def test_unpause_paused_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.pause(instance_ref)
self.connection.unpause(instance_ref)
@catch_notimplementederror
def test_suspend(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.suspend(instance_ref)
@catch_notimplementederror
def test_resume_unsuspended_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.resume(instance_ref)
@catch_notimplementederror
def test_resume_suspended_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.suspend(instance_ref)
self.connection.resume(instance_ref)
@@ -242,9 +216,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_destroy_instance(self):
- instance_ref = test_utils.get_test_instance()
- network_info = test_utils.get_test_network_info()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.assertIn(instance_ref['name'],
self.connection.list_instances())
self.connection.destroy(instance_ref, network_info)
@@ -253,9 +225,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_attach_detach_volume(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.attach_volume({'driver_volume_type': 'fake'},
instance_ref['name'],
'/mnt/nova/something')
@@ -265,9 +235,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_get_info(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
info = self.connection.get_info(instance_ref['name'])
self.assertIn('state', info)
self.assertIn('max_mem', info)
@@ -282,54 +250,40 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_get_diagnostics(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.get_diagnostics(instance_ref['name'])
@catch_notimplementederror
def test_list_disks(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.list_disks(instance_ref['name'])
@catch_notimplementederror
def test_list_interfaces(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.list_interfaces(instance_ref['name'])
@catch_notimplementederror
def test_block_stats(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
stats = self.connection.block_stats(instance_ref['name'], 'someid')
self.assertEquals(len(stats), 5)
@catch_notimplementederror
def test_interface_stats(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
stats = self.connection.interface_stats(instance_ref['name'], 'someid')
self.assertEquals(len(stats), 8)
@catch_notimplementederror
def test_get_console_output(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
console_output = self.connection.get_console_output(instance_ref)
self.assertTrue(isinstance(console_output, basestring))
@catch_notimplementederror
def test_get_ajax_console(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
ajax_console = self.connection.get_ajax_console(instance_ref)
self.assertIn('token', ajax_console)
self.assertIn('host', ajax_console)
@@ -337,9 +291,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_get_vnc_console(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
vnc_console = self.connection.get_vnc_console(instance_ref)
self.assertIn('token', vnc_console)
self.assertIn('host', vnc_console)
@@ -347,9 +299,7 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_get_console_pool_info(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
console_pool = self.connection.get_console_pool_info(instance_ref)
self.assertIn('address', console_pool)
self.assertIn('username', console_pool)
@@ -357,25 +307,19 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_refresh_security_group_rules(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
# FIXME: Create security group and add the instance to it
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.refresh_security_group_rules(1)
@catch_notimplementederror
def test_refresh_security_group_members(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
# FIXME: Create security group and add the instance to it
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.refresh_security_group_members(1)
@catch_notimplementederror
def test_refresh_provider_fw_rules(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.refresh_provider_fw_rules()
@catch_notimplementederror
@@ -424,28 +368,16 @@ class _VirtDriverTestCase(test.TestCase):
@catch_notimplementederror
def test_live_migration(self):
- network_info = test_utils.get_test_network_info()
- instance_ref = test_utils.get_test_instance()
- self.connection.spawn(self.ctxt, instance_ref, network_info)
+ instance_ref, network_info = self._get_running_instance()
self.connection.live_migration(self.ctxt, instance_ref, 'otherhost',
None, None)
@catch_notimplementederror
def _check_host_status_fields(self, host_status):
- self.assertIn('host_name-description', host_status)
- self.assertIn('host_hostname', host_status)
- self.assertIn('host_memory_total', host_status)
- self.assertIn('host_memory_overhead', host_status)
- self.assertIn('host_memory_free', host_status)
- self.assertIn('host_memory_free_computed', host_status)
- self.assertIn('host_other_config', host_status)
- self.assertIn('host_ip_address', host_status)
- self.assertIn('host_cpu_info', host_status)
- self.assertIn('disk_available', host_status)
self.assertIn('disk_total', host_status)
self.assertIn('disk_used', host_status)
- self.assertIn('host_uuid', host_status)
- self.assertIn('host_name_label', host_status)
+ self.assertIn('host_memory_total', host_status)
+ self.assertIn('host_memory_free', host_status)
@catch_notimplementederror
def test_update_host_status(self):
@@ -493,7 +425,42 @@ class FakeConnectionTestCase(_VirtDriverTestCase):
self.driver_module = nova.virt.fake
super(FakeConnectionTestCase, self).setUp()
-# Before long, we'll add the real hypervisor drivers here as well
-# with whatever instrumentation they need to work independently of
-# their hypervisor. This way, we can verify that they all act the
-# same.
+
+class LibvirtConnTestCase(_VirtDriverTestCase):
+ def setUp(self):
+ # Put fakelibvirt in place
+ if 'libvirt' in sys.modules:
+ self.saved_libvirt = sys.modules['libvirt']
+ else:
+ self.saved_libvirt = None
+
+ import fakelibvirt
+ import fake_libvirt_utils
+
+ sys.modules['libvirt'] = fakelibvirt
+
+ import nova.virt.libvirt.connection
+ import nova.virt.libvirt.firewall
+
+ nova.virt.libvirt.connection.libvirt = fakelibvirt
+ nova.virt.libvirt.connection.libvirt_utils = fake_libvirt_utils
+ nova.virt.libvirt.firewall.libvirt = fakelibvirt
+
+ # Point _VirtDriverTestCase at the right module
+ self.driver_module = nova.virt.libvirt.connection
+ super(LibvirtConnTestCase, self).setUp()
+ FLAGS.rescue_image_id = "2"
+ FLAGS.rescue_kernel_id = "3"
+ FLAGS.rescue_ramdisk_id = None
+
+ def tearDown(self):
+ super(LibvirtConnTestCase, self).setUp()
+
+ # Restore libvirt
+ import nova.virt.libvirt.connection
+ import nova.virt.libvirt.firewall
+ if self.saved_libvirt:
+ sys.modules['libvirt'] = self.saved_libvirt
+ nova.virt.libvirt.connection.libvirt = self.saved_libvirt
+ nova.virt.libvirt.connection.libvirt_utils = self.saved_libvirt
+ nova.virt.libvirt.firewall.libvirt = self.saved_libvirt
diff --git a/nova/tests/utils.py b/nova/tests/utils.py
index e0cacadb4d93..e5de01275b8d 100644
--- a/nova/tests/utils.py
+++ b/nova/tests/utils.py
@@ -25,6 +25,15 @@ def get_test_admin_context():
return nova.context.get_admin_context()
+def get_test_image_info(context, instance_ref):
+ if not context:
+ context = get_test_admin_context()
+
+ image_ref = instance_ref['image_ref']
+ image_service, image_id = nova.image.get_image_service(context, image_ref)
+ return image_service.show(context, image_id)
+
+
def get_test_instance(context=None):
if not context:
context = get_test_admin_context()
@@ -35,7 +44,7 @@ def get_test_instance(context=None):
'vcpus': 2,
'project_id': 'fake',
'bridge': 'br101',
- 'image_ref': '1',
+ 'image_ref': 'cedef40a-ed67-4d10-800e-17455edce175',
'instance_type_id': '5'} # m1.small
instance_ref = nova.db.instance_create(context, test_instance)
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 71ca21d1be3f..5e9be4ef9109 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -72,7 +72,7 @@ from nova.compute import power_state
from nova.virt import disk
from nova.virt import driver
from nova.virt import images
-from nova.virt.libvirt import netutils
+from nova.virt.libvirt import utils as libvirt_utils
libvirt = None
@@ -138,6 +138,7 @@ flags.DEFINE_string('libvirt_vif_driver',
flags.DEFINE_list('libvirt_volume_drivers',
['iscsi=nova.virt.libvirt.volume.LibvirtISCSIVolumeDriver',
'local=nova.virt.libvirt.volume.LibvirtVolumeDriver',
+ 'fake=nova.virt.libvirt.volume.LibvirtFakeVolumeDriver',
'rdb=nova.virt.libvirt.volume.LibvirtNetVolumeDriver',
'sheepdog=nova.virt.libvirt.volume.LibvirtNetVolumeDriver'],
'Libvirt handlers for remote volumes.')
@@ -187,6 +188,7 @@ class LibvirtConnection(driver.ComputeDriver):
self.libvirt_xml = open(FLAGS.libvirt_xml_template).read()
self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read()
+ self._host_state = None
self._wrapped_conn = None
self.read_only = read_only
@@ -445,7 +447,10 @@ class LibvirtConnection(driver.ComputeDriver):
This command only works with qemu 0.14+
"""
- virt_dom = self._lookup_by_name(instance['name'])
+ try:
+ virt_dom = self._lookup_by_name(instance['name'])
+ except exception.InstanceNotFound:
+ raise exception.InstanceNotRunning()
(image_service, image_id) = nova.image.get_image_service(
context, instance['image_ref'])
@@ -504,20 +509,11 @@ class LibvirtConnection(driver.ComputeDriver):
temp_dir = tempfile.mkdtemp()
try:
out_path = os.path.join(temp_dir, snapshot_name)
- qemu_img_cmd = ('qemu-img',
- 'convert',
- '-f',
- source_format,
- '-O',
- image_format,
- '-s',
- snapshot_name,
- disk_path,
- out_path)
- utils.execute(*qemu_img_cmd)
-
+ libvirt_utils.extract_snapshot(disk_path, source_format,
+ snapshot_name, out_path,
+ image_format)
# Upload that image to the image service
- with open(out_path) as image_file:
+ with libvirt_utils.file_open(out_path) as image_file:
image_service.update(context,
image_href,
metadata,
@@ -536,6 +532,7 @@ class LibvirtConnection(driver.ComputeDriver):
reboot happens, as the guest OS cannot ignore this action.
"""
+
virt_dom = self._conn.lookupByName(instance['name'])
# NOTE(itoumsn): Use XML delived from the running instance
# instead of using to_xml(instance, network_info). This is almost
@@ -612,9 +609,7 @@ class LibvirtConnection(driver.ComputeDriver):
unrescue_xml_path = os.path.join(FLAGS.instances_path,
instance['name'],
'unrescue.xml')
- f = open(unrescue_xml_path, 'w')
- f.write(unrescue_xml)
- f.close()
+ libvirt_utils.write_to_file(unrescue_xml_path, unrescue_xml)
xml = self.to_xml(instance, network_info, rescue=True)
rescue_images = {
@@ -637,10 +632,8 @@ class LibvirtConnection(driver.ComputeDriver):
unrescue_xml_path = os.path.join(FLAGS.instances_path,
instance['name'],
'unrescue.xml')
- f = open(unrescue_xml_path)
- unrescue_xml = f.read()
- f.close()
- os.remove(unrescue_xml_path)
+ unrescue_xml = libvirt_utils.load_file(unrescue_xml_path)
+ libvirt_utils.file_delete(unrescue_xml_path)
self.reboot(instance, network_info, xml=unrescue_xml)
@exception.wrap_exception()
@@ -711,18 +704,12 @@ class LibvirtConnection(driver.ComputeDriver):
fp.write(data)
return fpath
- def _dump_file(self, fpath):
- fp = open(fpath, 'r+')
- contents = fp.read()
- LOG.info(_('Contents of file %(fpath)s: %(contents)r') % locals())
- return contents
-
@exception.wrap_exception()
def get_console_output(self, instance):
console_log = os.path.join(FLAGS.instances_path, instance['name'],
'console.log')
- utils.execute('chown', os.getuid(), console_log, run_as_root=True)
+ libvirt_utils.chown(console_log, os.getuid())
if FLAGS.libvirt_type == 'xen':
# Xen is special
@@ -736,23 +723,10 @@ class LibvirtConnection(driver.ComputeDriver):
else:
fpath = console_log
- return self._dump_file(fpath)
+ return libvirt_utils.load_file(fpath)
@exception.wrap_exception()
def get_ajax_console(self, instance):
- def get_open_port():
- start_port, end_port = FLAGS.ajaxterm_portrange.split("-")
- for i in xrange(0, 100): # don't loop forever
- port = random.randint(int(start_port), int(end_port))
- # netcat will exit with 0 only if the port is in use,
- # so a nonzero return value implies it is unused
- cmd = 'netcat', '0.0.0.0', port, '-w', '1'
- try:
- stdout, stderr = utils.execute(*cmd, process_input='')
- except exception.ProcessExecutionError:
- return port
- raise Exception(_('Unable to find an open port'))
-
def get_pty_for_instance(instance_name):
virt_dom = self._lookup_by_name(instance_name)
xml = virt_dom.XMLDesc(0)
@@ -763,17 +737,15 @@ class LibvirtConnection(driver.ComputeDriver):
source = serial.getElementsByTagName('source')[0]
return source.getAttribute('path')
- port = get_open_port()
+ start_port, end_port = FLAGS.ajaxterm_portrange.split("-")
+ port = libvirt_utils.get_open_port(int(start_port), int(end_port))
token = str(uuid.uuid4())
host = instance['host']
ajaxterm_cmd = 'sudo netcat - %s' \
% get_pty_for_instance(instance['name'])
- cmd = ['%s/tools/ajaxterm/ajaxterm.py' % utils.novadir(),
- '--command', ajaxterm_cmd, '-t', token, '-p', port]
-
- utils.execute(cmd)
+ libvirt_utils.run_ajaxterm(ajaxterm_cmd, token, port)
return {'token': token, 'host': host, 'port': port}
def get_host_ip_addr(self):
@@ -816,7 +788,7 @@ class LibvirtConnection(driver.ComputeDriver):
if not os.path.exists(target):
base_dir = os.path.join(FLAGS.instances_path, '_base')
if not os.path.exists(base_dir):
- os.mkdir(base_dir)
+ libvirt_utils.ensure_tree(base_dir)
base = os.path.join(base_dir, fname)
@utils.synchronized(fname)
@@ -827,11 +799,9 @@ class LibvirtConnection(driver.ComputeDriver):
call_if_not_exists(base, fn, *args, **kwargs)
if cow:
- utils.execute('qemu-img', 'create', '-f', 'qcow2', '-o',
- 'cluster_size=2M,backing_file=%s' % base,
- target)
+ libvirt_utils.create_cow_image(base, target)
else:
- utils.execute('cp', base, target)
+ libvirt_utils.copy_image(base, target)
def _fetch_image(self, context, target, image_id, user_id, project_id,
size=None):
@@ -846,9 +816,10 @@ class LibvirtConnection(driver.ComputeDriver):
if not fs_format:
fs_format = FLAGS.default_local_format
- utils.execute('truncate', target, '-s', "%d%c" % (local_size, unit))
+ libvirt_utils.create_image('raw', target,
+ '%d%c' % (local_size, unit))
if fs_format:
- utils.execute('mkfs', '-t', fs_format, target)
+ libvirt_utils.mkfs(fs_format, target)
def _create_ephemeral(self, target, local_size, fs_label, os_type):
self._create_local(target, local_size)
@@ -856,8 +827,8 @@ class LibvirtConnection(driver.ComputeDriver):
def _create_swap(self, target, swap_mb):
"""Create a swap file of specified size"""
- self._create_local(target, swap_mb, unit='M')
- utils.execute('mkswap', target)
+ libvirt_utils.create_image('raw', target, '%dM' % swap_mb)
+ libvirt_utils.mkfs(target, 'swap')
def _create_image(self, context, inst, libvirt_xml, suffix='',
disk_images=None, network_info=None,
@@ -872,22 +843,17 @@ class LibvirtConnection(driver.ComputeDriver):
fname + suffix)
# ensure directories exist and are writable
- utils.execute('mkdir', '-p', basepath(suffix=''))
+ libvirt_utils.ensure_tree(basepath(suffix=''))
LOG.info(_('instance %s: Creating image'), inst['name'])
- f = open(basepath('libvirt.xml'), 'w')
- f.write(libvirt_xml)
- f.close()
+ libvirt_utils.write_to_file(basepath('libvirt.xml'), libvirt_xml)
if FLAGS.libvirt_type == 'lxc':
container_dir = '%s/rootfs' % basepath(suffix='')
- utils.execute('mkdir', '-p', container_dir)
+ libvirt_utils.ensure_tree(container_dir)
# NOTE(vish): No need add the suffix to console.log
- console_log = basepath('console.log', '')
- if os.path.exists(console_log):
- utils.execute('chown', os.getuid(), console_log, run_as_root=True)
- os.close(os.open(console_log, os.O_CREAT | os.O_WRONLY, 0660))
+ libvirt_utils.write_to_file(basepath('console.log', ''), '', 007)
if not disk_images:
disk_images = {'image_id': inst['image_ref'],
@@ -896,7 +862,7 @@ class LibvirtConnection(driver.ComputeDriver):
if disk_images['kernel_id']:
fname = disk_images['kernel_id']
- self._cache_image(fn=self._fetch_image,
+ self._cache_image(fn=libvirt_utils.fetch_image,
context=context,
target=basepath('kernel'),
fname=fname,
@@ -905,7 +871,7 @@ class LibvirtConnection(driver.ComputeDriver):
project_id=inst['project_id'])
if disk_images['ramdisk_id']:
fname = disk_images['ramdisk_id']
- self._cache_image(fn=self._fetch_image,
+ self._cache_image(fn=libvirt_utils.fetch_image,
context=context,
target=basepath('ramdisk'),
fname=fname,
@@ -924,7 +890,7 @@ class LibvirtConnection(driver.ComputeDriver):
if not self._volume_in_mapping(self.default_root_device,
block_device_info):
- self._cache_image(fn=self._fetch_image,
+ self._cache_image(fn=libvirt_utils.fetch_image,
context=context,
target=basepath('disk'),
fname=root_fname,
@@ -990,7 +956,7 @@ class LibvirtConnection(driver.ComputeDriver):
if config_drive_id:
fname = config_drive_id
- self._cache_image(fn=self._fetch_image,
+ self._cache_image(fn=libvirt_utils.fetch_image,
target=basepath('disk.config'),
fname=fname,
image_id=config_drive_id,
@@ -1078,7 +1044,7 @@ class LibvirtConnection(driver.ComputeDriver):
nbd=FLAGS.use_cow_images)
if FLAGS.libvirt_type == 'uml':
- utils.execute('chown', 'root', basepath('disk'), run_as_root=True)
+ libvirt_utils.chown(basepath('disk'), 'root')
def _volume_in_mapping(self, mount_device, block_device_info):
block_device_list = [block_device.strip_dev(vol['mount_device'])
@@ -1267,10 +1233,6 @@ class LibvirtConnection(driver.ComputeDriver):
return domain
- def get_diagnostics(self, instance_name):
- raise exception.ApiError(_("diagnostics are not supported "
- "for libvirt"))
-
def get_disks(self, instance_name):
"""
Note that this function takes an instance name.
@@ -1378,8 +1340,8 @@ class LibvirtConnection(driver.ComputeDriver):
"""
- hddinfo = os.statvfs(FLAGS.instances_path)
- return hddinfo.f_frsize * hddinfo.f_blocks / 1024 / 1024 / 1024
+ stats = libvirt_utils.get_fs_info(FLAGS.instances_path)
+ return stats['total'] / (1024 ** 3)
def get_vcpu_used(self):
""" Get vcpu usage number of physical computer.
@@ -1421,9 +1383,8 @@ class LibvirtConnection(driver.ComputeDriver):
"""
- hddinfo = os.statvfs(FLAGS.instances_path)
- avail = hddinfo.f_frsize * hddinfo.f_bavail / 1024 / 1024 / 1024
- return self.get_local_gb_total() - avail
+ stats = libvirt_utils.get_fs_info(FLAGS.instances_path)
+ return stats['used'] / (1024 ** 3)
def get_hypervisor_type(self):
"""Get hypervisor type.
@@ -1775,9 +1736,8 @@ class LibvirtConnection(driver.ComputeDriver):
# create backing file in case of qcow2.
instance_disk = os.path.join(instance_dir, base)
if not info['backing_file']:
- utils.execute('qemu-img', 'create', '-f', info['type'],
- instance_disk, info['local_gb'])
-
+ libvirt_utils.create_image(info['type'], instance_disk,
+ info['local_gb'])
else:
# Creating backing file follows same way as spawning instances.
backing_file = os.path.join(FLAGS.instances_path,
@@ -1794,9 +1754,7 @@ class LibvirtConnection(driver.ComputeDriver):
project_id=instance_ref['project_id'],
size=instance_ref['local_gb'])
- utils.execute('qemu-img', 'create', '-f', info['type'],
- '-o', 'backing_file=%s' % backing_file,
- instance_disk, info['local_gb'])
+ libvirt_utils.create_cow_image(backing_file, instance_disk)
# if image has kernel and ramdisk, just download
# following normal way.
@@ -1804,13 +1762,13 @@ class LibvirtConnection(driver.ComputeDriver):
user = manager.AuthManager().get_user(instance_ref['user_id'])
project = manager.AuthManager().get_project(
instance_ref['project_id'])
- self._fetch_image(nova_context.get_admin_context(),
+ libvirt_utils.fetch_image(nova_context.get_admin_context(),
os.path.join(instance_dir, 'kernel'),
instance_ref['kernel_id'],
user,
project)
if instance_ref['ramdisk_id']:
- self._fetch_image(nova_context.get_admin_context(),
+ libvirt_utils.fetch_image(nova_context.get_admin_context(),
os.path.join(instance_dir, 'ramdisk'),
instance_ref['ramdisk_id'],
user,
@@ -1879,18 +1837,11 @@ class LibvirtConnection(driver.ComputeDriver):
continue
disk_type = driver_nodes[cnt].get('type')
+ size = libvirt_utils.get_disk_size(path)
if disk_type == 'raw':
- size = int(os.path.getsize(path))
backing_file = ""
else:
- out, err = utils.execute('qemu-img', 'info', path)
- size = [i.split('(')[1].split()[0] for i in out.split('\n')
- if i.strip().find('virtual size') >= 0]
- size = int(size[0])
-
- backing_file = [i.split('actual path:')[1].strip()[:-1]
- for i in out.split('\n') if 0 <= i.find('backing file')]
- backing_file = os.path.basename(backing_file[0])
+ backing_file = libvirt_utils.get_backing_file(path)
# block migration needs same/larger size of empty image on the
# destination host. since qemu-img creates bit smaller size image
@@ -1932,7 +1883,7 @@ class LibvirtConnection(driver.ComputeDriver):
def host_power_action(self, host, action):
"""Reboots, shuts down or powers up the host."""
- pass
+ raise NotImplementedError()
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
@@ -1975,3 +1926,5 @@ class HostState(object):
data["hypervisor_version"] = self.connection.get_hypervisor_version()
self._stats = data
+
+ return data
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
new file mode 100644
index 000000000000..e528fe79d6d5
--- /dev/null
+++ b/nova/virt/libvirt/utils.py
@@ -0,0 +1,257 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# Copyright (c) 2010 Citrix Systems, Inc.
+# Copyright (c) 2011 Piston Cloud Computing, Inc
+# Copyright (c) 2011 OpenStack LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+import random
+import shutil
+
+from nova import exception
+from nova import flags
+from nova import utils
+from nova.virt import disk
+from nova.virt import images
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('qemu_img', 'qemu-img',
+ 'binary to use for qemu-img commands')
+
+
+def execute(*args, **kwargs):
+ return utils.execute(*args, **kwargs)
+
+
+def create_image(disk_format, path, size):
+ """Create a disk image
+
+ :param disk_format: Disk image format (as known by qemu-img)
+ :param path: Desired location of the disk image
+ :param size: Desired size of disk image. May be given as an int or
+ a string. If given as an int, it will be interpreted
+ as bytes. If it's a string, it should consist of a number
+ followed by an optional prefix ('k' for kilobytes, 'm'
+ for megabytes, 'g' for gigabytes, 't' for terabytes). If no
+ prefix is given, it will be interpreted as bytes.
+ """
+ execute(FLAGS.qemu_img, 'create', '-f', disk_format, path, size)
+
+
+def create_cow_image(backing_file, path):
+ """Create COW image
+
+ Creates a COW image with the given backing file
+
+ :param backing_file: Existing image on which to base the COW image
+ :param path: Desired location of the COW image
+ """
+ execute(FLAGS.qemu_img, 'create', '-f', 'qcow2', '-o',
+ 'cluster_size=2M,backing_file=%s' % backing_file, path)
+
+
+def get_disk_size(path):
+ """Get the (virtual) size of a disk image
+
+ :param path: Path to the disk image
+ :returns: Size (in bytes) of the given disk image as it would be seen
+ by a virtual machine.
+ """
+ out, err = execute(FLAGS.qemu_img, 'info', path)
+ size = [i.split('(')[1].split()[0] for i in out.split('\n')
+ if i.strip().find('virtual size') >= 0]
+ return int(size[0])
+
+
+def get_disk_backing_file(path):
+ """Get the backing file of a disk image
+
+ :param path: Path to the disk image
+ :returns: a path to the image's backing store
+ """
+ out, err = execute(FLAGS.qemu_img, 'info', path)
+ backing_file = [i.split('actual path:')[1].strip()[:-1]
+ for i in out.split('\n') if 0 <= i.find('backing file')]
+ backing_file = os.path.basename(backing_file[0])
+ return backing_file
+
+
+def copy_image(src, dest):
+ """Copy a disk image
+
+ :param src: Source image
+ :param dest: Destination path
+ """
+ shutil.copyfile(src, dest)
+
+
+def mkfs(fs, path):
+ """Format a file or block device
+
+ :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
+ 'btrfs', etc.)
+ :param path: Path to file or block device to format
+ """
+ if fs == 'swap':
+ execute('mkswap', path)
+ else:
+ execute('mkfs', '-t', fs, path)
+
+
+def ensure_tree(path):
+ """Create a directory (and any ancestor directories required)
+
+ :param path: Directory to create
+ """
+ execute('mkdir', '-p', path)
+
+
+def write_to_file(path, contents, umask=None):
+ """Write the given contents to a file
+
+ :param path: Destination file
+ :param contents: Desired contents of the file
+ :param umask: Umask to set when creating this file (will be reset)
+ """
+ if umask:
+ saved_umask = os.umask(umask)
+
+ try:
+ with open(path, 'w') as f:
+ f.write(contents)
+ finally:
+ if umask:
+ os.umask(saved_umask)
+
+
+def chown(path, owner):
+ """Change ownership of file or directory
+
+ :param path: File or directory whose ownership to change
+ :param owner: Desired new owner (given as uid or username)
+ """
+ utils.execute('chown', owner, path, run_as_root=True)
+
+
+def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt):
+ """Extract a named snapshot from a disk image
+
+ :param disk_path: Path to disk image
+ :param snapshot_name: Name of snapshot in disk image
+ :param out_path: Desired path of extracted snapshot
+ """
+ qemu_img_cmd = (FLAGS.qemu_img,
+ 'convert',
+ '-f',
+ source_fmt,
+ '-O',
+ dest_fmt,
+ '-s',
+ snapshot_name,
+ disk_path,
+ out_path)
+ execute(*qemu_img_cmd)
+
+
+def load_file(path):
+ """Read contents of file
+
+ :param path: File to read
+ """
+ with open(path, 'r+') as fp:
+ return fp.read()
+
+
+def file_open(*args, **kwargs):
+ """Open file
+
+ see built-in file() documentation for more details
+
+ Note: The reason this is kept in a separate module is to easily
+ be able to provide a stub module that doesn't alter system
+ state at all (for unit tests)
+ """
+ return file(*args, **kwargs)
+
+
+def file_delete(path):
+ """Delete (unlink) file
+
+ Note: The reason this is kept in a separate module is to easily
+ be able to provide a stub module that doesn't alter system
+ state at all (for unit tests)
+ """
+ return os.unlink(path)
+
+
+def get_open_port(start_port, end_port):
+ """Find an available port
+
+ :param start_port: Start of acceptable port range
+ :param end_port: End of acceptable port range
+ """
+ for i in xrange(0, 100): # don't loop forever
+ port = random.randint(start_port, end_port)
+ # netcat will exit with 0 only if the port is in use,
+ # so a nonzero return value implies it is unused
+ cmd = 'netcat', '0.0.0.0', port, '-w', '1'
+ try:
+ stdout, stderr = execute(*cmd, process_input='')
+ except exception.ProcessExecutionError:
+ return port
+ raise Exception(_('Unable to find an open port'))
+
+
+def run_ajaxterm(cmd, token, port):
+ """Run ajaxterm
+
+ :param cmd: Command to connect to
+ :param token: Token to require for authentication
+ :param port: Port to run on
+ """
+ cmd = ['%s/tools/ajaxterm/ajaxterm.py' % utils.novadir(),
+ '--command', cmd, '-t', token, '-p', port]
+ execute(*cmd)
+
+
+def get_fs_info(path):
+ """Get free/used/total space info for a filesystem
+
+ :param path: Any dirent on the filesystem
+ :returns: A dict containing:
+
+ :free: How much space is free (in bytes)
+ :used: How much space is used (in bytes)
+ :total: How big the filesystem is (in bytes)
+ """
+ hddinfo = os.statvfs(path)
+ total = hddinfo.f_frsize * hddinfo.f_blocks
+ free = hddinfo.f_frsize * hddinfo.f_bavail
+ used = hddinfo.f_frsize * (hddinfo.f_blocks - hddinfo.f_bfree)
+ return {'total': total,
+ 'free': free,
+ 'used': used}
+
+
+def fetch_image(context, target, image_id, user_id, project_id,
+ size=None):
+ """Grab image and optionally attempt to resize it"""
+ images.fetch(context, image_id, target, user_id, project_id)
+ if size:
+ disk.extend(target, size)
diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py
index a890dadad008..ff5bc1fe5681 100644
--- a/nova/virt/libvirt/volume.py
+++ b/nova/virt/libvirt/volume.py
@@ -55,6 +55,20 @@ class LibvirtVolumeDriver(object):
pass
+class LibvirtFakeVolumeDriver(LibvirtVolumeDriver):
+ """Driver to attach Network volumes to libvirt."""
+
+ def connect_volume(self, connection_info, mount_device):
+ protocol = 'fake'
+ name = 'fake'
+ xml = """
+
+
+
+ """ % (protocol, name, mount_device)
+ return xml
+
+
class LibvirtNetVolumeDriver(LibvirtVolumeDriver):
"""Driver to attach Network volumes to libvirt."""