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 + + + + + + + + + + +