From 1b508d80dd76810f6183df50b9d9b324831875be Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 16 Feb 2011 12:01:22 +0000 Subject: First commit for xenapi-vlan-networking. Totally untested --- nova/network/manager.py | 1 + nova/virt/xenapi/network_utils.py | 17 +++++++++++++++-- nova/virt/xenapi/vm_utils.py | 1 + nova/virt/xenapi/vmops.py | 15 +++++++++++++-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 8eb9f041b..6ba0f2e18 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -499,6 +499,7 @@ class VlanManager(NetworkManager): def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) + #TODO: the xenapi driver will create a xenserver network if necessary here self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py index c0406d8f0..4c2a81260 100644 --- a/nova/virt/xenapi/network_utils.py +++ b/nova/virt/xenapi/network_utils.py @@ -28,7 +28,20 @@ class NetworkHelper(HelperBase): """ The class that wraps the helper methods together. """ - + + + @classmethod + def find_network_with_name_label(cls,session,name_label): + networks = session.call_xenapi('network.get_by_name_label', name_label) + if len(networks) == 1: + return networks.keys()[0] + elif len(networks) > 1: + raise Exception(_('Found non-unique network' + ' for name_label %s') % name_label) + else: + return None + + @classmethod def find_network_with_bridge(cls, session, bridge): """Return the network on which the bridge is attached, if found.""" @@ -40,4 +53,4 @@ class NetworkHelper(HelperBase): raise Exception(_('Found non-unique network' ' for bridge %s') % bridge) else: - raise Exception(_('Found no network for bridge %s') % bridge) + raise Exception(_('Found no network for bridge %s') % bridge) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index f5c19099a..76257946e 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -204,6 +204,7 @@ class VMHelper(HelperBase): VIF reference.""" vif_rec = {} vif_rec['device'] = '0' + #network_ref should be the appropriate reference (network with VLAN) vif_rec['network'] = network_ref vif_rec['VM'] = vm_ref vif_rec['MAC'] = mac_address diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index fe95d881b..b5c5e082e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -67,10 +67,21 @@ class VMOps(object): raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) + #this will return the bridge name in nova db bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] - network_ref = \ - NetworkHelper.find_network_with_bridge(self._session, bridge) + + #this will return the appropriate network + #TODO: avoid unnecessary call to find_network_with_bridge + #when VLAN manager is being used (and not just use an if) + network_ref = None + try: + network_ref = \ + NetworkHelper.find_network_with_bridge(self._session, bridge) + except: + #try to get name with name_label + network_ref = \ + NetworkHelper.find_network_with_name_label(self._session,bridge) user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) -- cgit From 01e340f98765cc434624b3b4da49447f950f07ae Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 16 Feb 2011 17:16:31 +0000 Subject: First commit of working code --- nova/network/linux_net.py | 1 + nova/virt/xenapi/network_utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c1cbff7d8..f5efac0ae 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -291,6 +291,7 @@ def update_dhcp(context, network_id): if a dnsmasq instance is already running then send a HUP signal causing it to reload, otherwise spawn a new instance """ + LOG.debug("ENTERING update_dhcp - DHCP script:%s",FLAGS.dhcpbridge) network_ref = db.network_get(context, network_id) conffile = _dhcp_file(network_ref['bridge'], 'conf') diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py index 4c2a81260..8f7806e6c 100644 --- a/nova/virt/xenapi/network_utils.py +++ b/nova/virt/xenapi/network_utils.py @@ -34,7 +34,7 @@ class NetworkHelper(HelperBase): def find_network_with_name_label(cls,session,name_label): networks = session.call_xenapi('network.get_by_name_label', name_label) if len(networks) == 1: - return networks.keys()[0] + return networks[0] elif len(networks) > 1: raise Exception(_('Found non-unique network' ' for name_label %s') % name_label) -- cgit From 441beee908d2534c4fa1d85523dbc87770efeb17 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Thu, 17 Feb 2011 16:54:42 +0000 Subject: Supporting networks with multiple PIFs. pep8 fixes unit tests passed --- nova/network/linux_net.py | 1 - nova/network/manager.py | 2 +- nova/network/xenapi_net.py | 130 ++++++++++++++++++++++++++++++++++++++ nova/virt/xenapi/network_utils.py | 12 ++-- nova/virt/xenapi/vmops.py | 11 ++-- 5 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 nova/network/xenapi_net.py diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index f5efac0ae..c1cbff7d8 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -291,7 +291,6 @@ def update_dhcp(context, network_id): if a dnsmasq instance is already running then send a HUP signal causing it to reload, otherwise spawn a new instance """ - LOG.debug("ENTERING update_dhcp - DHCP script:%s",FLAGS.dhcpbridge) network_ref = db.network_get(context, network_id) conffile = _dhcp_file(network_ref['bridge'], 'conf') diff --git a/nova/network/manager.py b/nova/network/manager.py index 6ba0f2e18..85209e69f 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -499,7 +499,7 @@ class VlanManager(NetworkManager): def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) - #TODO: the xenapi driver will create a xenserver network if necessary here + #xenapi driver will create a xen network if necessary here self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py new file mode 100644 index 000000000..289dbd71f --- /dev/null +++ b/nova/network/xenapi_net.py @@ -0,0 +1,130 @@ +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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. +""" +Implements vlans, bridges, and iptables rules using linux utilities. +""" + +import os + +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import utils +from nova.virt.xenapi_conn import XenAPISession +from nova.virt.xenapi.network_utils import NetworkHelper + +LOG = logging.getLogger("nova.xenapi_net") + +FLAGS = flags.FLAGS +flags.DEFINE_string('vlan_interface', 'eth0', + 'network device for vlans') + + +def metadata_forward(): + pass + + +def init_host(): + pass + + +def bind_floating_ip(floating_ip, check_exit_code=True): + pass + + +def unbind_floating_ip(floating_ip): + pass + + +def ensure_vlan_forward(public_ip, port, private_ip): + pass + + +def ensure_floating_forward(floating_ip, fixed_ip): + pass + + +def remove_floating_forward(floating_ip, fixed_ip): + pass + + +def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): + """Create a vlan and bridge unless they already exist""" + #open xenapi session + url = FLAGS.xenapi_connection_url + username = FLAGS.xenapi_connection_username + password = FLAGS.xenapi_connection_password + session = XenAPISession(url, username, password) + #check whether bridge already exists + #retrieve network whose name_label is "bridge" + network_ref = NetworkHelper.find_network_with_name_label(session, bridge) + if network_ref == None: + #if bridge does not exists + #1 - create network + description = "network for nova bridge %s" % bridge + network_rec = { + 'name_label': bridge, + 'name_description': description, + 'other_config': {}, + } + network_ref = session.call_xenapi('network.create', network_rec) + #2 - find PIF for VLAN + expr = 'field "device" = "%s" and \ + field "VLAN" = "-1"' % FLAGS.vlan_interface + pifs = session.call_xenapi('PIF.get_all_records_where', expr) + pif_ref = None + #multiple PIF are ok: we are dealing with a pool + if len(pifs) == 0: + raise Exception( + _('Found no PID for device %s') % FLAGS.vlan_interface) + #3 - create vlan for network + for pif_ref in pifs.keys(): + session.call_xenapi('VLAN.create', pif_ref, + str(vlan_num), network_ref) + else: + #check VLAN tag is appropriate + network_rec = session.call_xenapi('network.get_record', network_ref) + #retrieve PIFs from network + for pif_ref in network_rec['PIFs']: + #retrieve VLAN from PIF + pif_rec = session.call_xenapi('PIF.get_record', pif_ref) + pif_vlan = int(pif_rec['VLAN']) + #raise an exception if VLAN <> vlan_num + if pif_vlan != vlan_num: + raise Exception(_("PIF %(pif_rec['uuid'])s for network " + "%(bridge)s has VLAN id %(pif_vlan)d." + "Expected %(vlan_num)d") % locals()) + + +def ensure_vlan(vlan_num): + pass + + +def ensure_bridge(bridge, interface, net_attrs=None): + pass + + +def get_dhcp_hosts(context, network_id): + pass + + +def update_dhcp(context, network_id): + pass + + +def update_ra(context, network_id): + pass diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py index 8f7806e6c..c4ee8a6be 100644 --- a/nova/virt/xenapi/network_utils.py +++ b/nova/virt/xenapi/network_utils.py @@ -28,10 +28,9 @@ class NetworkHelper(HelperBase): """ The class that wraps the helper methods together. """ - - + @classmethod - def find_network_with_name_label(cls,session,name_label): + def find_network_with_name_label(cls, session, name_label): networks = session.call_xenapi('network.get_by_name_label', name_label) if len(networks) == 1: return networks[0] @@ -39,9 +38,8 @@ class NetworkHelper(HelperBase): raise Exception(_('Found non-unique network' ' for name_label %s') % name_label) else: - return None - - + return None + @classmethod def find_network_with_bridge(cls, session, bridge): """Return the network on which the bridge is attached, if found.""" @@ -53,4 +51,4 @@ class NetworkHelper(HelperBase): raise Exception(_('Found non-unique network' ' for bridge %s') % bridge) else: - raise Exception(_('Found no network for bridge %s') % bridge) + raise Exception(_('Found no network for bridge %s') % bridge) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b5c5e082e..a0dbd1411 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -70,18 +70,19 @@ class VMOps(object): #this will return the bridge name in nova db bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] - + #this will return the appropriate network #TODO: avoid unnecessary call to find_network_with_bridge #when VLAN manager is being used (and not just use an if) - network_ref = None - try: + network_ref = None + try: network_ref = \ NetworkHelper.find_network_with_bridge(self._session, bridge) except: - #try to get name with name_label + #try to get name with name_label network_ref = \ - NetworkHelper.find_network_with_name_label(self._session,bridge) + NetworkHelper.find_network_with_name_label(self._session, + bridge) user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) -- cgit From e263a37f83ec5f8a1d81b0f4ec7a91464b2bc022 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 21 Feb 2011 11:02:19 +0000 Subject: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOVA-CORE DEVELOPERS SHOULD NOT REVIEW THIS MERGE PROPOSAL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is for Citrix OpenStack team only. We propose for merge into a cache of lp:nova to generate diffs for our internal peer review. --- nova/virt/xenapi/vmops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 9bd671045..09f5a1c94 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -141,6 +141,7 @@ class VMOps(object): #this will return the appropriate network #TODO(salvatore-orlando): avoid unnecessary call to #find_network_with_bridge when VLAN manager is being used + network_ref = None try: network_ref = \ -- cgit From 1278af43c16daea1bcbd2d47cd2d81919fe2c37e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sun, 27 Feb 2011 12:51:19 -0500 Subject: Add lxc libvirt driver --- nova/virt/libvirt.xml.template | 14 ++++++++++++++ nova/virt/libvirt_conn.py | 13 +++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 88bfbc668..87dea9039 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -2,6 +2,12 @@ ${name} ${memory_kb} +#if $type == 'lxc' + #set $disk_prefix = '' + #set $disk_bus = '' + exe + /sbin/init +#else #if $type == 'uml' #set $disk_prefix = 'ubd' #set $disk_bus = 'uml' @@ -37,6 +43,7 @@ #end if #end if + #end if #end if @@ -44,6 +51,12 @@ ${vcpus} +#if $type == 'lxc' + + + + +#else #if $getVar('rescue', False) @@ -68,6 +81,7 @@ #end if + #end if #end if diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e0fd106f..f97c10765 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -20,7 +20,7 @@ """ A connection to a hypervisor through libvirt. -Supports KVM, QEMU, UML, and XEN. +Supports KVM, LXC, QEMU, UML, and XEN. **Related Flags** @@ -83,7 +83,7 @@ flags.DEFINE_string('libvirt_xml_template', flags.DEFINE_string('libvirt_type', 'kvm', 'Libvirt domain type (valid options are: ' - 'kvm, qemu, uml, xen)') + 'kvm, lxc, qemu, uml, xen)') flags.DEFINE_string('libvirt_uri', '', 'Override the default libvirt URI (which is dependent' @@ -202,6 +202,8 @@ class LibvirtConnection(object): uri = FLAGS.libvirt_uri or 'uml:///system' elif FLAGS.libvirt_type == 'xen': uri = FLAGS.libvirt_uri or 'xen:///' + elif FLAGS.libvirt_type == 'lxc': + uri = FLAGS.libvirt_uri or 'lxc:///' else: uri = FLAGS.libvirt_uri or 'qemu:///system' return uri @@ -565,6 +567,10 @@ class LibvirtConnection(object): f.write(libvirt_xml) f.close() + if FLAGS.libvirt_type == 'lxc': + container_dir = '%s/rootfs' % basepath(suffix='') + utils.execute('mkdir -p %s' % container_dir) + # NOTE(vish): No need add the suffix to console.log os.close(os.open(basepath('console.log', ''), os.O_CREAT | os.O_WRONLY, 0660)) @@ -622,6 +628,9 @@ class LibvirtConnection(object): if not inst['kernel_id']: target_partition = "1" + if FLAGS.libvirt_type == 'lxc': + target_partition = None + key = str(inst['key_data']) net = None network_ref = db.network_get_by_instance(context.get_admin_context(), -- cgit From 2d6987d8c33477af19179e7664bbedf689bc08dc Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sun, 27 Feb 2011 13:05:26 -0500 Subject: Add ability to mount containers --- nova/virt/disk.py | 26 ++++++++++++++++++++++++++ nova/virt/libvirt_conn.py | 6 ++++++ 2 files changed, 32 insertions(+) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 2bded07a4..7cbc4a3be 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -114,6 +114,32 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): _unlink_device(device, nbd) +def setup_container(image, container_dir=None, partition=None, nbd=False): + """Setup the LXC container + + It will mount the loopback image to the container directory in order + to create the root filesystem for the container + """ + device = _link_device(image, nbd) + try: + if not partition is None: + # create partition + utils.execute('sudo kpartx -a %s' % device) + mapped_device = '/dev/mapper/%p%s' % (device.split('/')[-1], + partition) + else: + mapped_device = device + + utils.execute('sudo mount %s %s' %(mapped_device, container_dir)) + + except Exception as e: + LOG.warn(_('Unable to mount container')) + if not partition is None: + # remove partitions + utils.execute('sudo kpartx -s %s' % device) + _unlink_device(device, nbd) + + def _link_device(image, nbd): """Link image to device using loopback or nbd""" if nbd: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f97c10765..f7807a851 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -661,6 +661,12 @@ class LibvirtConnection(object): disk.inject_data(basepath('disk'), key, net, partition=target_partition, nbd=FLAGS.use_cow_images) + + if FLAGS.libvirt_type == 'lxc': + disk.setup_container(basepath('disk'), + container_dir=container_dir, + partition=target_partition, + nbd=FLAGS.use_cow_images) except Exception as e: # This could be a windows image, or a vmdk format disk LOG.warn(_('instance %(inst_name)s: ignoring error injecting' -- cgit From 33d7edb9d02d8f4dcb0b6ee85caf10434f060e1b Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sun, 27 Feb 2011 13:29:05 -0500 Subject: Clean up the mount points when it shutsdown --- nova/virt/disk.py | 1 - nova/virt/libvirt_conn.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 7cbc4a3be..28a4c11fb 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -139,7 +139,6 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): utils.execute('sudo kpartx -s %s' % device) _unlink_device(device, nbd) - def _link_device(image, nbd): """Link image to device using loopback or nbd""" if nbd: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f7807a851..5e76edb0b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -259,6 +259,8 @@ class LibvirtConnection(object): instance_name = instance['name'] LOG.info(_('instance %(instance_name)s: deleting instance files' ' %(target)s') % locals()) + if FLAGS.libvirt_type == 'lxc': + utils.execute('sudo umount %s/rootfs' % target) if os.path.exists(target): shutil.rmtree(target) -- cgit From 031205a26ae6fe906a47eefa716bbd575687c479 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sun, 27 Feb 2011 13:35:10 -0500 Subject: Add lxc to the libvirt tests --- nova/tests/test_virt.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index f151ae911..04f943a1f 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -112,12 +112,15 @@ class LibvirtConnTestCase(test.TestCase): 'uml': ('uml:///system', [(lambda t: t.find('.').get('type'), 'uml'), (lambda t: t.find('./os/type').text, 'uml')]), + 'lxc': ('lxc://', + [(lambda t: t.find('.').get('type'), 'lxc'), + (lambda t: t.find('./os/type').text, 'lxc')]), 'xen': ('xen:///', [(lambda t: t.find('.').get('type'), 'xen'), (lambda t: t.find('./os/type').text, 'linux')]), } - for hypervisor_type in ['qemu', 'kvm', 'xen']: + for hypervisor_type in ['qemu', 'kvm', 'lxc', 'xen']: check_list = type_uri_map[hypervisor_type][1] if rescue: -- cgit From cb30c80c922a09ccca18645670ea5b1cdc70f1f2 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 4 Mar 2011 12:31:59 +0000 Subject: fixed wrong local variable name in vmops --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a7838dbba..417f40da8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -519,7 +519,7 @@ class VMOps(object): NetworkHelper.find_network_with_name_label(self._session, bridge) if network_ref: - VMHelper.create_vif(self._session, vm_ref, + VMHelper.create_vif(self._session, vm_opaque_ref, network_ref, instance.mac_address) -- cgit From c4142835981eb9b2d5a55517d975dbda029986e2 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 8 Mar 2011 10:09:34 +0000 Subject: Removed excess comment lines --- nova/network/manager.py | 1 - nova/network/xenapi_net.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 05d677bc9..b36dd59cf 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -518,7 +518,6 @@ class VlanManager(NetworkManager): def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts.""" network_ref = db.network_get_by_instance(context, instance_id) - #xenapi driver will create a xen network if necessary here self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index d31a1352f..314638bcd 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -31,7 +31,7 @@ LOG = logging.getLogger("nova.xenapi_net") FLAGS = flags.FLAGS flags.DEFINE_string('vlan_interface', 'eth0', - 'network device for vlans') + 'Physical network interface for vlans') def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): -- cgit From e502ad0243962aca98cc28bfa5cf69f8cd08991c Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 9 Mar 2011 21:43:45 -0500 Subject: Moved umount container to disk.py and try to remove loopback when destroying the container --- nova/virt/disk.py | 8 ++++++++ nova/virt/libvirt_conn.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index e1b0171b5..484317dd8 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -140,6 +140,14 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): utils.execute('sudo kpartx -s %s' % device) _unlink_device(device, nbd) +def destroy_container(target, instance, nbd=False): + """Destroy the container once it terminates""" + try: + utils.execute('sudo umount %s/rootfs' % target) + image = os.path.join(FLAGS.instances_path, instance['name'], '' + 'disk') + except Exception as e: + LOG.warn(_('Unable to umount contianer')) + def _link_device(image, nbd): """Link image to device using loopback or nbd""" if nbd: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 97997513b..ef0eb20cd 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -260,7 +260,7 @@ class LibvirtConnection(object): LOG.info(_('instance %(instance_name)s: deleting instance files' ' %(target)s') % locals()) if FLAGS.libvirt_type == 'lxc': - utils.execute('sudo umount %s/rootfs' % target) + disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images) if os.path.exists(target): shutil.rmtree(target) -- cgit From 977fc1be4ea8af93b63975c5538462a776fbe168 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 11 Mar 2011 11:42:42 +0000 Subject: Moved vlan_interface flag in network.manager removed needless carriage return in vm_ops --- nova/network/linux_net.py | 5 ++--- nova/network/manager.py | 3 +++ nova/network/xenapi_net.py | 5 ----- nova/tests/test_xenapi.py | 12 ++++++++++++ nova/virt/xenapi/vmops.py | 3 +-- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 535ce87bc..6dcecdadb 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,13 +41,12 @@ flags.DEFINE_string('dhcpbridge_flagfile', flags.DEFINE_string('dhcp_domain', 'novalocal', 'domain to use for building the hostnames') - +#flags.DEFINE_string('vlan_interface', 'eth0', +# 'network device for vlans') flags.DEFINE_string('networks_path', '$state_path/networks', 'Location to keep network config files') flags.DEFINE_string('public_interface', 'eth0', 'Interface for public IP addresses') -flags.DEFINE_string('vlan_interface', 'eth0', - 'network device for vlans') flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'), 'location of nova-dhcpbridge') flags.DEFINE_string('routing_source_ip', '$my_ip', diff --git a/nova/network/manager.py b/nova/network/manager.py index b36dd59cf..15ce36940 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -85,6 +85,8 @@ flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block') flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block') flags.DEFINE_integer('cnt_vpn_clients', 0, 'Number of addresses reserved for vpn clients') +flags.DEFINE_string('vlan_interface', 'eth0', + 'network device for vlans') flags.DEFINE_string('network_driver', 'nova.network.linux_net', 'Driver to use for network creation') flags.DEFINE_bool('update_dhcp_on_disassociate', False, @@ -517,6 +519,7 @@ class VlanManager(NetworkManager): def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts.""" + LOG.debug("ENTERING SETUP COMPUTE NETWORK") network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 314638bcd..01889f94d 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -29,11 +29,6 @@ from nova.virt.xenapi.network_utils import NetworkHelper LOG = logging.getLogger("nova.xenapi_net") -FLAGS = flags.FLAGS -flags.DEFINE_string('vlan_interface', 'eth0', - 'Physical network interface for vlans') - - def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" #open xenapi session diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 7f437c2b8..0f3b9ce20 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -38,6 +38,8 @@ from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs from nova.tests.glance import stubs as glance_stubs +from nova import log as LOG + FLAGS = flags.FLAGS @@ -297,6 +299,16 @@ class XenAPIVMTestCase(test.TestCase): glance_stubs.FakeGlance.IMAGE_KERNEL, glance_stubs.FakeGlance.IMAGE_RAMDISK) + def test_spawn_vlanmanager(self): + self.flags(xenapi_image_service = 'glance', + network_manager='nova.network.manager.VlanManager', + network_driver='nova.network.xenapi_net') + LOG.debug("Self.network:%s",self.network) + self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, + glance_stubs.FakeGlance.IMAGE_KERNEL, + glance_stubs.FakeGlance.IMAGE_RAMDISK) + pass + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 9140774c5..66df3c28e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -236,8 +236,7 @@ class VMOps(object): """Create snapshot from a running VM instance :param instance: instance to be snapshotted - :param image_id: id of - image to upload to + :param image_id: id of image to upload to Steps involved in a XenServer snapshot: -- cgit From 743e82c0acac0fda78a55a8bbb65e601c4cb652c Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 14 Mar 2011 21:14:39 -0400 Subject: Refactor setup contianer/destroy container --- nova/virt/disk.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index f0b391efb..a3db1d882 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -122,31 +122,26 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): to create the root filesystem for the container """ device = _link_device(image, nbd) - try: - if not partition is None: - # create partition - utils.execute('sudo kpartx -a %s' % device) - mapped_device = '/dev/mapper/%p%s' % (device.split('/')[-1], - partition) - else: - mapped_device = device - - utils.execute('sudo mount %s %s' %(mapped_device, container_dir)) - - except Exception as e: - LOG.warn(_('Unable to mount container')) - if not partition is None: - # remove partitions - utils.execute('sudo kpartx -s %s' % device) + err = utils.execute('sudo', 'mount', mapped_device, container_dir) + if err: + raise exception.Error(_('Failed to mount filesystem: %s') + % err) _unlink_device(device, nbd) def destroy_container(target, instance, nbd=False): """Destroy the container once it terminates""" try: - utils.execute('sudo umount %s/rootfs' % target) + container_dir = '%s/rootfs' % target + utils.execute('sudo', 'umount', container_dir) + finally: image = os.path.join(FLAGS.instances_path, instance['name'], '' + 'disk') - except Exception as e: - LOG.warn(_('Unable to umount contianer')) + out, err = utils.execute('sudo', 'losetup', '--find', '--show', image) + device = out.strip() + if err: + raise execption.Error(_('Could not find loopback image: %s') + %err) + utils.execute('sudo', 'losetup', '--detach', device) + def _link_device(image, nbd): """Link image to device using loopback or nbd""" -- cgit From 48d3dd7f9d2633d8955080b6dccc7c97bc8ef7c3 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 15 Mar 2011 07:56:26 -0400 Subject: Mount the right device --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index a3db1d882..2c0460f39 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -122,7 +122,7 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): to create the root filesystem for the container """ device = _link_device(image, nbd) - err = utils.execute('sudo', 'mount', mapped_device, container_dir) + err = utils.execute('sudo', 'mount', device, container_dir) if err: raise exception.Error(_('Failed to mount filesystem: %s') % err) -- cgit From f60c9d0da8171b09bd7971fea52e9e032f98a143 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 15 Mar 2011 08:05:45 -0400 Subject: Add comments about the destroy container function --- nova/virt/disk.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 2c0460f39..dd4352957 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -129,7 +129,11 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): _unlink_device(device, nbd) def destroy_container(target, instance, nbd=False): - """Destroy the container once it terminates""" + """Destroy the container once it terminates + + It will umount the container that is mounted, try to find the loopback + device associated with the container and delete it. + """ try: container_dir = '%s/rootfs' % target utils.execute('sudo', 'umount', container_dir) -- cgit From 3c10c1ee1bcc3f3aad90e4e28761d1413ab203a9 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 15 Mar 2011 09:36:02 -0400 Subject: Really delete the loop --- nova/virt/disk.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index dd4352957..a44995613 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -23,6 +23,7 @@ Includes injection of SSH PGP keys into authorized_keys file. """ import os +import string import tempfile import time @@ -138,13 +139,10 @@ def destroy_container(target, instance, nbd=False): container_dir = '%s/rootfs' % target utils.execute('sudo', 'umount', container_dir) finally: - image = os.path.join(FLAGS.instances_path, instance['name'], '' + 'disk') - out, err = utils.execute('sudo', 'losetup', '--find', '--show', image) - device = out.strip() - if err: - raise execption.Error(_('Could not find loopback image: %s') - %err) - utils.execute('sudo', 'losetup', '--detach', device) + for loop in os.popen('sudo losetup -a').readlines(): + if instance['name'] in loop: + device = string.split(loop, ':') + utils.execute('sudo', 'losetup', '--detach', device) def _link_device(image, nbd): -- cgit From bb52b51d0e4f9b297dcc489562f38d1647e10856 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 16 Mar 2011 12:34:39 +0000 Subject: Adding unit test --- nova/network/manager.py | 2 ++ nova/network/xenapi_net.py | 3 +++ nova/tests/db/fakes.py | 45 +++++++++++++++++++++++++++---- nova/tests/test_xenapi.py | 22 ++++++++++++--- nova/virt/xenapi/fake.py | 67 ++++++++++++++++++++++++++++++++++++++++++---- nova/virt/xenapi/vmops.py | 2 +- 6 files changed, 127 insertions(+), 14 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 4baea482b..3b53d5d05 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -115,6 +115,7 @@ class NetworkManager(manager.Manager): timeout_fixed_ips = True def __init__(self, network_driver=None, *args, **kwargs): + LOG.debug("INIT - network driver:%s", network_driver) if not network_driver: network_driver = FLAGS.network_driver self.driver = utils.import_object(network_driver) @@ -520,6 +521,7 @@ class VlanManager(NetworkManager): def setup_compute_network(self, context, instance_id): """Sets up matching network for compute hosts.""" LOG.debug("ENTERING SETUP COMPUTE NETWORK") + LOG.debug("DRIVER:%s",self.driver) network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 01889f94d..49214764e 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -29,9 +29,12 @@ from nova.virt.xenapi.network_utils import NetworkHelper LOG = logging.getLogger("nova.xenapi_net") +FLAGS = flags.FLAGS + def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): """Create a vlan and bridge unless they already exist""" #open xenapi session + LOG.debug("ENTERING ensure_vlan_bridge in xenapi net") url = FLAGS.xenapi_connection_url username = FLAGS.xenapi_connection_username password = FLAGS.xenapi_connection_password diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index d760dc456..88daa82c3 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -23,8 +23,9 @@ from nova import db from nova import test from nova import utils +from nova import log as LOG -def stub_out_db_instance_api(stubs): +def stub_out_db_instance_api(stubs, injected=True): """ Stubs out the db API for creating Instances """ INSTANCE_TYPES = { @@ -36,6 +37,29 @@ def stub_out_db_instance_api(stubs): 'm1.xlarge': dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)} + flat_network_fields = { + 'id': 'fake_flat', + 'bridge': 'xenbr0', + 'label': 'fake_flat_network', + 'netmask': '255.255.255.0', + 'gateway': '10.0.0.1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2', + 'ra_server': None, + 'injected': injected} + + vlan_network_fields = { + 'id': 'fake_vlan', + 'bridge': 'br111', + 'label': 'fake_vlan_network', + 'netmask': '255.255.255.0', + 'gateway': '10.0.0.1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2', + 'ra_server': None, + 'vlan': 111, + 'injected': False} + class FakeModel(object): """ Stubs out for model """ def __init__(self, values): @@ -81,12 +105,23 @@ def stub_out_db_instance_api(stubs): return FakeModel(base_options) def fake_network_get_by_instance(context, instance_id): - fields = { - 'bridge': 'xenbr0', - } - return FakeModel(fields) + #even instance numbers are on vlan networks + if instance_id % 2 == 0: + return FakeModel(vlan_network_fields) + else: + return FakeModel(flat_network_fields) + + def fake_network_get_all_by_instance(context, instance_id): + l = [] + #even instance numbers are on vlan networks + if instance_id % 2 == 0: + l.append(FakeModel(vlan_network_fields)) + else: + l.append(FakeModel(flat_network_fields)) + return l stubs.Set(db, 'instance_create', fake_instance_create) stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'network_get_all_by_instance', fake_network_get_all_by_instance) stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 26fad39d1..2cdc84882 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -165,6 +165,7 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_connection_password = 'test_pass' xenapi_fake.reset() xenapi_fake.create_local_srs() + xenapi_fake.create_local_pifs() db_fakes.stub_out_db_instance_api(self.stubs) xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) @@ -252,6 +253,9 @@ class XenAPIVMTestCase(test.TestCase): # Check that the VM is running according to XenAPI. self.assertEquals(vm['power_state'], 'Running') + + # Check that VM network is consistent with nova network + LOG.debug("VM INFO - NETWORK:%s", vm_info) def _test_spawn(self, image_id, kernel_id, ramdisk_id, instance_type="m1.large"): @@ -301,13 +305,25 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_vlanmanager(self): self.flags(xenapi_image_service = 'glance', - network_manager='nova.network.manager.VlanManager', - network_driver='nova.network.xenapi_net') + network_manager = 'nova.network.manager.VlanManager', + network_driver = 'nova.network.xenapi_net', + vlan_interface = 'fake0') LOG.debug("Self.network:%s",self.network) + LOG.debug("network driver:%s",FLAGS.network_driver) + fake_instance_id = 2 + network_bk=self.network + #ensure we use xenapi_net driver + self.network = utils.import_object(FLAGS.network_manager) + self.network.setup_compute_network(None, fake_instance_id) self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, glance_stubs.FakeGlance.IMAGE_KERNEL, glance_stubs.FakeGlance.IMAGE_RAMDISK) - pass + url = FLAGS.xenapi_connection_url + username = FLAGS.xenapi_connection_username + password = FLAGS.xenapi_connection_password + session = xenapi_conn.XenAPISession(url, username, password) + + self.network = network_bk def tearDown(self): super(XenAPIVMTestCase, self).tearDown() diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index ba12d4d3a..2e8cd9c5c 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -61,7 +61,7 @@ from nova import log as logging _CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\ - 'PBD', 'VDI', 'VIF', 'VM', 'task'] + 'PBD', 'VDI', 'VIF', 'PIF', 'VM', 'VLAN', 'task'] _db_content = {} @@ -103,7 +103,6 @@ def create_vm(name_label, status, 'is_control_domain': is_control_domain, }) - def destroy_vm(vm_ref): vm_rec = _db_content['VM'][vm_ref] @@ -178,6 +177,12 @@ def create_task(name_label): }) +def create_local_pifs(): + """Adds a PIF for each to the local database with VLAN=-1. + Do this one per host.""" + for host_ref in _db_content['host'].keys(): + _create_local_pif(host_ref) + def create_local_srs(): """Create an SR that looks like the one created on the local disk by default by the XenServer installer. Do this one per host.""" @@ -204,8 +209,18 @@ def _create_local_sr(host_ref): _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] return sr_ref +def _create_local_pif(host_ref): + pif_ref= _create_object('PIF', { + 'name-label': 'Fake PIF', + 'MAC': '00:11:22:33:44:55', + 'physical': True, + 'VLAN': -1, + 'device': 'fake0', + 'host_uuid': host_ref, + }) def _create_object(table, obj): + LOG.debug("ENTERING _create_object:%s", obj) ref = str(uuid.uuid4()) obj['uuid'] = str(uuid.uuid4()) _db_content[table][ref] = obj @@ -228,6 +243,24 @@ def _create_sr(table, obj): return sr_ref +def _create_vlan(pif_ref, vlan_num, network_ref): + LOG.debug("ENTERING FAKE CREATE VLAN") + pif_rec = get_record('PIF', pif_ref) + vlan_pif_ref = _create_object('PIF', { + 'name-label': 'Fake VLAN PIF', + 'MAC': '00:11:22:33:44:55', + 'physical': True, + 'VLAN': vlan_num, + 'device': pif_rec['device'], + 'host_uuid': pif_rec['host_uuid'], + }) + return _create_object('VLAN', { + 'tagged-pif': pif_ref, + 'untagged-pif': vlan_pif_ref, + 'tag': vlan_num + }) + + def get_all(table): return _db_content[table].keys() @@ -235,7 +268,6 @@ def get_all(table): def get_all_records(table): return _db_content[table] - def get_record(table, ref): if ref in _db_content[table]: return _db_content[table].get(ref) @@ -286,6 +318,26 @@ class SessionBase(object): rec['currently_attached'] = False rec['device'] = '' + def PIF_get_all_records_where(self, _1,_2): + # TODO (salvatore-orlando):filter table on _2 + return _db_content['PIF'] + + def VM_get_xenstore_data(self, _1, vm_ref): + return _db_content['VM'][vm_ref].get('xenstore_data', '') + + def VM_remove_from_xenstore_data(self, _1, vm_ref, key): + db_ref = _db_content['VM'][vm_ref] + if not 'xenstore_data' in db_ref: + return + db_ref['xenstore_data'][key] = None + + + def VM_add_to_xenstore_data(self, _1, vm_ref, key, value): + db_ref = _db_content['VM'][vm_ref] + if not 'xenstore_data' in db_ref: + db_ref['xenstore_data'] = {} + db_ref['xenstore_data'][key] = value + def host_compute_free_memory(self, _1, ref): #Always return 12GB available return 12 * 1024 * 1024 * 1024 @@ -431,12 +483,17 @@ class SessionBase(object): def _create(self, name, params): self._check_session(params) is_sr_create = name == 'SR.create' + LOG.debug("NAME:%s",name) + is_vlan_create = name == 'VLAN.create' # Storage Repositories have a different API - expected = is_sr_create and 10 or 2 + expected = is_sr_create and 10 or is_vlan_create and 4 or 2 self._check_arg_count(params, expected) (cls, _) = name.split('.') ref = is_sr_create and \ - _create_sr(cls, params) or _create_object(cls, params[1]) + _create_sr(cls, params) or \ + is_vlan_create and \ + _create_vlan(params[1],params[2],params[3]) or \ + _create_object(cls, params[1]) # Call hook to provide any fixups needed (ex. creating backrefs) after_hook = 'after_%s_create' % cls diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0813c3db4..4bfef20f3 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -772,7 +772,7 @@ class VMOps(object): if network_ref: try: device = "1" if instance._rescue else "0" - except AttributeError: + except (AttributeError, KeyError): device = "0" VMHelper.create_vif( -- cgit From 7fbf061666516705e74592c3660155e86d3da895 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 16 Mar 2011 09:15:46 -0400 Subject: Fix up testsuite for lxc --- nova/tests/test_virt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index c149c9307..b3ca241cb 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -256,9 +256,9 @@ class LibvirtConnTestCase(test.TestCase): 'uml': ('uml:///system', [(lambda t: t.find('.').get('type'), 'uml'), (lambda t: t.find('./os/type').text, 'uml')]), - 'lxc': ('lxc://', + 'lxc': ('lxc://;', [(lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'lxc')]), + (lambda t: t.find('./os/type').text, 'exe')]), 'xen': ('xen:///', [(lambda t: t.find('.').get('type'), 'xen'), (lambda t: t.find('./os/type').text, 'linux')]), -- cgit From 8f9a5ecb7d3907456b9a77f3321ed09feb5c5f2f Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 16 Mar 2011 09:24:17 -0400 Subject: More execvp fallout --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index af2cbdce5..a79e0a065 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -599,7 +599,7 @@ class LibvirtConnection(object): if FLAGS.libvirt_type == 'lxc': container_dir = '%s/rootfs' % basepath(suffix='') - utils.execute('mkdir -p %s' % container_dir) + utils.execute('mkdir', '-p', container_dir) # NOTE(vish): No need add the suffix to console.log os.close(os.open(basepath('console.log', ''), -- cgit From a21efc63be6bad3bbde41eb96d6a1752e6d8174d Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 16 Mar 2011 09:26:37 -0400 Subject: Really fix testcase --- nova/tests/test_virt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index b3ca241cb..fed8ff803 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -256,7 +256,7 @@ class LibvirtConnTestCase(test.TestCase): 'uml': ('uml:///system', [(lambda t: t.find('.').get('type'), 'uml'), (lambda t: t.find('./os/type').text, 'uml')]), - 'lxc': ('lxc://;', + 'lxc': ('lxc:///', [(lambda t: t.find('.').get('type'), 'lxc'), (lambda t: t.find('./os/type').text, 'exe')]), 'xen': ('xen:///', -- cgit From c44ab013f5f5a078b27c4965e2e3c4abbfe30c59 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 16 Mar 2011 20:42:39 -0400 Subject: Revert testsuite changes --- nova/tests/test_virt.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index fed8ff803..b214f5ce7 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -256,15 +256,12 @@ class LibvirtConnTestCase(test.TestCase): 'uml': ('uml:///system', [(lambda t: t.find('.').get('type'), 'uml'), (lambda t: t.find('./os/type').text, 'uml')]), - 'lxc': ('lxc:///', - [(lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'exe')]), 'xen': ('xen:///', [(lambda t: t.find('.').get('type'), 'xen'), (lambda t: t.find('./os/type').text, 'linux')]), } - for hypervisor_type in ['qemu', 'kvm', 'lxc', 'xen']: + for hypervisor_type in ['qemu', 'kvm', 'xen']: check_list = type_uri_map[hypervisor_type][1] if rescue: -- cgit From fea850245835f867aa4cc741b612445e56e64236 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Wed, 16 Mar 2011 20:52:14 -0400 Subject: Add basic tests for lxc containers. --- nova/tests/test_virt.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index b214f5ce7..fab05de10 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -227,6 +227,42 @@ class LibvirtConnTestCase(test.TestCase): self._check_xml_and_uri(instance_data, expect_kernel=True, expect_ramdisk=True, rescue=True) + def test_lxc_container_and_uri(self): + instance_data = dict(self.test_instace) + self._check_xml_and_container(instance_data) + + def _check_xml_and_container(self, instance): + user_context = context.RequestContext(project=self.project, + user=self.user) + instance_ref = db.instance_create(user_context,instance) + host = self.network.get_network_host(user_context.elevated()) + network_ref= db.project_get_network(context.get_admin_context(), + self.project.id) + + fixed_ip = {'address': self.test_ip, + 'network_id': network_ref['id']} + + ctxt = context.get_admin_context() + fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) + db.fixed_ip_update(ctxt, self.test_ip, + {'allocated': True, + 'instance_id': instance_ref['id']}) + + FLAGS.libvirt_type = 'lxc' + self.assertEquals(uri, 'lxc:///') + + xml = conn.to_xml(instance_ref) + tree = xml_to_tree(xml) + + check = [ + (lambda t: t.find('.').get('type'), 'lxc'), + (lambda t: t.find('./os/type').text, 'exe') + ] + + for i (check, expected_result) in enumerate(check): + self.aseertEqual(check(time), + expected_result, + '%s failed common check %d' % (xml, i)) def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): user_context = context.RequestContext(project=self.project, -- cgit From 7f837b1f22922cb968b0ffb42bdb4d56c0d9f3c3 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 07:06:58 -0400 Subject: Update Authors and testsuite --- Authors | 1 + nova/tests/test_virt.py | 41 ++++++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Authors b/Authors index 9aad104a7..12d2f02de 100644 --- a/Authors +++ b/Authors @@ -11,6 +11,7 @@ Chiradeep Vittal Chmouel Boudjnah Chris Behrens Christian Berendt +Chuck Short Cory Wright Dan Prince David Pravec diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index fab05de10..92c80272b 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -228,16 +228,16 @@ class LibvirtConnTestCase(test.TestCase): expect_ramdisk=True, rescue=True) def test_lxc_container_and_uri(self): - instance_data = dict(self.test_instace) + instance_data = dict(self.test_instance) self._check_xml_and_container(instance_data) def _check_xml_and_container(self, instance): user_context = context.RequestContext(project=self.project, user=self.user) - instance_ref = db.instance_create(user_context,instance) + instance_ref = db.instance_create(user_context, instance) host = self.network.get_network_host(user_context.elevated()) - network_ref= db.project_get_network(context.get_admin_context(), - self.project.id) + network_ref = db.project_get_network(context.get_admin_context(), + self.project.id) fixed_ip = {'address': self.test_ip, 'network_id': network_ref['id']} @@ -245,24 +245,28 @@ class LibvirtConnTestCase(test.TestCase): ctxt = context.get_admin_context() fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) db.fixed_ip_update(ctxt, self.test_ip, - {'allocated': True, - 'instance_id': instance_ref['id']}) + {'allocated': True, + 'instance_id': instance_ref['id']}) FLAGS.libvirt_type = 'lxc' + conn = libvirt_conn.LibvirtConnection(True) + + uri = conn.get_uri() self.assertEquals(uri, 'lxc:///') xml = conn.to_xml(instance_ref) tree = xml_to_tree(xml) check = [ - (lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'exe') + (lambda t: t.find('.').get('type'), 'lxc'), + (lambda t: t.find('./os/type').text, 'exe'), ] - for i (check, expected_result) in enumerate(check): - self.aseertEqual(check(time), + for i, (check, expected_result) in enumerate(check): + self.assertEqual(check(tree), expected_result, '%s failed common check %d' % (xml, i)) + def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): user_context = context.RequestContext(project=self.project, @@ -322,6 +326,7 @@ class LibvirtConnTestCase(test.TestCase): check = (lambda t: t.find('./os/initrd'), None) check_list.append(check) + common_checks = [ (lambda t: t.find('.').tag, 'domain'), (lambda t: t.find( @@ -338,8 +343,9 @@ class LibvirtConnTestCase(test.TestCase): (lambda t: t.find('./devices/serial/source').get( 'path').split('/')[1], 'console.log'), (lambda t: t.find('./memory').text, '2097152')] + if rescue: - common_checks += [ + common_checks = [ (lambda t: t.findall('./devices/disk/source')[0].get( 'file').split('/')[1], 'disk.rescue'), (lambda t: t.findall('./devices/disk/source')[1].get( @@ -362,14 +368,15 @@ class LibvirtConnTestCase(test.TestCase): xml = conn.to_xml(instance_ref, rescue) tree = xml_to_tree(xml) for i, (check, expected_result) in enumerate(checks): - self.assertEqual(check(tree), - expected_result, - '%s failed check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed check %d' % (xml, i)) + for i, (check, expected_result) in enumerate(common_checks): - self.assertEqual(check(tree), - expected_result, - '%s failed common check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed common check %d' % (xml, i)) # This test is supposed to make sure we don't # override a specifically set uri -- cgit From 7701edd34f1fc9fa26b3dfcc77ff87018622bedc Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 07:13:31 -0400 Subject: get_console_output is not supported by lxc and libvirt --- nova/virt/libvirt_conn.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d08ca8b6a..9bfd3f841 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -490,6 +490,9 @@ class LibvirtConnection(object): instance['name']) data = self._flush_xen_console(virsh_output) fpath = self._append_to_file(data, console_log) + elif FLAGS.libvirt_type == 'lxc': + # LXC is also special + LOG.info(_("Unable to read LXC console")) else: fpath = console_log -- cgit From 70cd1a51ada85f4724190d2562130172e9495e5e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 07:53:25 -0400 Subject: Update authors again --- Authors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authors b/Authors index 12d2f02de..22a9fb8eb 100644 --- a/Authors +++ b/Authors @@ -11,7 +11,7 @@ Chiradeep Vittal Chmouel Boudjnah Chris Behrens Christian Berendt -Chuck Short +Chuck Short Cory Wright Dan Prince David Pravec -- cgit From 9ba500c304595dff037da296f26cb13d02bfbc04 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 07:57:06 -0400 Subject: Fix pep8 errors --- nova/tests/test_virt.py | 2 -- nova/virt/disk.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 92c80272b..7d50960a3 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -326,7 +326,6 @@ class LibvirtConnTestCase(test.TestCase): check = (lambda t: t.find('./os/initrd'), None) check_list.append(check) - common_checks = [ (lambda t: t.find('.').tag, 'domain'), (lambda t: t.find( @@ -372,7 +371,6 @@ class LibvirtConnTestCase(test.TestCase): expected_result, '%s failed check %d' % (xml, i)) - for i, (check, expected_result) in enumerate(common_checks): self.assertEqual(check(tree), expected_result, diff --git a/nova/virt/disk.py b/nova/virt/disk.py index a44995613..26976940e 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -129,6 +129,7 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): % err) _unlink_device(device, nbd) + def destroy_container(target, instance, nbd=False): """Destroy the container once it terminates -- cgit From bb6096c51cde91dccaad0e9f584f2dc26057da1f Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 08:49:52 -0400 Subject: Update mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index ccf2109a7..78cfef53b 100644 --- a/.mailmap +++ b/.mailmap @@ -10,6 +10,7 @@ + -- cgit From c98cead470f33041e928a6f82be801efeb94ccc3 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 08:52:52 -0400 Subject: Remove nbd=FLAGS.use_cow_images for destroy container --- nova/virt/disk.py | 7 +++---- nova/virt/libvirt_conn.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 26976940e..90d3cf499 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -23,7 +23,6 @@ Includes injection of SSH PGP keys into authorized_keys file. """ import os -import string import tempfile import time @@ -130,7 +129,7 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): _unlink_device(device, nbd) -def destroy_container(target, instance, nbd=False): +def destroy_container(target, instance): """Destroy the container once it terminates It will umount the container that is mounted, try to find the loopback @@ -140,9 +139,9 @@ def destroy_container(target, instance, nbd=False): container_dir = '%s/rootfs' % target utils.execute('sudo', 'umount', container_dir) finally: - for loop in os.popen('sudo losetup -a').readlines(): + for loop in utils.popen('sudo losetup -a').readlines(): if instance['name'] in loop: - device = string.split(loop, ':') + device = loop.split(loop, ':') utils.execute('sudo', 'losetup', '--detach', device) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9bfd3f841..9e0d0538d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -275,7 +275,7 @@ class LibvirtConnection(object): LOG.info(_('instance %(instance_name)s: deleting instance files' ' %(target)s') % locals()) if FLAGS.libvirt_type == 'lxc': - disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images) + disk.destroy_container(target, instance) if os.path.exists(target): shutil.rmtree(target) -- cgit From 6bd017262a5c61d915ede2e58ef2758f1f190ff3 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 08:54:05 -0400 Subject: Remove target_partition for setup_container but still hardcode because its needed when you inject the keys into the image. --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9e0d0538d..0ca27d629 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -700,8 +700,7 @@ class LibvirtConnection(object): if FLAGS.libvirt_type == 'lxc': disk.setup_container(basepath('disk'), container_dir=container_dir, - partition=target_partition, - nbd=FLAGS.use_cow_images) + partition=target_partition) except Exception as e: # This could be a windows image, or a vmdk format disk LOG.warn(_('instance %(inst_name)s: ignoring error injecting' -- cgit From cc716f9648355bc3737dd749a35dc327ebda1e6f Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 09:15:33 -0400 Subject: Fixed typo when I was trying to add test cases for lxc --- nova/tests/test_virt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 7d50960a3..c2b8ba0a1 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -344,7 +344,7 @@ class LibvirtConnTestCase(test.TestCase): (lambda t: t.find('./memory').text, '2097152')] if rescue: - common_checks = [ + common_checks += [ (lambda t: t.findall('./devices/disk/source')[0].get( 'file').split('/')[1], 'disk.rescue'), (lambda t: t.findall('./devices/disk/source')[1].get( -- cgit From 174d8ca2da7e2e53c9105ccc5e5d9a97bc12c0b8 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 09:17:42 -0400 Subject: Set nbd to false when mounting the image --- nova/virt/disk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 90d3cf499..a84425de7 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -121,6 +121,7 @@ def setup_container(image, container_dir=None, partition=None, nbd=False): It will mount the loopback image to the container directory in order to create the root filesystem for the container """ + nbd=False device = _link_device(image, nbd) err = utils.execute('sudo', 'mount', device, container_dir) if err: -- cgit From bc0ef2c7aead759504eedcb4e2ab6d96dba7c266 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:02:40 -0400 Subject: Fix up setup container --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index a84425de7..2fa7819d7 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -115,7 +115,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): _unlink_device(device, nbd) -def setup_container(image, container_dir=None, partition=None, nbd=False): +def setup_container(image, container_dir=None, partition=None): """Setup the LXC container It will mount the loopback image to the container directory in order -- cgit From abb86555f7417225a72126872beb377268acfdb1 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:16:37 -0400 Subject: Remove me from mailmap --- .mailmap | 1 - 1 file changed, 1 deletion(-) diff --git a/.mailmap b/.mailmap index 78cfef53b..ccf2109a7 100644 --- a/.mailmap +++ b/.mailmap @@ -10,7 +10,6 @@ - -- cgit From 36285d3acb940c39dc1827699c1e3c0cc9846529 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:22:57 -0400 Subject: Fix more pep8 errors --- nova/tests/test_virt.py | 12 ++++++------ nova/virt/disk.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index c2b8ba0a1..222975adc 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -367,14 +367,14 @@ class LibvirtConnTestCase(test.TestCase): xml = conn.to_xml(instance_ref, rescue) tree = xml_to_tree(xml) for i, (check, expected_result) in enumerate(checks): - self.assertEqual(check(tree), - expected_result, - '%s failed check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed check %d' % (xml, i)) for i, (check, expected_result) in enumerate(common_checks): - self.assertEqual(check(tree), - expected_result, - '%s failed common check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed common check %d' % (xml, i)) # This test is supposed to make sure we don't # override a specifically set uri diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 2fa7819d7..6c5f126bd 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -121,7 +121,7 @@ def setup_container(image, container_dir=None, partition=None): It will mount the loopback image to the container directory in order to create the root filesystem for the container """ - nbd=False + nbd = False device = _link_device(image, nbd) err = utils.execute('sudo', 'mount', device, container_dir) if err: @@ -132,7 +132,7 @@ def setup_container(image, container_dir=None, partition=None): def destroy_container(target, instance): """Destroy the container once it terminates - + It will umount the container that is mounted, try to find the loopback device associated with the container and delete it. """ -- cgit From a06203c66af05c96c161b80511f4a6607ffe4905 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:41:55 -0400 Subject: Fix up tests --- nova/tests/test_virt.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 222975adc..fefc11f0d 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -239,8 +239,8 @@ class LibvirtConnTestCase(test.TestCase): network_ref = db.project_get_network(context.get_admin_context(), self.project.id) - fixed_ip = {'address': self.test_ip, - 'network_id': network_ref['id']} + fixed_ip = {'address': self.test_ip, + 'network_id': network_ref['id']} ctxt = context.get_admin_context() fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) @@ -259,13 +259,13 @@ class LibvirtConnTestCase(test.TestCase): check = [ (lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'exe'), + (lambda t: t.find('./os/type').text, 'exe') ] for i, (check, expected_result) in enumerate(check): - self.assertEqual(check(tree), - expected_result, - '%s failed common check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed common check %d' % (xml, i)) def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): @@ -342,7 +342,6 @@ class LibvirtConnTestCase(test.TestCase): (lambda t: t.find('./devices/serial/source').get( 'path').split('/')[1], 'console.log'), (lambda t: t.find('./memory').text, '2097152')] - if rescue: common_checks += [ (lambda t: t.findall('./devices/disk/source')[0].get( -- cgit From dee8a59b5d575a0327464e27115d0d870cde97be Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:43:48 -0400 Subject: more pep8 fixes --- nova/tests/test_virt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index fefc11f0d..2510525fc 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -263,9 +263,9 @@ class LibvirtConnTestCase(test.TestCase): ] for i, (check, expected_result) in enumerate(check): - self.assertEqual(check(tree), - expected_result, - '%s failed common check %d' % (xml, i)) + self.assertEqual(check(tree), + expected_result, + '%s failed common check %d' % (xml, i)) def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): -- cgit From 4364a158fd31bdfcfa3ae835a2fd9c0f47d3632f Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:45:31 -0400 Subject: pep8 fixes --- nova/tests/test_virt.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 2510525fc..45f98fcde 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -259,8 +259,7 @@ class LibvirtConnTestCase(test.TestCase): check = [ (lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'exe') - ] + (lambda t: t.find('./os/type').text, 'exe')] for i, (check, expected_result) in enumerate(check): self.assertEqual(check(tree), -- cgit From b0e3b8e58a925ebf52fa741883f757ed2bc4383c Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 17 Mar 2011 10:47:19 -0400 Subject: more pep8 fixes --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 6c5f126bd..f6e6795d6 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -132,7 +132,7 @@ def setup_container(image, container_dir=None, partition=None): def destroy_container(target, instance): """Destroy the container once it terminates - + It will umount the container that is mounted, try to find the loopback device associated with the container and delete it. """ -- cgit From af67fba36436feeede4dcc5720e51d2b66c3094a Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 17 Mar 2011 22:30:34 -0400 Subject: Images now v1.1 supported...mostly. --- nova/api/openstack/__init__.py | 11 +- nova/api/openstack/images.py | 214 ++++++++++++++------------------ nova/api/openstack/views/images.py | 63 ++++++++-- nova/tests/api/openstack/test_images.py | 171 +++++++++++++++++++------ 4 files changed, 285 insertions(+), 174 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 0b50d17d0..0ac67fdba 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -113,8 +113,6 @@ class APIRouter(wsgi.Router): parent_resource=dict(member_name='server', collection_name='servers')) - mapper.resource("image", "images", controller=images.Controller(), - collection={'detail': 'GET'}) mapper.resource("flavor", "flavors", controller=flavors.Controller(), collection={'detail': 'GET'}) mapper.resource("shared_ip_group", "shared_ip_groups", @@ -130,6 +128,10 @@ class APIRouterV10(APIRouter): collection={'detail': 'GET'}, member=self.server_members) + mapper.resource("image", "images", + controller=images.Controller_v1_0(), + collection={'detail': 'GET'}) + class APIRouterV11(APIRouter): def _setup_routes(self, mapper): @@ -139,6 +141,11 @@ class APIRouterV11(APIRouter): collection={'detail': 'GET'}, member=self.server_members) + mapper.resource("image", "images", + controller=images.Controller_v1_1(), + collection={'detail': 'GET'}) + + class Versions(wsgi.Application): @webob.dec.wsgify(RequestClass=wsgi.Request) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 98f0dd96b..2357bfd3d 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -1,6 +1,4 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -19,92 +17,19 @@ from webob import exc from nova import compute from nova import flags +from nova import log from nova import utils from nova import wsgi -import nova.api.openstack -from nova.api.openstack import common -from nova.api.openstack import faults -import nova.image.service - - -FLAGS = flags.FLAGS - - -def _translate_keys(item): - """ - Maps key names to Rackspace-like attributes for return - also pares down attributes to those we want - item is a dict - - Note: should be removed when the set of keys expected by the api - and the set of keys returned by the image service are equivalent - - """ - # TODO(tr3buchet): this map is specific to s3 object store, - # replace with a list of keys for _filter_keys later - mapped_keys = {'status': 'imageState', - 'id': 'imageId', - 'name': 'imageLocation'} - - mapped_item = {} - # TODO(tr3buchet): - # this chunk of code works with s3 and the local image service/glance - # when we switch to glance/local image service it can be replaced with - # a call to _filter_keys, and mapped_keys can be changed to a list - try: - for k, v in mapped_keys.iteritems(): - # map s3 fields - mapped_item[k] = item[v] - except KeyError: - # return only the fields api expects - mapped_item = _filter_keys(item, mapped_keys.keys()) - - return mapped_item - - -def _translate_status(item): - """ - Translates status of image to match current Rackspace api bindings - item is a dict +from nova.api.openstack.views import images as images_view - Note: should be removed when the set of statuses expected by the api - and the set of statuses returned by the image service are equivalent +class Controller(wsgi.Controller): """ - status_mapping = { - 'pending': 'queued', - 'decrypting': 'preparing', - 'untarring': 'saving', - 'available': 'active'} - try: - item['status'] = status_mapping[item['status']] - except KeyError: - # TODO(sirp): Performing translation of status (if necessary) here for - # now. Perhaps this should really be done in EC2 API and - # S3ImageService - pass - - return item - - -def _filter_keys(item, keys): - """ - Filters all model attributes except for keys - item is a dict - + Base `wsgi.Controller` for retrieving and displaying images in the + OpenStack API. Version-inspecific code goes here. """ - return dict((k, v) for k, v in item.iteritems() if k in keys) - - -def _convert_image_id_to_hash(image): - if 'imageId' in image: - # Convert EC2-style ID (i-blah) to Rackspace-style (int) - image_id = abs(hash(image['imageId'])) - image['imageId'] = image_id - image['id'] = image_id - -class Controller(wsgi.Controller): + _builder = images_view.Builder_v1_0() _serialization_metadata = { 'application/xml': { @@ -112,55 +37,96 @@ class Controller(wsgi.Controller): "image": ["id", "name", "updated", "created", "status", "serverId", "progress"]}}} - def __init__(self): - self._service = utils.import_object(FLAGS.image_service) + def __init__(self, image_service=None, compute_service=None): + """ + Initialize new `ImageController`. + + @param compute_service: `nova.compute.api:API` + @param image_service: `nova.image.service:BaseImageService` + """ + _default_service = utils.import_object(flags.FLAGS.image_service) + + self.__compute = compute_service or compute.API() + self.__image = image_service or _default_service + self.__log = log.getLogger(self.__class__.__name__) def index(self, req): - """Return all public images in brief""" - items = self._service.index(req.environ['nova.context']) - items = common.limited(items, req) - items = [_filter_keys(item, ('id', 'name')) for item in items] - return dict(images=items) + """ + Return an index listing of images available to the request. + + @param req: `webob.Request` object + """ + context = req.environ['nova.context'] + images = self.__image.index(context) + build = self._builder.build + return dict(images=[build(req, image, False) for image in images]) def detail(self, req): - """Return all public images in detail""" - try: - items = self._service.detail(req.environ['nova.context']) - except NotImplementedError: - items = self._service.index(req.environ['nova.context']) - for image in items: - _convert_image_id_to_hash(image) - - items = common.limited(items, req) - items = [_translate_keys(item) for item in items] - items = [_translate_status(item) for item in items] - return dict(images=items) - - def show(self, req, id): - """Return data about the given image id""" - image_id = common.get_image_id_from_image_hash(self._service, - req.environ['nova.context'], id) - - image = self._service.show(req.environ['nova.context'], image_id) - _convert_image_id_to_hash(image) - return dict(image=image) - - def delete(self, req, id): - # Only public images are supported for now. - raise faults.Fault(exc.HTTPNotFound()) + """ + Return a detailed index listing of images available to the request. + + @param req: `webob.Request` object. + """ + context = req.environ['nova.context'] + images = self.__image.detail(context) + build = self._builder.build + return dict(images=[build(req, image, True) for image in images]) + + def show(self, req, image_id): + """ + Return detailed information about a specific image. + + @param req: `webob.Request` object + @param image_id: Image identifier (integer) + """ + context = req.environ['nova.context'] + image = self.__image.show(context, image_id) + return self._builder.build(req, image, True) + + def delete(self, req, image_id): + """ + Delete an image, if allowed. + + @param req: `webob.Request` object + @param image_id: Image identifier (integer) + """ + context = req.environ['nova.context'] + self.__image.delete(context, image_id) + return exc.HTTPNoContent() def create(self, req): + """ + Snapshot a server instance and save the image. + + @param req: `webob.Request` object + """ context = req.environ['nova.context'] - env = self._deserialize(req.body, req.get_content_type()) - instance_id = env["image"]["serverId"] - name = env["image"]["name"] + body = req.body + content_type = req.get_content_type() + image = self._deserialize(body, content_type) + + if not image: + raise exc.HTTPBadRequest() + + try: + server_id = image["serverId"] + image_name = image["name"] + except KeyError: + raise exc.HTTPBadRequest() + + image = self.__compute.snapshot(context, server_id, image_name) + return self._builder.build(req, image, True) - image_meta = compute.API().snapshot( - context, instance_id, name) - return dict(image=image_meta) +class Controller_v1_0(Controller): + """ + Version 1.0 specific controller logic. + """ + _builder = images_view.Builder_v1_0() + - def update(self, req, id): - # Users may not modify public images, and that's all that - # we support for now. - raise faults.Fault(exc.HTTPNotFound()) +class Controller_v1_1(Controller): + """ + Version 1.1 specific controller logic. + """ + _builder = images_view.Builder_v1_1() diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index a6c6ad7d1..1631d1fe3 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -15,20 +15,61 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.api.openstack import common +class Builder(object): + """ + Base class for generating responses to OpenStack API requests for + information about images. + """ -class ViewBuilder(object): - def __init__(self): - pass + def build(self, request, image_obj, detail=False): + """ + Return a standardized image structure for display by the API. + """ + image = { + "id": image_obj["id"], + "name": image_obj["name"], + } - def build(self, image_obj): - raise NotImplementedError() + if detail: + image.update({ + "created": image_obj["created_at"], + "updated": image_obj["updated_at"], + "status": image_obj["status"], + }) + return image -class ViewBuilderV11(ViewBuilder): - def __init__(self, base_url): - self.base_url = base_url - def generate_href(self, image_id): - return "%s/images/%s" % (self.base_url, image_id) +class Builder_v1_0(Builder): + pass + + +class Builder_v1_1(Builder): + """ + OpenStack API v1.1 Image Builder + """ + + def build(self, request, image_obj, detail=False): + """ + Return a standardized image structure for display by the API. + """ + image = Builder.build(self, request, image_obj, detail) + href = "%s/images/%s" % (request.application_url, image_obj["id"]) + + image["links"] = [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/json", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/xml", + "href": href, + }] + + return image diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 76f758929..c313192b7 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -42,8 +42,9 @@ FLAGS = flags.FLAGS class BaseImageServiceTests(object): - - """Tasks to test for all image services""" + """ + Tasks to test for all image services. + """ def test_create(self): @@ -173,10 +174,9 @@ class GlanceImageServiceTest(test.TestCase, class ImageControllerWithGlanceServiceTest(test.TestCase): - - """Test of the OpenStack API /images application controller""" - - # Registered images at start of each test. + """ + Test of the OpenStack API /images application controller w/Glance. + """ IMAGE_FIXTURES = [ {'id': '23g2ogk23k4hhkk4k42l', @@ -198,7 +198,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'deleted': False, 'is_public': True, 'status': 'available', - 'image_type': 'ramdisk'}] + 'image_type': 'ramdisk'}, + ] def setUp(self): super(ImageControllerWithGlanceServiceTest, self).setUp() @@ -219,36 +220,132 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): super(ImageControllerWithGlanceServiceTest, self).tearDown() def test_get_image_index(self): - req = webob.Request.blank('/v1.0/images') - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) + request = webob.Request.blank('/v1.0/images') + response = request.get_response(fakes.wsgi_app()) + + response_dict = json.loads(response.body) + response_list = response_dict["images"] + + for image in self.IMAGE_FIXTURES: + test_image = { + "id": image["id"], + "name": image["name"], + } + self.assertTrue(test_image in response_list) + + self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) + + def test_get_image_index_v1_1(self): + request = webob.Request.blank('/v1.1/images') + response = request.get_response(fakes.wsgi_app()) + + response_dict = json.loads(response.body) + response_list = response_dict["images"] + + for image in self.IMAGE_FIXTURES: + href = "http://localhost/v1.1/images/%s" % image["id"] + test_image = { + "id": image["id"], + "name": image["name"], + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/images/%s" % image["id"], + }, + { + "rel": "bookmark", + "type": "application/json", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/xml", + "href": href, + }], + } + print test_image + print + print response_list + self.assertTrue(test_image in response_list) + + self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) - fixture_index = [dict(id=f['id'], name=f['name']) for f - in self.IMAGE_FIXTURES] + def test_get_image_details(self): + request = webob.Request.blank('/v1.0/images/detail') + response = request.get_response(fakes.wsgi_app()) + + response_dict = json.loads(response.body) + response_list = response_dict["images"] + + for image in self.IMAGE_FIXTURES: + test_image = { + "id": image["id"], + "name": image["name"], + "updated": image["updated_at"], + "created": image["created_at"], + "status": image["status"], + } + self.assertTrue(test_image in response_list) + + self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) + + def test_get_image_details_v1_1(self): + request = webob.Request.blank('/v1.1/images/detail') + response = request.get_response(fakes.wsgi_app()) + + response_dict = json.loads(response.body) + response_list = response_dict["images"] + + for image in self.IMAGE_FIXTURES: + href = "http://localhost/v1.1/images/%s" % image["id"] + test_image = { + "id": image["id"], + "name": image["name"], + "updated": image["updated_at"], + "created": image["created_at"], + "status": image["status"], + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/json", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/xml", + "href": href, + }], + } + self.assertTrue(test_image in response_list) + + self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) + + def test_get_image_create_empty(self): + request = webob.Request.blank('/v1.1/images') + request.method = "POST" + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(400, response.status_int) + + def test_get_image_create_bad_no_name(self): + request = webob.Request.blank('/v1.1/images') + request.method = "POST" + request.content_type = "application/json" + request.body = json.dumps({ + "serverId": 1, + }) + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(400, response.status_int) + + def test_get_image_create_bad_no_id(self): + request = webob.Request.blank('/v1.1/images') + request.method = "POST" + request.content_type = "application/json" + request.body = json.dumps({ + "name" : "Snapshot Test", + }) + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(400, response.status_int) - for image in res_dict['images']: - self.assertEquals(1, fixture_index.count(image), - "image %s not in fixture index!" % str(image)) - def test_get_image_details(self): - req = webob.Request.blank('/v1.0/images/detail') - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - def _is_equivalent_subset(x, y): - if set(x) <= set(y): - for k, v in x.iteritems(): - if x[k] != y[k]: - if x[k] == 'active' and y[k] == 'available': - continue - return False - return True - return False - - for image in res_dict['images']: - for image_fixture in self.IMAGE_FIXTURES: - if _is_equivalent_subset(image, image_fixture): - break - else: - self.assertEquals(1, 2, "image %s not in fixtures!" % - str(image)) -- cgit From febbfd45a0c1dbd16093ab38897e94ddb331e9ea Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 17 Mar 2011 23:34:12 -0400 Subject: Updated naming, removed some prints, and removed some invalid tests. --- nova/api/openstack/__init__.py | 4 ++-- nova/api/openstack/images.py | 20 +++++++++++--------- nova/api/openstack/views/images.py | 8 ++++---- nova/tests/api/openstack/test_images.py | 31 ------------------------------- 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 0ac67fdba..1ec943f55 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -129,7 +129,7 @@ class APIRouterV10(APIRouter): member=self.server_members) mapper.resource("image", "images", - controller=images.Controller_v1_0(), + controller=images.ControllerV10(), collection={'detail': 'GET'}) @@ -142,7 +142,7 @@ class APIRouterV11(APIRouter): member=self.server_members) mapper.resource("image", "images", - controller=images.Controller_v1_1(), + controller=images.ControllerV11(), collection={'detail': 'GET'}) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 2357bfd3d..bc7338699 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -29,13 +29,15 @@ class Controller(wsgi.Controller): OpenStack API. Version-inspecific code goes here. """ - _builder = images_view.Builder_v1_0() - _serialization_metadata = { 'application/xml': { "attributes": { "image": ["id", "name", "updated", "created", "status", - "serverId", "progress"]}}} + "serverId", "progress"], + "link": ["rel", "type", "href"], + }, + }, + } def __init__(self, image_service=None, compute_service=None): """ @@ -109,8 +111,8 @@ class Controller(wsgi.Controller): raise exc.HTTPBadRequest() try: - server_id = image["serverId"] - image_name = image["name"] + server_id = image["image"]["serverId"] + image_name = image["image"]["name"] except KeyError: raise exc.HTTPBadRequest() @@ -118,15 +120,15 @@ class Controller(wsgi.Controller): return self._builder.build(req, image, True) -class Controller_v1_0(Controller): +class ControllerV10(Controller): """ Version 1.0 specific controller logic. """ - _builder = images_view.Builder_v1_0() + _builder = images_view.ViewBuilderV10() -class Controller_v1_1(Controller): +class ControllerV11(Controller): """ Version 1.1 specific controller logic. """ - _builder = images_view.Builder_v1_1() + _builder = images_view.ViewBuilderV11() diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 1631d1fe3..c41e60546 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -16,7 +16,7 @@ # under the License. -class Builder(object): +class ViewBuilder(object): """ Base class for generating responses to OpenStack API requests for information about images. @@ -41,11 +41,11 @@ class Builder(object): return image -class Builder_v1_0(Builder): +class ViewBuilderV10(ViewBuilder): pass -class Builder_v1_1(Builder): +class ViewBuilderV11(ViewBuilder): """ OpenStack API v1.1 Image Builder """ @@ -54,7 +54,7 @@ class Builder_v1_1(Builder): """ Return a standardized image structure for display by the API. """ - image = Builder.build(self, request, image_obj, detail) + image = ViewBuilder.build(self, request, image_obj, detail) href = "%s/images/%s" % (request.application_url, image_obj["id"]) image["links"] = [{ diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index c313192b7..c5a866bc7 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -262,9 +262,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): "href": href, }], } - print test_image - print - print response_list self.assertTrue(test_image in response_list) self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) @@ -321,31 +318,3 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertTrue(test_image in response_list) self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) - - def test_get_image_create_empty(self): - request = webob.Request.blank('/v1.1/images') - request.method = "POST" - response = request.get_response(fakes.wsgi_app()) - self.assertEqual(400, response.status_int) - - def test_get_image_create_bad_no_name(self): - request = webob.Request.blank('/v1.1/images') - request.method = "POST" - request.content_type = "application/json" - request.body = json.dumps({ - "serverId": 1, - }) - response = request.get_response(fakes.wsgi_app()) - self.assertEqual(400, response.status_int) - - def test_get_image_create_bad_no_id(self): - request = webob.Request.blank('/v1.1/images') - request.method = "POST" - request.content_type = "application/json" - request.body = json.dumps({ - "name" : "Snapshot Test", - }) - response = request.get_response(fakes.wsgi_app()) - self.assertEqual(400, response.status_int) - - -- cgit From fce6b6f8f39378f5f31b8aa432922374c744544e Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 18 Mar 2011 00:05:58 -0400 Subject: Become compatible with ironcamel and bcwaldon's implementations for standardness. --- nova/api/openstack/images.py | 31 +++++++++++++++++++++++-------- nova/api/openstack/views/images.py | 18 +++++++++++++++--- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index bc7338699..a192883fc 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -17,9 +17,9 @@ from webob import exc from nova import compute from nova import flags -from nova import log from nova import utils from nova import wsgi +from nova.api.openstack import common from nova.api.openstack.views import images as images_view @@ -39,6 +39,11 @@ class Controller(wsgi.Controller): }, } + _builder_dispatch = { + "1.0": images_view.ViewBuilderV10, + "1.1": images_view.ViewBuilderV11, + } + def __init__(self, image_service=None, compute_service=None): """ Initialize new `ImageController`. @@ -50,7 +55,17 @@ class Controller(wsgi.Controller): self.__compute = compute_service or compute.API() self.__image = image_service or _default_service - self.__log = log.getLogger(self.__class__.__name__) + + def get_builder(self, request): + """ + Property to get the ViewBuilder class we need to use. + """ + version = common.get_api_version(request) + base_url = request.application_url + try: + return self._builder_dispatch[version](base_url) + except KeyError: + raise exc.HTTPNotFound() def index(self, req): """ @@ -60,7 +75,7 @@ class Controller(wsgi.Controller): """ context = req.environ['nova.context'] images = self.__image.index(context) - build = self._builder.build + build = self.get_builder(req).build return dict(images=[build(req, image, False) for image in images]) def detail(self, req): @@ -71,7 +86,7 @@ class Controller(wsgi.Controller): """ context = req.environ['nova.context'] images = self.__image.detail(context) - build = self._builder.build + build = self.get_builder(req).build return dict(images=[build(req, image, True) for image in images]) def show(self, req, image_id): @@ -83,7 +98,7 @@ class Controller(wsgi.Controller): """ context = req.environ['nova.context'] image = self.__image.show(context, image_id) - return self._builder.build(req, image, True) + return self.get_builder(req).build(req, image, True) def delete(self, req, image_id): """ @@ -117,18 +132,18 @@ class Controller(wsgi.Controller): raise exc.HTTPBadRequest() image = self.__compute.snapshot(context, server_id, image_name) - return self._builder.build(req, image, True) + return self.get_builder(req).build(req, image, True) class ControllerV10(Controller): """ Version 1.0 specific controller logic. """ - _builder = images_view.ViewBuilderV10() + pass class ControllerV11(Controller): """ Version 1.1 specific controller logic. """ - _builder = images_view.ViewBuilderV11() + pass diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index c41e60546..313ba2eba 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -22,7 +22,19 @@ class ViewBuilder(object): information about images. """ - def build(self, request, image_obj, detail=False): + def __init__(self, base_url): + """ + Initialize new `ViewBuilder`. + """ + self._url = base_url + + def generate_href(self, image_id): + """ + Return an href string pointing to this object. + """ + return "%s/images/%s" % (self._url, image_id) + + def build(self, image_obj, detail=False): """ Return a standardized image structure for display by the API. """ @@ -50,12 +62,12 @@ class ViewBuilderV11(ViewBuilder): OpenStack API v1.1 Image Builder """ - def build(self, request, image_obj, detail=False): + def build(self, image_obj, detail=False): """ Return a standardized image structure for display by the API. """ image = ViewBuilder.build(self, request, image_obj, detail) - href = "%s/images/%s" % (request.application_url, image_obj["id"]) + href = self.generate_url(image_obj["id"]) image["links"] = [{ "rel": "self", -- cgit From c28ec048a56a3ead96dc7528ca50865945d40646 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 18 Mar 2011 00:19:53 -0400 Subject: Final touches and bug/pep8 fixes. --- nova/api/openstack/__init__.py | 1 - nova/api/openstack/images.py | 35 ++++++++++++++++++----------------- nova/api/openstack/servers.py | 1 + nova/api/openstack/views/images.py | 4 ++-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 1ec943f55..14cb6b331 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -146,7 +146,6 @@ class APIRouterV11(APIRouter): collection={'detail': 'GET'}) - class Versions(wsgi.Application): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index a192883fc..4cd989054 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -56,17 +56,6 @@ class Controller(wsgi.Controller): self.__compute = compute_service or compute.API() self.__image = image_service or _default_service - def get_builder(self, request): - """ - Property to get the ViewBuilder class we need to use. - """ - version = common.get_api_version(request) - base_url = request.application_url - try: - return self._builder_dispatch[version](base_url) - except KeyError: - raise exc.HTTPNotFound() - def index(self, req): """ Return an index listing of images available to the request. @@ -76,7 +65,7 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] images = self.__image.index(context) build = self.get_builder(req).build - return dict(images=[build(req, image, False) for image in images]) + return dict(images=[build(image, False) for image in images]) def detail(self, req): """ @@ -87,7 +76,7 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] images = self.__image.detail(context) build = self.get_builder(req).build - return dict(images=[build(req, image, True) for image in images]) + return dict(images=[build(image, True) for image in images]) def show(self, req, image_id): """ @@ -98,7 +87,7 @@ class Controller(wsgi.Controller): """ context = req.environ['nova.context'] image = self.__image.show(context, image_id) - return self.get_builder(req).build(req, image, True) + return self.get_builder().build(req, image, True) def delete(self, req, image_id): """ @@ -132,18 +121,30 @@ class Controller(wsgi.Controller): raise exc.HTTPBadRequest() image = self.__compute.snapshot(context, server_id, image_name) - return self.get_builder(req).build(req, image, True) + return self.get_builder(req).build(image, True) class ControllerV10(Controller): """ Version 1.0 specific controller logic. """ - pass + + def get_builder(self, request): + """ + Property to get the ViewBuilder class we need to use. + """ + base_url = request.application_url + return images_view.ViewBuilderV10(base_url) class ControllerV11(Controller): """ Version 1.1 specific controller logic. """ - pass + + def get_builder(self, request): + """ + Property to get the ViewBuilder class we need to use. + """ + base_url = request.application_url + return images_view.ViewBuilderV11(base_url) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 5f6fbd96c..73843f63e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -516,6 +516,7 @@ class Controller(wsgi.Controller): return kernel_id, ramdisk_id + class ControllerV10(Controller): def _image_id_from_req_data(self, data): return data['server']['imageId'] diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 313ba2eba..7daa6fe26 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -66,8 +66,8 @@ class ViewBuilderV11(ViewBuilder): """ Return a standardized image structure for display by the API. """ - image = ViewBuilder.build(self, request, image_obj, detail) - href = self.generate_url(image_obj["id"]) + image = ViewBuilder.build(self, image_obj, detail) + href = self.generate_href(image_obj["id"]) image["links"] = [{ "rel": "self", -- cgit From f0bb48fc2f2e7d9326c51b4b57e73e0258930909 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 23 Mar 2011 09:34:34 +0000 Subject: removed excess debug line --- nova/virt/xenapi/network_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py index 52ad0e1a5..546f6bea9 100644 --- a/nova/virt/xenapi/network_utils.py +++ b/nova/virt/xenapi/network_utils.py @@ -28,7 +28,6 @@ class NetworkHelper(HelperBase): """ The class that wraps the helper methods together. """ - @classmethod def find_network_with_name_label(cls, session, name_label): networks = session.call_xenapi('network.get_by_name_label', name_label) -- cgit From ff9e29e3ef56ec8b28f28d328ca010ce25f0c7b0 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 23 Mar 2011 09:47:22 -0400 Subject: Removed some un-needed code, and started adding tests for show(), which I forgot\! --- nova/api/openstack/images.py | 37 +++++++++++--------- nova/tests/api/openstack/test_images.py | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4cd989054..d8606e3c2 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,9 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. -from webob import exc +import webob.exc from nova import compute +from nova import exception from nova import flags from nova import utils from nova import wsgi @@ -39,11 +40,6 @@ class Controller(wsgi.Controller): }, } - _builder_dispatch = { - "1.0": images_view.ViewBuilderV10, - "1.1": images_view.ViewBuilderV11, - } - def __init__(self, image_service=None, compute_service=None): """ Initialize new `ImageController`. @@ -60,7 +56,7 @@ class Controller(wsgi.Controller): """ Return an index listing of images available to the request. - @param req: `webob.Request` object + @param req: `wsgi.Request` object """ context = req.environ['nova.context'] images = self.__image.index(context) @@ -71,31 +67,38 @@ class Controller(wsgi.Controller): """ Return a detailed index listing of images available to the request. - @param req: `webob.Request` object. + @param req: `wsgi.Request` object. """ context = req.environ['nova.context'] images = self.__image.detail(context) build = self.get_builder(req).build return dict(images=[build(image, True) for image in images]) - def show(self, req, image_id): + def show(self, req, id): """ Return detailed information about a specific image. - @param req: `webob.Request` object - @param image_id: Image identifier (integer) + @param req: `wsgi.Request` object + @param id: Image identifier (integer) """ + image_id = id context = req.environ['nova.context'] - image = self.__image.show(context, image_id) - return self.get_builder().build(req, image, True) - def delete(self, req, image_id): + try: + image = self.__image.show(context, image_id) + except exception.NotFound: + raise webob.exc.HTTPNotFound + + return self.get_builder(req).build(image, True) + + def delete(self, req, id): """ Delete an image, if allowed. - @param req: `webob.Request` object - @param image_id: Image identifier (integer) + @param req: `wsgi.Request` object + @param id: Image identifier (integer) """ + image_id = id context = req.environ['nova.context'] self.__image.delete(context, image_id) return exc.HTTPNoContent() @@ -104,7 +107,7 @@ class Controller(wsgi.Controller): """ Snapshot a server instance and save the image. - @param req: `webob.Request` object + @param req: `wsgi.Request` object """ context = req.environ['nova.context'] body = req.body diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index c5a866bc7..8828b0e34 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -235,6 +235,67 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) + def test_get_image(self): + request = webob.Request.blank('/v1.0/images/23g2ogk23k4hhkk4k42l') + response = request.get_response(fakes.wsgi_app()) + + actual_image = json.loads(response.body) + + expected = self.IMAGE_FIXTURES[0] + expected_image = { + "id": expected["id"], + "name": expected["name"], + "updated": expected["updated_at"], + "created": expected["created_at"], + "status": expected["status"], + } + + self.assertEqual(expected_image, actual_image) + + def test_get_image_v1_1(self): + request = webob.Request.blank('/v1.1/images/23g2ogk23k4hhkk4k42l') + response = request.get_response(fakes.wsgi_app()) + + actual_image = json.loads(response.body) + + expected = self.IMAGE_FIXTURES[0] + href = "http://localhost/v1.1/images/%s" % expected["id"] + + expected_image = { + "id": expected["id"], + "name": expected["name"], + "updated": expected["updated_at"], + "created": expected["created_at"], + "status": expected["status"], + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/json", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/xml", + "href": href, + }], + } + + self.assertEqual(expected_image, actual_image) + + def test_get_image_404(self): + request = webob.Request.blank('/v1.0/images/NonExistantImage') + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(404, response.status_int) + self.assertEqual("", response.body) + + def test_get_image_v1_1_404(self): + request = webob.Request.blank('/v1.1/images/NonExistantImage') + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(404, response.status_int) + def test_get_image_index_v1_1(self): request = webob.Request.blank('/v1.1/images') response = request.get_response(fakes.wsgi_app()) -- cgit From e9800364853078115cfb205bae263c3a55410b02 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 23 Mar 2011 11:04:20 -0400 Subject: Fixed tests. --- nova/tests/api/openstack/fakes.py | 4 ++-- nova/tests/api/openstack/test_images.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 16c7bc163..911eeaaad 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -79,9 +79,9 @@ def wsgi_app(inner_app10=None, inner_app11=None): inner_app11 = openstack.APIRouterV11() mapper = urlmap.URLMap() api10 = openstack.FaultWrapper(auth.AuthMiddleware( - ratelimiting.RateLimitingMiddleware(inner_app10))) + limits.RateLimitingMiddleware(inner_app10))) api11 = openstack.FaultWrapper(auth.AuthMiddleware( - ratelimiting.RateLimitingMiddleware(inner_app11))) + limits.RateLimitingMiddleware(inner_app11))) mapper['/v1.0'] = api10 mapper['/v1.1'] = api11 mapper['/'] = openstack.FaultWrapper(openstack.Versions()) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index a6ee23e1d..e93a1ea40 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -301,7 +301,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): request = webob.Request.blank('/v1.0/images/NonExistantImage') response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) - self.assertEqual("", response.body) def test_get_image_v1_1_404(self): request = webob.Request.blank('/v1.1/images/NonExistantImage') -- cgit From 572b6d30c809af6e117d96de9a5a2d845c1eeda0 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Wed, 23 Mar 2011 11:52:43 -0400 Subject: Testing of XML and JSON for show(), and conformance to API spec for JSON. --- nova/api/openstack/images.py | 6 +- nova/tests/api/openstack/test_images.py | 159 +++++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 27 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index d8606e3c2..38c8a7306 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -21,6 +21,7 @@ from nova import flags from nova import utils from nova import wsgi from nova.api.openstack import common +from nova.api.openstack import faults from nova.api.openstack.views import images as images_view @@ -87,9 +88,10 @@ class Controller(wsgi.Controller): try: image = self.__image.show(context, image_id) except exception.NotFound: - raise webob.exc.HTTPNotFound + ex = webob.exc.HTTPNotFound(explanation="Image not found.") + raise faults.Fault(ex) - return self.get_builder(req).build(image, True) + return dict(image=self.get_builder(req).build(image, True)) def delete(self, req, id): """ diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index e93a1ea40..deb8f1744 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -26,6 +26,8 @@ import os import shutil import tempfile +from xml.dom.minidom import parseString + import stubout import webob @@ -255,11 +257,13 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): expected = self.IMAGE_FIXTURES[0] expected_image = { - "id": expected["id"], - "name": expected["name"], - "updated": expected["updated_at"], - "created": expected["created_at"], - "status": expected["status"], + "image": { + "id": expected["id"], + "name": expected["name"], + "updated": expected["updated_at"], + "created": expected["created_at"], + "status": expected["status"], + }, } self.assertEqual(expected_image, actual_image) @@ -274,39 +278,142 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): href = "http://localhost/v1.1/images/%s" % expected["id"] expected_image = { - "id": expected["id"], - "name": expected["name"], - "updated": expected["updated_at"], - "created": expected["created_at"], - "status": expected["status"], - "links": [{ - "rel": "self", - "href": href, - }, - { - "rel": "bookmark", - "type": "application/json", - "href": href, + "image": { + "id": expected["id"], + "name": expected["name"], + "updated": expected["updated_at"], + "created": expected["created_at"], + "status": expected["status"], + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/json", + "href": href, + }, + { + "rel": "bookmark", + "type": "application/xml", + "href": href, + }], }, - { - "rel": "bookmark", - "type": "application/xml", - "href": href, - }], } self.assertEqual(expected_image, actual_image) - def test_get_image_404(self): + def test_get_image_xml(self): + request = webob.Request.blank('/v1.0/images/23g2ogk23k4hhkk4k42l') + request.accept = "application/xml" + response = request.get_response(fakes.wsgi_app()) + + actual_image = parseString(response.body.replace(" ", "")) + + expected = self.IMAGE_FIXTURES[0] + expected_image = parseString(""" + + """ % (expected)) + + self.assertEqual(expected_image.toxml(), actual_image.toxml()) + + def test_get_image_v1_1_xml(self): + request = webob.Request.blank('/v1.1/images/23g2ogk23k4hhkk4k42l') + request.accept = "application/xml" + response = request.get_response(fakes.wsgi_app()) + + actual_image = parseString(response.body.replace(" ", "")) + + expected = self.IMAGE_FIXTURES[0] + expected["href"] = "http://localhost/v1.1/images/23g2ogk23k4hhkk4k42l" + expected_image = parseString(""" + + + + + + + + """.replace(" ", "") % (expected)) + + self.assertEqual(expected_image.toxml(), actual_image.toxml()) + + def test_get_image_404_json(self): + request = webob.Request.blank('/v1.0/images/NonExistantImage') + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(404, response.status_int) + + expected = { + "itemNotFound": { + "message": "Image not found.", + "code": 404, + }, + } + + actual = json.loads(response.body) + + self.assertEqual(expected, actual) + + def test_get_image_404_xml(self): request = webob.Request.blank('/v1.0/images/NonExistantImage') + request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) - def test_get_image_v1_1_404(self): + expected = parseString(""" + + + Image not found. + + + """.replace(" ", "")) + + actual = parseString(response.body.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_get_image_404_v1_1_json(self): request = webob.Request.blank('/v1.1/images/NonExistantImage') response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) + expected = { + "itemNotFound": { + "message": "Image not found.", + "code": 404, + }, + } + + actual = json.loads(response.body) + + self.assertEqual(expected, actual) + + def test_get_image_404_v1_1_xml(self): + request = webob.Request.blank('/v1.1/images/NonExistantImage') + request.accept = "application/xml" + response = request.get_response(fakes.wsgi_app()) + self.assertEqual(404, response.status_int) + + expected = parseString(""" + + + Image not found. + + + """.replace(" ", "")) + + actual = parseString(response.body.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_get_image_index_v1_1(self): request = webob.Request.blank('/v1.1/images') response = request.get_response(fakes.wsgi_app()) @@ -338,6 +445,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(len(response_list), len(self.IMAGE_FIXTURES)) + + def test_get_image_details(self): request = webob.Request.blank('/v1.0/images/detail') response = request.get_response(fakes.wsgi_app()) -- cgit From 1378db7ac86b69b8a966448b63415b2136b6b5bc Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 24 Mar 2011 09:07:57 -0400 Subject: Fix up formatting of libvirt.xml.template --- nova/virt/libvirt.xml.template | 132 ++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 77c1b1997..26f528cb1 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -3,47 +3,47 @@ ${memory_kb} #if $type == 'lxc' - #set $disk_prefix = '' + #set $disk prefix = '' #set $disk_bus = '' exe /sbin/init #else -#if $type == 'uml' - #set $disk_prefix = 'ubd' - #set $disk_bus = 'uml' - uml - /usr/bin/linux - /dev/ubda -#else - #if $type == 'xen' - #set $disk_prefix = 'sd' - #set $disk_bus = 'scsi' - linux - /dev/xvda - #else - #set $disk_prefix = 'vd' - #set $disk_bus = 'virtio' - hvm - #end if - #if $getVar('rescue', False) - ${basepath}/kernel.rescue - ${basepath}/ramdisk.rescue - #else - #if $getVar('kernel', None) - ${kernel} - #if $type == 'xen' - ro - #else - root=/dev/vda console=ttyS0 - #end if - #if $getVar('ramdisk', None) - ${ramdisk} - #end if - #else - - #end if - #end if - #end if + #if $type == 'uml' + #set $disk_prefix = 'ubd' + #set $disk_bus = 'uml' + uml + /usr/bin/linux + /dev/ubda + #else + #if $type == 'xen' + #set $disk_prefix = 'sd' + #set $disk_bus = 'scsi' + linux + /dev/xvda + #else + #set $disk_prefix = 'vd' + #set $disk_bus = 'virtio' + hvm + #end if + #if $getVar('rescue', False) + ${basepath}/kernel.rescue + ${basepath}/ramdisk.rescue + #else + #if $getVar('kernel', None) + ${kernel} + #if $type == 'xen' + ro + #else + root=/dev/vda console=ttyS0 + #end if + #if $getVar('ramdisk', None) + ${ramdisk} + #end if + #else + + #end if + #end if + #end if #end if @@ -52,36 +52,36 @@ ${vcpus} #if $type == 'lxc' - - - - -#else -#if $getVar('rescue', False) - - - - - - - - - - + + + + #else - - - - - - #if $getVar('local', False) - - - - - - #end if - #end if + #if $getVar('rescue', False) + + + + + + + + + + + #else + + + + + + #if $getVar('local', False) + + + + + + #end if +#end if #end if -- cgit From da159d18b56af44f93cbf2c5e80b6aa3c98d5187 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 24 Mar 2011 09:12:24 -0400 Subject: Dont use popen in dettaching the lxc loop --- nova/virt/disk.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index f6e6795d6..0bdb04cde 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -140,7 +140,8 @@ def destroy_container(target, instance): container_dir = '%s/rootfs' % target utils.execute('sudo', 'umount', container_dir) finally: - for loop in utils.popen('sudo losetup -a').readlines(): + out, err = utils('sudo', 'losetup', '-a') + for loop in out.splitlines(): if instance['name'] in loop: device = loop.split(loop, ':') utils.execute('sudo', 'losetup', '--detach', device) -- cgit From fa6b969a2a7c9252aaebc4a56d82f9b04e46910c Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 24 Mar 2011 10:34:34 -0400 Subject: Merged trunk and fixed tests. --- nova/tests/api/openstack/test_images.py | 40 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index f64ee16a1..3a5d821d3 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -20,6 +20,7 @@ Tests of the new image services, both as a service layer, and as a WSGI layer """ +import copy import json import datetime import os @@ -193,6 +194,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): """ # Registered images at start of each test. now = datetime.datetime.utcnow() + formated_date = now.strftime('%Y-%m-%dT%H:%M:%SZ') IMAGE_FIXTURES = [ {'id': '23g2ogk23k4hhkk4k42l', 'imageId': '23g2ogk23k4hhkk4k42l', @@ -256,13 +258,13 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): actual_image = json.loads(response.body) - expected = self.IMAGE_FIXTURES[0] + expected = copy.copy(self.IMAGE_FIXTURES[0]) expected_image = { "image": { "id": expected["id"], "name": expected["name"], - "updated": expected["updated_at"], - "created": expected["created_at"], + "updated": self.formated_date, + "created": self.formated_date, "status": expected["status"], }, } @@ -275,15 +277,15 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): actual_image = json.loads(response.body) - expected = self.IMAGE_FIXTURES[0] + expected = copy.copy(self.IMAGE_FIXTURES[0]) href = "http://localhost/v1.1/images/%s" % expected["id"] expected_image = { "image": { "id": expected["id"], "name": expected["name"], - "updated": expected["updated_at"], - "created": expected["created_at"], + "updated": self.formated_date, + "created": self.formated_date, "status": expected["status"], "links": [{ "rel": "self", @@ -311,7 +313,9 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): actual_image = parseString(response.body.replace(" ", "")) - expected = self.IMAGE_FIXTURES[0] + expected = copy.copy(self.IMAGE_FIXTURES[0]) + expected["updated_at"] = self.formated_date + expected["created_at"] = self.formated_date expected_image = parseString(""" Date: Thu, 24 Mar 2011 12:15:33 -0400 Subject: Renamed __image and __compute to better describe their purposes. Use os.path.join to create href as per suggestion. Added base get_builder as per pychecker suggestion. --- nova/api/openstack/images.py | 24 ++++++++++++++---------- nova/api/openstack/views/images.py | 4 +++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index dc07348e4..b01d991e8 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -50,8 +50,8 @@ class Controller(wsgi.Controller): """ _default_service = utils.import_object(flags.FLAGS.image_service) - self.__compute = compute_service or compute.API() - self.__image = image_service or _default_service + self._compute_service = compute_service or compute.API() + self._image_service = image_service or _default_service def index(self, req): """ @@ -60,7 +60,7 @@ class Controller(wsgi.Controller): @param req: `wsgi.Request` object """ context = req.environ['nova.context'] - images = self.__image.index(context) + images = self._image_service.index(context) build = self.get_builder(req).build return dict(images=[build(image, False) for image in images]) @@ -71,7 +71,7 @@ class Controller(wsgi.Controller): @param req: `wsgi.Request` object. """ context = req.environ['nova.context'] - images = self.__image.detail(context) + images = self._image_service.detail(context) build = self.get_builder(req).build return dict(images=[build(image, True) for image in images]) @@ -86,7 +86,7 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] try: - image = self.__image.show(context, image_id) + image = self._image_service.show(context, image_id) except exception.NotFound: ex = webob.exc.HTTPNotFound(explanation="Image not found.") raise faults.Fault(ex) @@ -102,8 +102,8 @@ class Controller(wsgi.Controller): """ image_id = id context = req.environ['nova.context'] - self.__image.delete(context, image_id) - return exc.HTTPNoContent() + self._image_service.delete(context, image_id) + return webob.exc.HTTPNoContent() def create(self, req): """ @@ -116,17 +116,21 @@ class Controller(wsgi.Controller): image = self._deserialize(req.body, content_type) if not image: - raise exc.HTTPBadRequest() + raise webob.exc.HTTPBadRequest() try: server_id = image["image"]["serverId"] image_name = image["image"]["name"] except KeyError: - raise exc.HTTPBadRequest() + raise webob.exc.HTTPBadRequest() - image = self.__compute.snapshot(context, server_id, image_name) + image = self._compute_service.snapshot(context, server_id, image_name) return self.get_builder(req).build(image, True) + def get_builder(self, request): + """Indicates that you must use a Controller subclass.""" + raise NotImplementedError + class ControllerV10(Controller): """ diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 9d0bad095..bab1f0bac 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -15,6 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os.path + class ViewBuilder(object): """ @@ -37,7 +39,7 @@ class ViewBuilder(object): """ Return an href string pointing to this object. """ - return "%s/images/%s" % (self._url, image_id) + return os.path.join(self._url, "images", str(image_id)) def build(self, image_obj, detail=False): """ -- cgit From 3d06c636537374557ee6ff77b7c0bc7718eafcdb Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 24 Mar 2011 18:59:11 -0400 Subject: Added detail keywork and i18n as per suggestions. --- nova/api/openstack/images.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index b01d991e8..be33bb656 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -62,7 +62,7 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] images = self._image_service.index(context) build = self.get_builder(req).build - return dict(images=[build(image, False) for image in images]) + return dict(images=[build(image, detail=False) for image in images]) def detail(self, req): """ @@ -73,7 +73,7 @@ class Controller(wsgi.Controller): context = req.environ['nova.context'] images = self._image_service.detail(context) build = self.get_builder(req).build - return dict(images=[build(image, True) for image in images]) + return dict(images=[build(image, detail=True) for image in images]) def show(self, req, id): """ @@ -88,10 +88,10 @@ class Controller(wsgi.Controller): try: image = self._image_service.show(context, image_id) except exception.NotFound: - ex = webob.exc.HTTPNotFound(explanation="Image not found.") + ex = webob.exc.HTTPNotFound(explanation=_("Image not found.")) raise faults.Fault(ex) - return dict(image=self.get_builder(req).build(image, True)) + return dict(image=self.get_builder(req).build(image, detail=True)) def delete(self, req, id): """ @@ -125,7 +125,7 @@ class Controller(wsgi.Controller): raise webob.exc.HTTPBadRequest() image = self._compute_service.snapshot(context, server_id, image_name) - return self.get_builder(req).build(image, True) + return self.get_builder(req).build(image, detail=True) def get_builder(self, request): """Indicates that you must use a Controller subclass.""" -- cgit From 9632db75d229eac16970af1dfabbb047c2b71a4e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 25 Mar 2011 08:20:56 -0400 Subject: Removed partition from setup_container --- nova/virt/disk.py | 4 ++-- nova/virt/libvirt_conn.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 0bdb04cde..bbdcc8ddf 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -115,13 +115,13 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): _unlink_device(device, nbd) -def setup_container(image, container_dir=None, partition=None): +def setup_container(image, container_dir=None): """Setup the LXC container It will mount the loopback image to the container directory in order to create the root filesystem for the container """ - nbd = False + nbd = "False" device = _link_device(image, nbd) err = utils.execute('sudo', 'mount', device, container_dir) if err: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9fb8ba955..bfb0a75f1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -798,8 +798,7 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'lxc': disk.setup_container(basepath('disk'), - container_dir=container_dir, - partition=target_partition) + container_dir=container_dir) except Exception as e: # This could be a windows image, or a vmdk format disk LOG.warn(_('instance %(inst_name)s: ignoring error injecting' -- cgit From 47b54662c17c7af0bca9cf96dc5d4a498706fe8b Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 25 Mar 2011 08:37:47 -0400 Subject: Dont always assume qemu --- nova/virt/libvirt_conn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bfb0a75f1..840eaceeb 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1053,7 +1053,8 @@ class LibvirtConnection(driver.ComputeDriver): total = 0 for dom_id in self._conn.listDomainsID(): dom = self._conn.lookupByID(dom_id) - total += len(dom.vcpus()[1]) + if dom is None: + total += len(dom.vcpus()[1]) return total def get_memory_mb_used(self): -- cgit From 3d8b55294702b531a570b279fb29db8d4ea104d3 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 25 Mar 2011 08:52:18 -0400 Subject: Fix up templating --- nova/virt/libvirt.xml.template | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index c2c5384bb..26f528cb1 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -83,24 +83,21 @@ #end if #end if #end if - -#for $nic in $nics - - + + - - - -#if $getVar('nic.extra_params', False) - ${nic.extra_params} + + + +#if $getVar('extra_params', False) + ${extra_params} #end if -#if $getVar('nic.gateway_v6', False) - +#if $getVar('gateway_v6', False) + #end if -#end for -- cgit From 6ce4f9c6ae00138184e79cdcfb6f78fc3474580e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 25 Mar 2011 08:52:54 -0400 Subject: Fix up destroy container --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index bbdcc8ddf..b9a8fb7e7 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -140,7 +140,7 @@ def destroy_container(target, instance): container_dir = '%s/rootfs' % target utils.execute('sudo', 'umount', container_dir) finally: - out, err = utils('sudo', 'losetup', '-a') + out, err = utils.execute('sudo', 'losetup', '-a') for loop in out.splitlines(): if instance['name'] in loop: device = loop.split(loop, ':') -- cgit From c8fb0c5a16852afc98349edf89bb31afac166749 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 25 Mar 2011 09:39:20 -0400 Subject: Revert dom check --- nova/virt/libvirt_conn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 840eaceeb..bfb0a75f1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1053,8 +1053,7 @@ class LibvirtConnection(driver.ComputeDriver): total = 0 for dom_id in self._conn.listDomainsID(): dom = self._conn.lookupByID(dom_id) - if dom is None: - total += len(dom.vcpus()[1]) + total += len(dom.vcpus()[1]) return total def get_memory_mb_used(self): -- cgit From 51c07f77686473bc73c700aacc7baeecf278a948 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 25 Mar 2011 16:57:21 -0400 Subject: Removed print. --- nova/tests/api/openstack/test_images.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index b51a52cfe..3bf710d1a 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -642,7 +642,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): def test_get_image_found(self): req = webob.Request.blank('/v1.0/images/123') res = req.get_response(fakes.wsgi_app()) - print self.fixtures image_meta = json.loads(res.body)['image'] expected = {'id': 123, 'name': 'public image', 'updated': self.NOW_API_FORMAT, -- cgit From a78c1bd3e862700dbab68cc5011197270abd4b38 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 25 Mar 2011 21:46:14 -0400 Subject: Replaced import of an object with module import as per suggestion. --- nova/tests/api/openstack/test_images.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 3bf710d1a..5279ca77c 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -26,8 +26,7 @@ import datetime import os import shutil import tempfile - -from xml.dom.minidom import parseString +import xml.dom.minidom as minidom import stubout import webob @@ -326,10 +325,10 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) - actual_image = parseString(response.body.replace(" ", "")) + actual_image = minidom.parseString(response.body.replace(" ", "")) expected_now = self.NOW_API_FORMAT - expected_image = parseString(""" + expected_image = minidom.parseString(""" Image not found. @@ -396,7 +395,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): """.replace(" ", "")) - actual = parseString(response.body.replace(" ", "")) + actual = minidom.parseString(response.body.replace(" ", "")) self.assertEqual(expected.toxml(), actual.toxml()) @@ -422,7 +421,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) - expected = parseString(""" + expected = minidom.parseString(""" Image not found. @@ -430,7 +429,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): """.replace(" ", "")) - actual = parseString(response.body.replace(" ", "")) + actual = minidom.parseString(response.body.replace(" ", "")) self.assertEqual(expected.toxml(), actual.toxml()) -- cgit From cb8a13e3751cc12f7157094d094c7a26d6f583f0 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sun, 27 Mar 2011 15:30:47 -0400 Subject: Fix utils checking --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index fd45e2c51..15cd49789 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -124,7 +124,7 @@ def setup_container(image, container_dir=None): """ nbd = "False" device = _link_device(image, nbd) - err = utils.execute('sudo', 'mount', device, container_dir) + out, err = utils.execute('sudo', 'mount', device, container_dir) if err: raise exception.Error(_('Failed to mount filesystem: %s') % err) -- cgit From 8957914ad9dd7691b2a43d977d845e00f7dd48c4 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 28 Mar 2011 10:54:29 +0100 Subject: addressed termies review (first round) --- nova/network/xenapi_net.py | 26 +++---- nova/tests/db/fakes.py | 8 +- nova/tests/fake_utils.py | 11 +-- nova/tests/fake_utils.py.moved | 106 --------------------------- nova/tests/test_xenapi.py | 37 +++++----- nova/virt/xenapi/network_utils.py | 2 +- nova/virt/xenapi/vmops.py | 149 ++++++++++++++++++++++---------------- 7 files changed, 125 insertions(+), 214 deletions(-) delete mode 100644 nova/tests/fake_utils.py.moved diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 1725a4b7a..7a9da8046 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -35,19 +35,19 @@ FLAGS = flags.FLAGS def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): - """Create a vlan and bridge unless they already exist""" - #open xenapi session + """Create a vlan and bridge unless they already exist.""" + # Open xenapi session LOG.debug("ENTERING ensure_vlan_bridge in xenapi net") url = FLAGS.xenapi_connection_url username = FLAGS.xenapi_connection_username password = FLAGS.xenapi_connection_password session = XenAPISession(url, username, password) - #check whether bridge already exists - #retrieve network whose name_label is "bridge" + # Check whether bridge already exists + # Retrieve network whose name_label is "bridge" network_ref = NetworkHelper.find_network_with_name_label(session, bridge) if network_ref == None: - #if bridge does not exists - #1 - create network + # If bridge does not exists + # 1 - create network description = "network for nova bridge %s" % bridge network_rec = { 'name_label': bridge, @@ -55,28 +55,28 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): 'other_config': {}, } network_ref = session.call_xenapi('network.create', network_rec) - #2 - find PIF for VLAN + # 2 - find PIF for VLAN expr = 'field "device" = "%s" and \ field "VLAN" = "-1"' % FLAGS.vlan_interface pifs = session.call_xenapi('PIF.get_all_records_where', expr) pif_ref = None - #multiple PIF are ok: we are dealing with a pool + # Multiple PIF are ok: we are dealing with a pool if len(pifs) == 0: raise Exception( _('Found no PIF for device %s') % FLAGS.vlan_interface) - #3 - create vlan for network + # 3 - create vlan for network for pif_ref in pifs.keys(): session.call_xenapi('VLAN.create', pif_ref, str(vlan_num), network_ref) else: - #check VLAN tag is appropriate + # Check VLAN tag is appropriate network_rec = session.call_xenapi('network.get_record', network_ref) - #retrieve PIFs from network + # Retrieve PIFs from network for pif_ref in network_rec['PIFs']: - #retrieve VLAN from PIF + # Retrieve VLAN from PIF pif_rec = session.call_xenapi('PIF.get_record', pif_ref) pif_vlan = int(pif_rec['VLAN']) - #raise an exception if VLAN <> vlan_num + # Raise an exception if VLAN <> vlan_num if pif_vlan != vlan_num: raise Exception(_("PIF %(pif_rec['uuid'])s for network " "%(bridge)s has VLAN id %(pif_vlan)d. " diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 05a47d4c9..f7610aa56 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -25,7 +25,7 @@ from nova import utils def stub_out_db_instance_api(stubs, injected=True): - """ Stubs out the db API for creating Instances """ + """Stubs out the db API for creating Instances.""" INSTANCE_TYPES = { 'm1.tiny': dict(memory_mb=512, @@ -91,7 +91,7 @@ def stub_out_db_instance_api(stubs, injected=True): 'network_id': 'fake_flat'} class FakeModel(object): - """ Stubs out for model """ + """Stubs out for model.""" def __init__(self, values): self.values = values @@ -111,7 +111,7 @@ def stub_out_db_instance_api(stubs, injected=True): return INSTANCE_TYPES[name] def fake_network_get_by_instance(context, instance_id): - #even instance numbers are on vlan networks + # Even instance numbers are on vlan networks if instance_id % 2 == 0: return FakeModel(vlan_network_fields) else: @@ -119,7 +119,7 @@ def stub_out_db_instance_api(stubs, injected=True): return FakeModel(network_fields) def fake_network_get_all_by_instance(context, instance_id): - #even instance numbers are on vlan networks + # Even instance numbers are on vlan networks if instance_id % 2 == 0: return [FakeModel(vlan_network_fields)] else: diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py index 823c775cb..23996ba95 100644 --- a/nova/tests/fake_utils.py +++ b/nova/tests/fake_utils.py @@ -14,8 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""This modules stubs out functions in nova.utils -""" +"""This modules stubs out functions in nova.utils.""" import re import types @@ -42,22 +41,20 @@ def fake_execute_clear_log(): def fake_execute_set_repliers(repliers): - """Allows the client to configure replies to commands""" + """Allows the client to configure replies to commands.""" global _fake_execute_repliers _fake_execute_repliers = repliers def fake_execute_default_reply_handler(*ignore_args, **ignore_kwargs): """A reply handler for commands that haven't been added to the reply - list. Returns empty strings for stdout and stderr - """ + list. Returns empty strings for stdout and stderr.""" return '', '' def fake_execute(*cmd_parts, **kwargs): """This function stubs out execute, optionally executing - a preconfigued function to return expected data - """ + a preconfigued function to return expected data.""" global _fake_execute_repliers process_input = kwargs.get('process_input', None) diff --git a/nova/tests/fake_utils.py.moved b/nova/tests/fake_utils.py.moved deleted file mode 100644 index 8982f50be..000000000 --- a/nova/tests/fake_utils.py.moved +++ /dev/null @@ -1,106 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2011 Citrix Systems, Inc. -# -# 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. - -"""This modules stubs out functions in nova.utils -""" - -import re -import types - -from eventlet import greenthread - -from nova import exception -from nova import log as logging -from nova import utils - -LOG = logging.getLogger('nova.tests.fake_utils') - -_fake_execute_repliers = [] -_fake_execute_log = [] - - -def fake_execute_get_log(): - global _fake_execute_log - return _fake_execute_log - - -def fake_execute_clear_log(): - global _fake_execute_log - _fake_execute_log = [] - - -def fake_execute_set_repliers(repliers): - """Allows the client to configure replies to commands""" - global _fake_execute_repliers - _fake_execute_repliers = repliers - - -def fake_execute_default_reply_handler(*ignore_args, **ignore_kwargs): - """A reply handler for commands that haven't been added to the reply - list. Returns empty strings for stdout and stderr - """ - return '', '' - - -def fake_execute(*cmd, **kwargs): - """This function stubs out execute, optionally executing - a preconfigued function to return expected data - """ - global _fake_execute_repliers - - process_input = kwargs.get('process_input', None) - addl_env = kwargs.get('addl_env', None) - check_exit_code = kwargs.get('check_exit_code', 0) - cmd_map = map(str, cmd) - cmd_str = ' '.join(cmd_map) - - LOG.debug(_("Faking execution of cmd (subprocess): %s"), cmd_str) - _fake_execute_log.append(cmd_str) - - reply_handler = fake_execute_default_reply_handler - - for fake_replier in _fake_execute_repliers: - if re.match(fake_replier[0], cmd_str): - reply_handler = fake_replier[1] - LOG.debug(_('Faked command matched %s') % fake_replier[0]) - break - - if isinstance(reply_handler, types.StringTypes): - # If the reply handler is a string, return it as stdout - reply = reply_handler, '' - else: - try: - # Alternative is a function, so call it - reply = reply_handler(cmd, - process_input=process_input, - addl_env=addl_env, - check_exit_code=check_exit_code) - except exception.ProcessExecutionError as e: - LOG.debug(_('Faked command raised an exception %s' % str(e))) - raise - - LOG.debug(_("Reply to faked command is stdout='%(0)s' stderr='%(1)s'") % - {'0': reply[0], '1': reply[1]}) - - # Replicate the sleep call in the real function - greenthread.sleep(0) - return reply - - -def stub_out_utils_execute(stubs): - fake_execute_set_repliers([]) - fake_execute_clear_log() - stubs.Set(utils, 'execute', fake_execute) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index f91f37d4b..6ec0525a7 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -15,7 +15,7 @@ # under the License. """ -Test suite for XenAPI +Test suite for XenAPI. """ import functools @@ -66,7 +66,7 @@ def stub_vm_utils_with_vdi_attached_here(function, should_return=True): class XenAPIVolumeTestCase(test.TestCase): """ - Unit tests for Volume operations + Unit tests for Volume operations. """ def setUp(self): super(XenAPIVolumeTestCase, self).setUp() @@ -76,7 +76,6 @@ class XenAPIVolumeTestCase(test.TestCase): FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' db_fakes.stub_out_db_instance_api(self.stubs) - #db_fakes.stub_out_db_network_api(self.stubs) stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() self.values = {'id': 1, @@ -102,7 +101,7 @@ class XenAPIVolumeTestCase(test.TestCase): return db.volume_create(self.context, vol) def test_create_iscsi_storage(self): - """ This shows how to test helper classes' methods """ + """This shows how to test helper classes' methods.""" stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') helper = volume_utils.VolumeHelper @@ -117,7 +116,7 @@ class XenAPIVolumeTestCase(test.TestCase): db.volume_destroy(context.get_admin_context(), vol['id']) def test_parse_volume_info_raise_exception(self): - """ This shows how to test helper classes' methods """ + """This shows how to test helper classes' methods.""" stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') helper = volume_utils.VolumeHelper @@ -131,7 +130,7 @@ class XenAPIVolumeTestCase(test.TestCase): db.volume_destroy(context.get_admin_context(), vol['id']) def test_attach_volume(self): - """ This shows how to test Ops classes' methods """ + """This shows how to test Ops classes' methods.""" stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests) conn = xenapi_conn.get_connection(False) volume = self._create_volume() @@ -150,7 +149,7 @@ class XenAPIVolumeTestCase(test.TestCase): check() def test_attach_volume_raise_exception(self): - """ This shows how to test when exceptions are raised """ + """This shows how to test when exceptions are raised.""" stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeFailedTests) conn = xenapi_conn.get_connection(False) @@ -174,7 +173,7 @@ def reset_network(*args): class XenAPIVMTestCase(test.TestCase): """ - Unit tests for VM operations + Unit tests for VM operations. """ def setUp(self): super(XenAPIVMTestCase, self).setUp() @@ -475,21 +474,21 @@ class XenAPIVMTestCase(test.TestCase): network_manager='nova.network.manager.VlanManager', network_driver='nova.network.xenapi_net', vlan_interface='fake0') - #reset network table + # Reset network table xenapi_fake.reset_table('network') - #instance id = 2 will use vlan network (see db/fakes.py) + # Instance id = 2 will use vlan network (see db/fakes.py) fake_instance_id = 2 network_bk = self.network - #ensure we use xenapi_net driver + # Ensure we use xenapi_net driver self.network = utils.import_object(FLAGS.network_manager) self.network.setup_compute_network(None, fake_instance_id) self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, glance_stubs.FakeGlance.IMAGE_KERNEL, glance_stubs.FakeGlance.IMAGE_RAMDISK, instance_id=fake_instance_id) - #TODO(salvatore-orlando): a complete test here would require - #a check for making sure the bridge for the VM's VIF is - #consistent with bridge specified in nova db + # TODO(salvatore-orlando): a complete test here would require + # A check for making sure the bridge for the VM's VIF is + # consistent with bridge specified in nova db self.network = network_bk def test_spawn_with_network_qos(self): @@ -521,7 +520,7 @@ class XenAPIVMTestCase(test.TestCase): self.stubs.UnsetAll() def _create_instance(self): - """Creates and spawns a test instance""" + """Creates and spawns a test instance.""" stubs.stubout_loopingcall_start(self.stubs) values = { 'id': 1, @@ -540,7 +539,7 @@ class XenAPIVMTestCase(test.TestCase): class XenAPIDiffieHellmanTestCase(test.TestCase): """ - Unit tests for Diffie-Hellman code + Unit tests for Diffie-Hellman code. """ def setUp(self): super(XenAPIDiffieHellmanTestCase, self).setUp() @@ -566,7 +565,7 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): class XenAPIMigrateInstance(test.TestCase): """ - Unit test for verifying migration-related actions + Unit test for verifying migration-related actions. """ def setUp(self): @@ -623,7 +622,7 @@ class XenAPIMigrateInstance(test.TestCase): class XenAPIDetermineDiskImageTestCase(test.TestCase): """ - Unit tests for code that detects the ImageType + Unit tests for code that detects the ImageType. """ def setUp(self): super(XenAPIDetermineDiskImageTestCase, self).setUp() @@ -644,7 +643,7 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): def test_instance_disk(self): """ - If a kernel is specified then the image type is DISK (aka machine) + If a kernel is specified then the image type is DISK (aka machine). """ FLAGS.xenapi_image_service = 'objectstore' self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_MACHINE diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py index 546f6bea9..94d8e5199 100644 --- a/nova/virt/xenapi/network_utils.py +++ b/nova/virt/xenapi/network_utils.py @@ -44,7 +44,7 @@ class NetworkHelper(HelperBase): """ Return the network on which the bridge is attached, if found. The bridge is defined in the nova db and can be found either in the - 'bridge' or 'name_label' fields of the XenAPI network record + 'bridge' or 'name_label' fields of the XenAPI network record. """ expr = 'field "name__label" = "%s" or ' \ 'field "bridge" = "%s"' % (bridge, bridge) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 52e1f9eba..9b18ac732 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -58,7 +58,7 @@ class VMOps(object): VMHelper.XenAPI = self.XenAPI def list_instances(self): - """List VM instances""" + """List VM instances.""" # TODO(justinsb): Should we just always use the details method? # Seems to be the same number of API calls.. vm_refs = [] @@ -69,7 +69,7 @@ class VMOps(object): return vm_refs def list_instances_detail(self): - """List VM instances, returning InstanceInfo objects""" + """List VM instances, returning InstanceInfo objects.""" instance_infos = [] for vm_ref in self._session.get_xenapi().VM.get_all(): vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) @@ -119,11 +119,11 @@ class VMOps(object): self._spawn(instance, vm_ref) def spawn_rescue(self, instance): - """Spawn a rescue instance""" + """Spawn a rescue instance.""" self.spawn(instance) def _create_vm(self, instance, vdi_uuid, network_info=None): - """Create VM instance""" + """Create VM instance.""" instance_name = instance.name vm_ref = VMHelper.lookup(self._session, instance_name) if vm_ref is not None: @@ -180,7 +180,7 @@ class VMOps(object): return vm_ref def _spawn(self, instance, vm_ref): - """Spawn a new instance""" + """Spawn a new instance.""" LOG.debug(_('Starting VM %s...'), vm_ref) self._start(instance, vm_ref) instance_name = instance.name @@ -236,7 +236,8 @@ class VMOps(object): return timer.start(interval=0.5, now=True) def _get_vm_opaque_ref(self, instance_or_vm): - """Refactored out the common code of many methods that receive either + """ + Refactored out the common code of many methods that receive either a vm name or a vm instance, and want a vm instance in return. """ # if instance_or_vm is a string it must be opaque ref or instance name @@ -264,21 +265,22 @@ class VMOps(object): return vm_ref def _acquire_bootlock(self, vm): - """Prevent an instance from booting""" + """Prevent an instance from booting.""" self._session.call_xenapi( "VM.set_blocked_operations", vm, {"start": ""}) def _release_bootlock(self, vm): - """Allow an instance to boot""" + """Allow an instance to boot.""" self._session.call_xenapi( "VM.remove_from_blocked_operations", vm, "start") def snapshot(self, instance, image_id): - """Create snapshot from a running VM instance + """ + Create snapshot from a running VM instance :param instance: instance to be snapshotted :param image_id: id of image to upload to @@ -330,11 +332,12 @@ class VMOps(object): return def migrate_disk_and_power_off(self, instance, dest): - """Copies a VHD from one host machine to another + """ + Copies a VHD from one host machine to another - :param instance: the instance that owns the VHD in question - :param dest: the destination host machine - :param disk_type: values are 'primary' or 'cow' + :param instance: the instance that owns the VHD in question. + :param dest: the destination host machine. + :param disk_type: values are 'primary' or 'cow'. """ vm_ref = VMHelper.lookup(self._session, instance.name) @@ -383,7 +386,7 @@ class VMOps(object): return {'base_copy': base_copy_uuid, 'cow': cow_uuid} def link_disks(self, instance, base_copy_uuid, cow_uuid): - """Links the base copy VHD to the COW via the XAPI plugin""" + """Links the base copy VHD to the COW via the XAPI plugin.""" vm_ref = VMHelper.lookup(self._session, instance.name) new_base_copy_uuid = str(uuid.uuid4()) new_cow_uuid = str(uuid.uuid4()) @@ -404,7 +407,7 @@ class VMOps(object): return new_cow_uuid def resize_instance(self, instance, vdi_uuid): - """Resize a running instance by changing it's RAM and disk size """ + """Resize a running instance by changing it's RAM and disk size.""" #TODO(mdietz): this will need to be adjusted for swap later #The new disk size must be in bytes @@ -418,13 +421,14 @@ class VMOps(object): LOG.debug(_("Resize instance %s complete") % (instance.name)) def reboot(self, instance): - """Reboot VM instance""" + """Reboot VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref) self._session.wait_for_task(task, instance.id) def set_admin_password(self, instance, new_pass): - """Set the root/admin password on the VM instance. This is done via + """ + Set the root/admin password on the VM instance. This is done via an agent running on the VM. Communication between nova and the agent is done via writing xenstore records. Since communication is done over the XenAPI RPC calls, we need to encrypt the password. We're using a @@ -462,7 +466,8 @@ class VMOps(object): return resp_dict['message'] def inject_file(self, instance, path, contents): - """Write a file to the VM instance. The path to which it is to be + """ + Write a file to the VM instance. The path to which it is to be written and the contents of the file need to be supplied; both will be base64-encoded to prevent errors with non-ASCII characters being transmitted. If the agent does not support file injection, or the user @@ -487,7 +492,7 @@ class VMOps(object): return resp_dict['message'] def _shutdown(self, instance, vm_ref, hard=True): - """Shutdown an instance""" + """Shutdown an instance.""" state = self.get_info(instance['name'])['state'] if state == power_state.SHUTDOWN: instance_name = instance.name @@ -511,11 +516,11 @@ class VMOps(object): LOG.exception(exc) def _shutdown_rescue(self, rescue_vm_ref): - """Shutdown a rescue instance""" + """Shutdown a rescue instance.""" self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm_ref) def _destroy_vdis(self, instance, vm_ref): - """Destroys all VDIs associated with a VM""" + """Destroys all VDIs associated with a VM.""" instance_id = instance.id LOG.debug(_("Destroying VDIs for Instance %(instance_id)s") % locals()) @@ -532,7 +537,7 @@ class VMOps(object): LOG.exception(exc) def _destroy_rescue_vdis(self, rescue_vm_ref): - """Destroys all VDIs associated with a rescued VM""" + """Destroys all VDIs associated with a rescued VM.""" vdi_refs = VMHelper.lookup_vm_vdis(self._session, rescue_vm_ref) for vdi_ref in vdi_refs: try: @@ -541,7 +546,7 @@ class VMOps(object): continue def _destroy_rescue_vbds(self, rescue_vm_ref): - """Destroys all VBDs tied to a rescue VM""" + """Destroys all VBDs tied to a rescue VM.""" vbd_refs = self._session.get_xenapi().VM.get_VBDs(rescue_vm_ref) for vbd_ref in vbd_refs: vbd_rec = self._session.get_xenapi().VBD.get_record(vbd_ref) @@ -589,7 +594,7 @@ class VMOps(object): LOG.debug(_("kernel/ramdisk files removed")) def _destroy_vm(self, instance, vm_ref): - """Destroys a VM record""" + """Destroys a VM record.""" instance_id = instance.id try: task = self._session.call_xenapi('Async.VM.destroy', vm_ref) @@ -600,7 +605,7 @@ class VMOps(object): LOG.debug(_("Instance %(instance_id)s VM destroyed") % locals()) def _destroy_rescue_instance(self, rescue_vm_ref): - """Destroy a rescue instance""" + """Destroy a rescue instance.""" self._destroy_rescue_vbds(rescue_vm_ref) self._shutdown_rescue(rescue_vm_ref) self._destroy_rescue_vdis(rescue_vm_ref) @@ -624,10 +629,10 @@ class VMOps(object): """ Destroys VM instance by performing: - 1. A shutdown if requested - 2. Destroying associated VDIs - 3. Destroying kernel and ramdisk files (if necessary) - 4. Destroying that actual VM record + 1. A shutdown if requested. + 2. Destroying associated VDIs. + 3. Destroying kernel and ramdisk files (if necessary). + 4. Destroying that actual VM record. """ if vm_ref is None: LOG.warning(_("VM is not present, skipping destroy...")) @@ -650,36 +655,36 @@ class VMOps(object): callback(ret) def pause(self, instance, callback): - """Pause VM instance""" + """Pause VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.pause', vm_ref) self._wait_with_callback(instance.id, task, callback) def unpause(self, instance, callback): - """Unpause VM instance""" + """Unpause VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.unpause', vm_ref) self._wait_with_callback(instance.id, task, callback) def suspend(self, instance, callback): - """suspend the specified instance""" + """Suspend the specified instance""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.suspend', vm_ref) self._wait_with_callback(instance.id, task, callback) def resume(self, instance, callback): - """resume the specified instance""" + """Resume the specified instance.""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.resume', vm_ref, False, True) self._wait_with_callback(instance.id, task, callback) def rescue(self, instance, callback): - """Rescue the specified instance - - shutdown the instance VM - - set 'bootlock' to prevent the instance from starting in rescue - - spawn a rescue VM (the vm name-label will be instance-N-rescue) - + """ + Rescue the specified instance + - shutdown the instance VM. + - set 'bootlock' to prevent the instance from starting in rescue. + - spawn a rescue VM (the vm name-label will be instance-N-rescue). """ rescue_vm_ref = VMHelper.lookup(self._session, "%s-rescue" % instance.name) @@ -703,9 +708,9 @@ class VMOps(object): def unrescue(self, instance, callback): """Unrescue the specified instance - - unplug the instance VM's disk from the rescue VM - - teardown the rescue VM - - release the bootlock to allow the instance VM to start + - unplug the instance VM's disk from the rescue VM. + - teardown the rescue VM. + - release the bootlock to allow the instance VM to start. """ rescue_vm_ref = VMHelper.lookup(self._session, @@ -723,7 +728,8 @@ class VMOps(object): self._start(instance, original_vm_ref) def poll_rescued_instances(self, timeout): - """Look for expirable rescued instances + """ + Look for expirable rescued instances - forcibly exit rescue mode for any instances that have been in rescue mode for >= the provided timeout """ @@ -761,30 +767,30 @@ class VMOps(object): False) def get_info(self, instance): - """Return data about VM instance""" + """Return data about VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) return VMHelper.compile_info(vm_rec) def get_diagnostics(self, instance): - """Return data about VM diagnostics""" + """Return data about VM diagnostics.""" vm_ref = self._get_vm_opaque_ref(instance) vm_rec = self._session.get_xenapi().VM.get_record(vm_ref) return VMHelper.compile_diagnostics(self._session, vm_rec) def get_console_output(self, instance): - """Return snapshot of console""" + """Return snapshot of console.""" # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' def get_ajax_console(self, instance): - """Return link to instance's ajax console""" + """Return link to instance's ajax console.""" # TODO: implement this! return 'http://fakeajaxconsole/fake_url' # TODO(tr3buchet) - remove this function after nova multi-nic def _get_network_info(self, instance): - """creates network info list for instance""" + """Creates network info list for instance.""" admin_context = context.get_admin_context() IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id']) @@ -826,7 +832,7 @@ class VMOps(object): def inject_network_info(self, instance, vm_ref, network_info): """ Generate the network info and make calls to place it into the - xenstore and the xenstore param list + xenstore and the xenstore param list. """ logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref) @@ -847,7 +853,7 @@ class VMOps(object): pass def create_vifs(self, vm_ref, network_info): - """Creates vifs for an instance""" + """Creates vifs for an instance.""" logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref) # this function raises if vm_ref is not a vm_opaque_ref @@ -872,7 +878,8 @@ class VMOps(object): args, vm_ref) def list_from_xenstore(self, vm, path): - """Runs the xenstore-ls command to get a listing of all records + """ + Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, and the value stored in those paths as values. If nothing is found at that path, returns None. @@ -881,7 +888,8 @@ class VMOps(object): return json.loads(ret) def read_from_xenstore(self, vm, path): - """Returns the value stored in the xenstore record for the given VM + """ + Returns the value stored in the xenstore record for the given VM at the specified location. A XenAPIPlugin.PluginError will be raised if any error is encountered in the read process. """ @@ -897,7 +905,8 @@ class VMOps(object): return ret def write_to_xenstore(self, vm, path, value): - """Writes the passed value to the xenstore record for the given VM + """ + Writes the passed value to the xenstore record for the given VM at the specified location. A XenAPIPlugin.PluginError will be raised if any error is encountered in the write process. """ @@ -905,7 +914,8 @@ class VMOps(object): {'value': json.dumps(value)}) def clear_xenstore(self, vm, path): - """Deletes the VM's xenstore record for the specified path. + """ + Deletes the VM's xenstore record for the specified path. If there is no such record, the request is ignored. """ self._make_xenstore_call('delete_record', vm, path) @@ -922,7 +932,8 @@ class VMOps(object): def _make_plugin_call(self, plugin, method, vm, path, addl_args=None, vm_ref=None): - """Abstracts out the process of calling a method of a xenapi plugin. + """ + Abstracts out the process of calling a method of a xenapi plugin. Any errors raised by the plugin will in turn raise a RuntimeError here. """ instance_id = vm.id @@ -952,7 +963,8 @@ class VMOps(object): return ret def add_to_xenstore(self, vm, path, key, value): - """Adds the passed key/value pair to the xenstore record for + """ + Adds the passed key/value pair to the xenstore record for the given VM at the specified location. A XenAPIPlugin.PluginError will be raised if any error is encountered in the write process. """ @@ -965,7 +977,8 @@ class VMOps(object): self.write_to_xenstore(vm, path, current) def remove_from_xenstore(self, vm, path, key_or_keys): - """Takes either a single key or a list of keys and removes + """ + Takes either a single key or a list of keys and removes them from the xenstoreirecord data for the given VM. If the key doesn't exist, the request is ignored. """ @@ -992,7 +1005,8 @@ class VMOps(object): ###### names to distinguish them. (dabo) ######################################################################## def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix): - """Returns a dict of all the keys in the xenstore parameter record + """ + Returns a dict of all the keys in the xenstore parameter record for the given instance that begin with the key_prefix. """ data = self.read_from_param_xenstore(instance_or_vm) @@ -1003,7 +1017,8 @@ class VMOps(object): return data def read_from_param_xenstore(self, instance_or_vm, keys=None): - """Returns the xenstore parameter record data for the specified VM + """ + Returns the xenstore parameter record data for the specified VM instance as a dict. Accepts an optional key or list of keys; if a value for 'keys' is passed, the returned dict is filtered to only return the values for those keys. @@ -1025,9 +1040,11 @@ class VMOps(object): return ret def add_to_param_xenstore(self, instance_or_vm, key, val): - """Takes a key/value pair and adds it to the xenstore parameter + """ + Takes a key/value pair and adds it to the xenstore parameter record for the given vm instance. If the key exists in xenstore, - it is overwritten""" + it is overwritten + """ vm_ref = self._get_vm_opaque_ref(instance_or_vm) self.remove_from_param_xenstore(instance_or_vm, key) jsonval = json.dumps(val) @@ -1035,7 +1052,8 @@ class VMOps(object): (vm_ref, key, jsonval)) def write_to_param_xenstore(self, instance_or_vm, mapping): - """Takes a dict and writes each key/value pair to the xenstore + """ + Takes a dict and writes each key/value pair to the xenstore parameter record for the given vm instance. Any existing data for those keys is overwritten. """ @@ -1043,7 +1061,8 @@ class VMOps(object): self.add_to_param_xenstore(instance_or_vm, k, v) def remove_from_param_xenstore(self, instance_or_vm, key_or_keys): - """Takes either a single key or a list of keys and removes + """ + Takes either a single key or a list of keys and removes them from the xenstore parameter record data for the given VM. If the key doesn't exist, the request is ignored. """ @@ -1069,7 +1088,8 @@ def _runproc(cmd): class SimpleDH(object): - """This class wraps all the functionality needed to implement + """ + This class wraps all the functionality needed to implement basic Diffie-Hellman-Merkle key exchange in Python. It features intelligent defaults for the prime and base numbers needed for the calculation, while allowing you to supply your own. It requires that @@ -1078,7 +1098,8 @@ class SimpleDH(object): is not available, a RuntimeError will be raised. """ def __init__(self, prime=None, base=None, secret=None): - """You can specify the values for prime and base if you wish; + """ + You can specify the values for prime and base if you wish; otherwise, reasonable default values will be used. """ if prime is None: -- cgit From df946c08acba6fe1234b13f04d3c46c3973647c2 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 28 Mar 2011 11:52:28 +0100 Subject: addressed termie's review (second round) --- nova/network/xenapi_net.py | 14 ++-- nova/tests/test_xenapi.py | 2 +- nova/virt/xenapi/fake.py | 156 +++++++++++++++++++++------------------------ nova/virt/xenapi/vmops.py | 2 +- 4 files changed, 82 insertions(+), 92 deletions(-) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 7a9da8046..8603fd842 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -49,11 +49,9 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): # If bridge does not exists # 1 - create network description = "network for nova bridge %s" % bridge - network_rec = { - 'name_label': bridge, + network_rec = {'name_label': bridge, 'name_description': description, - 'other_config': {}, - } + 'other_config': {}} network_ref = session.call_xenapi('network.create', network_rec) # 2 - find PIF for VLAN expr = 'field "device" = "%s" and \ @@ -66,8 +64,10 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): _('Found no PIF for device %s') % FLAGS.vlan_interface) # 3 - create vlan for network for pif_ref in pifs.keys(): - session.call_xenapi('VLAN.create', pif_ref, - str(vlan_num), network_ref) + session.call_xenapi('VLAN.create', + pif_ref, + str(vlan_num), + network_ref) else: # Check VLAN tag is appropriate network_rec = session.call_xenapi('network.get_record', network_ref) @@ -76,7 +76,7 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): # Retrieve VLAN from PIF pif_rec = session.call_xenapi('PIF.get_record', pif_ref) pif_vlan = int(pif_rec['VLAN']) - # Raise an exception if VLAN <> vlan_num + # Raise an exception if VLAN != vlan_num if pif_vlan != vlan_num: raise Exception(_("PIF %(pif_rec['uuid'])s for network " "%(bridge)s has VLAN id %(pif_vlan)d. " diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6ec0525a7..9fdd1feeb 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -487,7 +487,7 @@ class XenAPIVMTestCase(test.TestCase): glance_stubs.FakeGlance.IMAGE_RAMDISK, instance_id=fake_instance_id) # TODO(salvatore-orlando): a complete test here would require - # A check for making sure the bridge for the VM's VIF is + # a check for making sure the bridge for the VM's VIF is # consistent with bridge specified in nova db self.network = network_bk diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 6d17a642e..d084c725f 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -78,7 +78,10 @@ def reset(): for c in _CLASSES: _db_content[c] = {} create_host('fake') - create_vm('fake', 'Running', is_a_template=False, is_control_domain=True) + create_vm('fake', + 'Running', + is_a_template=False, + is_control_domain=True) def reset_table(table): @@ -88,26 +91,23 @@ def reset_table(table): def create_host(name_label): - return _create_object('host', { - 'name_label': name_label, - }) + return _create_object('host', + {'name_label': name_label}) def create_network(name_label, bridge): - return _create_object('network', { - 'name_label': name_label, - 'bridge': bridge, - }) + return _create_object('network', + {'name_label': name_label, + 'bridge': bridge}) def create_vm(name_label, status, is_a_template=False, is_control_domain=False): - return _create_object('VM', { - 'name_label': name_label, - 'power-state': status, - 'is_a_template': is_a_template, - 'is_control_domain': is_control_domain, - }) + return _create_object('VM', + {'name_label': name_label, + 'power-state': status, + 'is_a_template': is_a_template, + 'is_control_domain': is_control_domain}) def destroy_vm(vm_ref): @@ -129,27 +129,24 @@ def destroy_vdi(vdi_ref): def create_vdi(name_label, read_only, sr_ref, sharable): - return _create_object('VDI', { - 'name_label': name_label, - 'read_only': read_only, - 'SR': sr_ref, - 'type': '', - 'name_description': '', - 'sharable': sharable, - 'other_config': {}, - 'location': '', - 'xenstore_data': '', - 'sm_config': {}, - 'VBDs': {}, - }) + return _create_object('VDI', + {'name_label': name_label, + 'read_only': read_only, + 'SR': sr_ref, + 'type': '', + 'name_description': '', + 'sharable': sharable, + 'other_config': {}, + 'location': '', + 'xenstore_data': '', + 'sm_config': {}, + 'VBDs': {}}) def create_vbd(vm_ref, vdi_ref): - vbd_rec = { - 'VM': vm_ref, - 'VDI': vdi_ref, - 'currently_attached': False, - } + vbd_rec = {'VM': vm_ref, + 'VDI': vdi_ref, + 'currently_attached': False} vbd_ref = _create_object('VBD', vbd_rec) after_VBD_create(vbd_ref, vbd_rec) return vbd_ref @@ -175,19 +172,17 @@ def after_VM_create(vm_ref, vm_rec): def create_pbd(config, host_ref, sr_ref, attached): - return _create_object('PBD', { - 'device-config': config, - 'host': host_ref, - 'SR': sr_ref, - 'currently-attached': attached, - }) + return _create_object('PBD', + {'device-config': config, + 'host': host_ref, + 'SR': sr_ref, + 'currently-attached': attached}) def create_task(name_label): - return _create_object('task', { - 'name_label': name_label, - 'status': 'pending', - }) + return _create_object('task', + {'name_label': name_label, + 'status': 'pending'}) def create_local_pifs(): @@ -205,34 +200,32 @@ def create_local_srs(): def _create_local_sr(host_ref): - sr_ref = _create_object('SR', { - 'name_label': 'Local storage', - 'type': 'lvm', - 'content_type': 'user', - 'shared': False, - 'physical_size': str(1 << 30), - 'physical_utilisation': str(0), - 'virtual_allocation': str(0), - 'other_config': { - 'i18n-original-value-name_label': 'Local storage', - 'i18n-key': 'local-storage', - }, - 'VDIs': [] - }) + sr_ref = \ + _create_object('SR', + {'name_label': 'Local storage', + 'type': 'lvm', + 'content_type': 'user', + 'shared': False, + 'physical_size': str(1 << 30), + 'physical_utilisation': str(0), + 'virtual_allocation': str(0), + 'other_config': {'i18n-original-value-name_label': \ + 'Local storage', + 'i18n-key': 'local-storage'}, + 'VDIs': []}) pbd_ref = create_pbd('', host_ref, sr_ref, True) _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] return sr_ref def _create_local_pif(host_ref): - pif_ref = _create_object('PIF', { - 'name-label': 'Fake PIF', - 'MAC': '00:11:22:33:44:55', - 'physical': True, - 'VLAN': -1, - 'device': 'fake0', - 'host_uuid': host_ref, - }) + pif_ref = _create_object('PIF', + {'name-label': 'Fake PIF', + 'MAC': '00:11:22:33:44:55', + 'physical': True, + 'VLAN': -1, + 'device': 'fake0', + 'host_uuid': host_ref}) def _create_object(table, obj): @@ -260,19 +253,17 @@ def _create_sr(table, obj): def _create_vlan(pif_ref, vlan_num, network_ref): pif_rec = get_record('PIF', pif_ref) - vlan_pif_ref = _create_object('PIF', { - 'name-label': 'Fake VLAN PIF', - 'MAC': '00:11:22:33:44:55', - 'physical': True, - 'VLAN': vlan_num, - 'device': pif_rec['device'], - 'host_uuid': pif_rec['host_uuid'], - }) - return _create_object('VLAN', { - 'tagged-pif': pif_ref, - 'untagged-pif': vlan_pif_ref, - 'tag': vlan_num, - }) + vlan_pif_ref = _create_object('PIF', + {'name-label': 'Fake VLAN PIF', + 'MAC': '00:11:22:33:44:55', + 'physical': True, + 'VLAN': vlan_num, + 'device': pif_rec['device'], + 'host_uuid': pif_rec['host_uuid']}) + return _create_object('VLAN', + {'tagged-pif': pif_ref, + 'untagged-pif': vlan_pif_ref, + 'tag': vlan_num}) def get_all(table): @@ -334,7 +325,7 @@ class SessionBase(object): rec['device'] = '' def PIF_get_all_records_where(self, _1, _2): - # TODO (salvatore-orlando):filter table on _2 + # TODO (salvatore-orlando): filter table on _2 return _db_content['PIF'] def VM_get_xenstore_data(self, _1, vm_ref): @@ -347,7 +338,7 @@ class SessionBase(object): db_ref['xenstore_data'][key] = None def network_get_all_records_where(self, _1, _2): - # TODO (salvatore-orlando):filter table on _2 + # TODO (salvatore-orlando): filter table on _2 return _db_content['network'] def VM_add_to_xenstore_data(self, _1, vm_ref, key, value): @@ -385,10 +376,9 @@ class SessionBase(object): def _login(self, method, params): self._session = str(uuid.uuid4()) - _db_content['session'][self._session] = { - 'uuid': str(uuid.uuid4()), - 'this_host': _db_content['host'].keys()[0], - } + _db_content['session'][self._session] = \ + {'uuid': str(uuid.uuid4()), + 'this_host': _db_content['host'].keys()[0]} def _logout(self): s = self._session diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 9b18ac732..08046318e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -338,6 +338,7 @@ class VMOps(object): :param instance: the instance that owns the VHD in question. :param dest: the destination host machine. :param disk_type: values are 'primary' or 'cow'. + """ vm_ref = VMHelper.lookup(self._session, instance.name) @@ -711,7 +712,6 @@ class VMOps(object): - unplug the instance VM's disk from the rescue VM. - teardown the rescue VM. - release the bootlock to allow the instance VM to start. - """ rescue_vm_ref = VMHelper.lookup(self._session, "%s-rescue" % instance.name) -- cgit From 184fa8239d54d20ff294cdb019d07989ed3d6c4d Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 28 Mar 2011 12:08:43 +0100 Subject: addressed termies review (third round) --- nova/tests/db/fakes.py | 63 ++++++++++++++++++++++------------------------- nova/tests/test_xenapi.py | 26 +++++++++---------- nova/virt/xenapi/fake.py | 2 +- nova/virt/xenapi/vmops.py | 3 ++- 4 files changed, 46 insertions(+), 48 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index f7610aa56..7ddfe377a 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -56,39 +56,36 @@ def stub_out_db_instance_api(stubs, injected=True): flavorid=5, rxtx_cap=5)} - flat_network_fields = { - 'id': 'fake_flat', - 'bridge': 'xenbr0', - 'label': 'fake_flat_network', - 'netmask': '255.255.255.0', - 'cidr_v6': 'fe80::a00:0/120', - 'netmask_v6': '120', - 'gateway': '10.0.0.1', - 'gateway_v6': 'fe80::a00:1', - 'broadcast': '10.0.0.255', - 'dns': '10.0.0.2', - 'ra_server': None, - 'injected': injected} - - vlan_network_fields = { - 'id': 'fake_vlan', - 'bridge': 'br111', - 'label': 'fake_vlan_network', - 'netmask': '255.255.255.0', - 'cidr_v6': 'fe80::a00:0/120', - 'netmask_v6': '120', - 'gateway': '10.0.0.1', - 'gateway_v6': 'fe80::a00:1', - 'broadcast': '10.0.0.255', - 'dns': '10.0.0.2', - 'ra_server': None, - 'vlan': 111, - 'injected': False} - - fixed_ip_fields = { - 'address': '10.0.0.3', - 'address_v6': 'fe80::a00:3', - 'network_id': 'fake_flat'} + flat_network_fields = {'id': 'fake_flat', + 'bridge': 'xenbr0', + 'label': 'fake_flat_network', + 'netmask': '255.255.255.0', + 'cidr_v6': 'fe80::a00:0/120', + 'netmask_v6': '120', + 'gateway': '10.0.0.1', + 'gateway_v6': 'fe80::a00:1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2', + 'ra_server': None, + 'injected': injected} + + vlan_network_fields = {'id': 'fake_vlan', + 'bridge': 'br111', + 'label': 'fake_vlan_network', + 'netmask': '255.255.255.0', + 'cidr_v6': 'fe80::a00:0/120', + 'netmask_v6': '120', + 'gateway': '10.0.0.1', + 'gateway_v6': 'fe80::a00:1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2', + 'ra_server': None, + 'vlan': 111, + 'injected': False} + + fixed_ip_fields = {'address': '10.0.0.3', + 'address_v6': 'fe80::a00:3', + 'network_id': 'fake_flat'} class FakeModel(object): """Stubs out for model.""" diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9fdd1feeb..bc1469223 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -287,19 +287,19 @@ class XenAPIVMTestCase(test.TestCase): key = 'vm-data/networking/aabbccddeeff' xenstore_value = xenstore_data[key] tcpip_data = ast.literal_eval(xenstore_value) - self.assertEquals(tcpip_data, { - 'label': 'fake_flat_network', - 'broadcast': '10.0.0.255', - 'ips': [{'ip': '10.0.0.3', - 'netmask':'255.255.255.0', - 'enabled':'1'}], - 'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff', - 'netmask': '120', - 'enabled': '1', - 'gateway': 'fe80::a00:1'}], - 'mac': 'aa:bb:cc:dd:ee:ff', - 'dns': ['10.0.0.2'], - 'gateway': '10.0.0.1'}) + self.assertEquals(tcpip_data, + {'label': 'fake_flat_network', + 'broadcast': '10.0.0.255', + 'ips': [{'ip': '10.0.0.3', + 'netmask':'255.255.255.0', + 'enabled':'1'}], + 'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff', + 'netmask': '120', + 'enabled': '1', + 'gateway': 'fe80::a00:1'}], + 'mac': 'aa:bb:cc:dd:ee:ff', + 'dns': ['10.0.0.2'], + 'gateway': '10.0.0.1'}) def check_vm_params_for_windows(self): self.assertEquals(self.vm['platform']['nx'], 'true') diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index d084c725f..d79312a60 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -60,7 +60,7 @@ from nova import exception from nova import log as logging -_CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\ +_CLASSES = ['host', 'network', 'session', 'SR', 'VBD', \ 'PBD', 'VDI', 'VIF', 'PIF', 'VM', 'VLAN', 'task'] _db_content = {} diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 08046318e..147419f90 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -668,7 +668,7 @@ class VMOps(object): self._wait_with_callback(instance.id, task, callback) def suspend(self, instance, callback): - """Suspend the specified instance""" + """Suspend the specified instance.""" vm_ref = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.suspend', vm_ref) self._wait_with_callback(instance.id, task, callback) @@ -686,6 +686,7 @@ class VMOps(object): - shutdown the instance VM. - set 'bootlock' to prevent the instance from starting in rescue. - spawn a rescue VM (the vm name-label will be instance-N-rescue). + """ rescue_vm_ref = VMHelper.lookup(self._session, "%s-rescue" % instance.name) -- cgit From 8922630e70e97b52e363a861c76fe4a01b8418ff Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 09:37:05 -0400 Subject: Fix typo in libvirt xml template --- nova/virt/libvirt.xml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 26f528cb1..a62c3b6a8 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -3,7 +3,7 @@ ${memory_kb} #if $type == 'lxc' - #set $disk prefix = '' + #set $disk_prefix = '' #set $disk_bus = '' exe /sbin/init -- cgit From 4aad5721bff628ef8b34e0c536e0e2415f2b63f4 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 11:16:59 -0400 Subject: Removed 'is not None' to do more general truth-checking. Added rather verbose testing. --- nova/image/glance.py | 2 +- nova/tests/api/openstack/test_images.py | 69 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index be9805b69..f0f1ecf57 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -220,7 +220,7 @@ def _convert_timestamps_to_datetimes(image_meta): Returns image with known timestamp fields converted to datetime objects """ for attr in ['created_at', 'updated_at', 'deleted_at']: - if image_meta.get(attr) is not None: + if image_meta.get(attr): image_meta[attr] = _parse_glance_iso8601_timestamp( image_meta[attr]) return image_meta diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 1cdccadd6..88dd8f506 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -197,6 +197,73 @@ class GlanceImageServiceTest(test.TestCase, image_metas = self.service.detail(self.context) self.assertDictMatch(image_metas[0], expected) + def test_image_valid_date_format(self): + """ + Ensure 'created_at', 'updated_at', and 'deleted_at' can be valid + ISO strings. + """ + fixture = { + 'name': 'test image', + 'is_public': False, + 'properties': { + 'instance_id': '42', + 'user_id': '1', + }, + } + + valid_dates = ['2010-10-11T10:30:22', '2012-12-21T00:00:00'] + + for date in valid_dates: + fixture["created_at"] = date + fixture["updated_at"] = date + fixture["deleted_at"] = date + image_id = self.service.create(self.context, fixture)['id'] + self.assertDictMatch(self.sent_to_glance['metadata'], fixture) + + def test_image_invalid_date_format(self): + """ + Ensure `created_at`, `modified_at` can't be invalid dates. + """ + fixture = { + 'name': 'test image', + 'is_public': False, + 'properties': { + 'instance_id': '42', + 'user_id': '1', + }, + } + + invalid_dates = ['Not a date.', 4] + + for date in invalid_dates: + fixture["created_at"] = date + fixture["updated_at"] = date + fixture["deleted_at"] = date + self.assertRaises((TypeError, ValueError), self.service.create, + self.context, fixture) + + def test_image_blank_date_format(self): + """ + Ensure `created_at`, `modified_at` can be blank dates. + """ + fixture = { + 'name': 'test image', + 'is_public': False, + 'properties': { + 'instance_id': '42', + 'user_id': '1', + }, + } + + blank_dates = [None, ''] + + for date in blank_dates: + fixture["created_at"] = date + fixture["updated_at"] = date + fixture["deleted_at"] = date + image_id = self.service.create(self.context, fixture)['id'] + self.assertDictMatch(self.sent_to_glance['metadata'], fixture) + def test_create_without_instance_id(self): """ Ensure we can create an image without having to specify an @@ -235,6 +302,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): FLAGS.image_service = self.orig_image_service super(ImageControllerWithGlanceServiceTest, self).tearDown() + + def test_get_image_index(self): req = webob.Request.blank('/v1.0/images') res = req.get_response(fakes.wsgi_app()) -- cgit From 23bed216dbbd512e733ecf6065105b2d20703531 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 13:04:02 -0400 Subject: Added MUCH more flexiable iso8601 parser dep for added stability. --- nova/image/glance.py | 8 ++-- nova/tests/api/openstack/test_images.py | 69 --------------------------------- nova/tests/image/test_glance.py | 50 ++++++++++++++++++++++++ tools/pip-requires | 1 + 4 files changed, 54 insertions(+), 74 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index f0f1ecf57..c08e2e0e8 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -20,6 +20,8 @@ from __future__ import absolute_import import datetime +import iso8601 + from glance.common import exception as glance_exception from nova import exception @@ -230,8 +232,4 @@ def _parse_glance_iso8601_timestamp(timestamp): """ Parse a subset of iso8601 timestamps into datetime objects """ - GLANCE_FMT = "%Y-%m-%dT%H:%M:%S" - ISO_FMT = "%Y-%m-%dT%H:%M:%S.%f" - # FIXME(sirp): Glance is not returning in ISO format, we should fix Glance - # to do so, and then switch to parsing it here - return datetime.datetime.strptime(timestamp, GLANCE_FMT) + return iso8601.parse_date(timestamp).replace(tzinfo=None) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 88dd8f506..1cdccadd6 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -197,73 +197,6 @@ class GlanceImageServiceTest(test.TestCase, image_metas = self.service.detail(self.context) self.assertDictMatch(image_metas[0], expected) - def test_image_valid_date_format(self): - """ - Ensure 'created_at', 'updated_at', and 'deleted_at' can be valid - ISO strings. - """ - fixture = { - 'name': 'test image', - 'is_public': False, - 'properties': { - 'instance_id': '42', - 'user_id': '1', - }, - } - - valid_dates = ['2010-10-11T10:30:22', '2012-12-21T00:00:00'] - - for date in valid_dates: - fixture["created_at"] = date - fixture["updated_at"] = date - fixture["deleted_at"] = date - image_id = self.service.create(self.context, fixture)['id'] - self.assertDictMatch(self.sent_to_glance['metadata'], fixture) - - def test_image_invalid_date_format(self): - """ - Ensure `created_at`, `modified_at` can't be invalid dates. - """ - fixture = { - 'name': 'test image', - 'is_public': False, - 'properties': { - 'instance_id': '42', - 'user_id': '1', - }, - } - - invalid_dates = ['Not a date.', 4] - - for date in invalid_dates: - fixture["created_at"] = date - fixture["updated_at"] = date - fixture["deleted_at"] = date - self.assertRaises((TypeError, ValueError), self.service.create, - self.context, fixture) - - def test_image_blank_date_format(self): - """ - Ensure `created_at`, `modified_at` can be blank dates. - """ - fixture = { - 'name': 'test image', - 'is_public': False, - 'properties': { - 'instance_id': '42', - 'user_id': '1', - }, - } - - blank_dates = [None, ''] - - for date in blank_dates: - fixture["created_at"] = date - fixture["updated_at"] = date - fixture["deleted_at"] = date - image_id = self.service.create(self.context, fixture)['id'] - self.assertDictMatch(self.sent_to_glance['metadata'], fixture) - def test_create_without_instance_id(self): """ Ensure we can create an image without having to specify an @@ -302,8 +235,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): FLAGS.image_service = self.orig_image_service super(ImageControllerWithGlanceServiceTest, self).tearDown() - - def test_get_image_index(self): req = webob.Request.blank('/v1.0/images') res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index d03aa9cc8..dfdce7584 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -57,6 +57,7 @@ class NullWriter(object): class BaseGlanceTest(unittest.TestCase): NOW_GLANCE_FORMAT = "2010-10-11T10:30:22" NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22) + NOW_ISO_FORMAT = "2010-10-11T10:30:22.000000" def setUp(self): # FIXME(sirp): we can probably use stubs library here rather than @@ -74,6 +75,10 @@ class BaseGlanceTest(unittest.TestCase): self.assertEqual(image_meta['updated_at'], None) self.assertEqual(image_meta['deleted_at'], None) + def assertDateTimesBlank(self, image_meta): + self.assertEqual(image_meta['updated_at'], '') + self.assertEqual(image_meta['deleted_at'], '') + class TestGlanceImageServiceProperties(BaseGlanceTest): def test_show_passes_through_to_client(self): @@ -108,33 +113,65 @@ class TestGetterDateTimeNoneTests(BaseGlanceTest): image_meta = self.service.show(self.context, 'image1') self.assertDateTimesEmpty(image_meta) + def test_show_handles_blank_datetimes(self): + self.client.images = self._make_blank_datetime_fixtures() + image_meta = self.service.show(self.context, 'image1') + self.assertDateTimesBlank(image_meta) + def test_detail_handles_none_datetimes(self): self.client.images = self._make_none_datetime_fixtures() image_meta = self.service.detail(self.context)[0] self.assertDateTimesEmpty(image_meta) + def test_detail_handles_blank_datetimes(self): + self.client.images = self._make_blank_datetime_fixtures() + image_meta = self.service.detail(self.context)[0] + self.assertDateTimesBlank(image_meta) + def test_get_handles_none_datetimes(self): self.client.images = self._make_none_datetime_fixtures() writer = NullWriter() image_meta = self.service.get(self.context, 'image1', writer) self.assertDateTimesEmpty(image_meta) + def test_get_handles_blank_datetimes(self): + self.client.images = self._make_blank_datetime_fixtures() + writer = NullWriter() + image_meta = self.service.get(self.context, 'image1', writer) + self.assertDateTimesBlank(image_meta) + def test_show_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() image_meta = self.service.show(self.context, 'image1') self.assertDateTimesFilled(image_meta) + def test_show_makes_datetimes_iso(self): + self.client.images = self._make_iso_fixtures() + image_meta = self.service.show(self.context, 'image1') + self.assertDateTimesFilled(image_meta) + def test_detail_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() image_meta = self.service.detail(self.context)[0] self.assertDateTimesFilled(image_meta) + def test_detail_makes_datetimes_iso(self): + self.client.images = self._make_iso_fixtures() + image_meta = self.service.detail(self.context)[0] + self.assertDateTimesFilled(image_meta) + def test_get_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() writer = NullWriter() image_meta = self.service.get(self.context, 'image1', writer) self.assertDateTimesFilled(image_meta) + def test_get_makes_datetimes_iso(self): + self.client.images = self._make_iso_fixtures() + writer = NullWriter() + image_meta = self.service.get(self.context, 'image1', writer) + self.assertDateTimesFilled(image_meta) + def _make_datetime_fixtures(self): fixtures = {'image1': {'name': 'image1', 'is_public': True, 'created_at': self.NOW_GLANCE_FORMAT, @@ -142,12 +179,25 @@ class TestGetterDateTimeNoneTests(BaseGlanceTest): 'deleted_at': self.NOW_GLANCE_FORMAT}} return fixtures + def _make_iso_fixtures(self): + fixtures = {'image1': {'name': 'image1', 'is_public': True, + 'created_at': self.NOW_ISO_FORMAT, + 'updated_at': self.NOW_ISO_FORMAT, + 'deleted_at': self.NOW_ISO_FORMAT}} + return fixtures + def _make_none_datetime_fixtures(self): fixtures = {'image1': {'name': 'image1', 'is_public': True, 'updated_at': None, 'deleted_at': None}} return fixtures + def _make_blank_datetime_fixtures(self): + fixtures = {'image1': {'name': 'image1', 'is_public': True, + 'updated_at': '', + 'deleted_at': ''}} + return fixtures + class TestMutatorDateTimeTests(BaseGlanceTest): """Tests create(), update()""" diff --git a/tools/pip-requires b/tools/pip-requires index 4ab9644d8..5cd80d1f4 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -31,3 +31,4 @@ netaddr sphinx glance suds==0.4 +iso8601 -- cgit From 56b4dd3929448585c15c8d11c5fe1569ce21ea7d Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 13:18:47 -0400 Subject: Removed extra dependency as per suggestion, although it fixes the issue much better IMO, we should be safe sticking with using the format from python's isoformat() --- nova/image/glance.py | 5 ++--- nova/tests/image/test_glance.py | 26 +------------------------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index c08e2e0e8..32c9fa6be 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -20,8 +20,6 @@ from __future__ import absolute_import import datetime -import iso8601 - from glance.common import exception as glance_exception from nova import exception @@ -232,4 +230,5 @@ def _parse_glance_iso8601_timestamp(timestamp): """ Parse a subset of iso8601 timestamps into datetime objects """ - return iso8601.parse_date(timestamp).replace(tzinfo=None) + ISO_FMT = "%Y-%m-%dT%H:%M:%S.%f" + return datetime.datetime.strptime(timestamp, ISO_FMT) diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index dfdce7584..dfa754b89 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -55,9 +55,8 @@ class NullWriter(object): class BaseGlanceTest(unittest.TestCase): - NOW_GLANCE_FORMAT = "2010-10-11T10:30:22" + NOW_GLANCE_FORMAT = "2010-10-11T10:30:22.000000" NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22) - NOW_ISO_FORMAT = "2010-10-11T10:30:22.000000" def setUp(self): # FIXME(sirp): we can probably use stubs library here rather than @@ -145,33 +144,17 @@ class TestGetterDateTimeNoneTests(BaseGlanceTest): image_meta = self.service.show(self.context, 'image1') self.assertDateTimesFilled(image_meta) - def test_show_makes_datetimes_iso(self): - self.client.images = self._make_iso_fixtures() - image_meta = self.service.show(self.context, 'image1') - self.assertDateTimesFilled(image_meta) - def test_detail_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() image_meta = self.service.detail(self.context)[0] self.assertDateTimesFilled(image_meta) - def test_detail_makes_datetimes_iso(self): - self.client.images = self._make_iso_fixtures() - image_meta = self.service.detail(self.context)[0] - self.assertDateTimesFilled(image_meta) - def test_get_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() writer = NullWriter() image_meta = self.service.get(self.context, 'image1', writer) self.assertDateTimesFilled(image_meta) - def test_get_makes_datetimes_iso(self): - self.client.images = self._make_iso_fixtures() - writer = NullWriter() - image_meta = self.service.get(self.context, 'image1', writer) - self.assertDateTimesFilled(image_meta) - def _make_datetime_fixtures(self): fixtures = {'image1': {'name': 'image1', 'is_public': True, 'created_at': self.NOW_GLANCE_FORMAT, @@ -179,13 +162,6 @@ class TestGetterDateTimeNoneTests(BaseGlanceTest): 'deleted_at': self.NOW_GLANCE_FORMAT}} return fixtures - def _make_iso_fixtures(self): - fixtures = {'image1': {'name': 'image1', 'is_public': True, - 'created_at': self.NOW_ISO_FORMAT, - 'updated_at': self.NOW_ISO_FORMAT, - 'deleted_at': self.NOW_ISO_FORMAT}} - return fixtures - def _make_none_datetime_fixtures(self): fixtures = {'image1': {'name': 'image1', 'is_public': True, 'updated_at': None, -- cgit From e043561a8d5dc0c3183ec7e3a5a44f2aa306d2fd Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 13:20:46 -0400 Subject: Removed iso8601 dep from pip-requires --- tools/pip-requires | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/pip-requires b/tools/pip-requires index 5cd80d1f4..4ab9644d8 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -31,4 +31,3 @@ netaddr sphinx glance suds==0.4 -iso8601 -- cgit From 805cb3609379827d1643785be83f75b69b602d74 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 13:44:33 -0400 Subject: Fix libvirt merge mistake --- nova/virt/libvirt.xml.template | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index a62c3b6a8..894b216e9 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -83,22 +83,24 @@ #end if #end if #end if + +#for $nic in $nics - - + + - - - -#if $getVar('extra_params', False) - ${extra_params} + + + +#if $getVar('nic.extra_params', False) + ${nic.extra_params} #end if -#if $getVar('gateway_v6', False) - +#if $getVar('nic.gateway_v6', False) + #end if - +#end for -- cgit From de07a6409d575af5db748bdbfa2cc57881136d66 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 13:47:18 -0400 Subject: Decided to not break old format so this should work with the way Glance used to work and the way glace works now..The best of both worlds? --- nova/image/glance.py | 12 ++++++++++-- nova/tests/image/test_glance.py | 27 +++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index 32c9fa6be..34fc78e71 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -230,5 +230,13 @@ def _parse_glance_iso8601_timestamp(timestamp): """ Parse a subset of iso8601 timestamps into datetime objects """ - ISO_FMT = "%Y-%m-%dT%H:%M:%S.%f" - return datetime.datetime.strptime(timestamp, ISO_FMT) + ISO_FORMATS = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"] + + for iso_format in ISO_FORMATS: + try: + return datetime.datetime.strptime(timestamp, iso_format) + except ValueError: + pass + + raise ValueError(_("""%(timestamp)s does not follow any of the \ +signatures: %(ISO_FORMATS)s""") % (locals())) diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index dfa754b89..9d0b14613 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -55,6 +55,7 @@ class NullWriter(object): class BaseGlanceTest(unittest.TestCase): + NOW_GLANCE_OLD_FORMAT = "2010-10-11T10:30:22" NOW_GLANCE_FORMAT = "2010-10-11T10:30:22.000000" NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22) @@ -143,23 +144,41 @@ class TestGetterDateTimeNoneTests(BaseGlanceTest): self.client.images = self._make_datetime_fixtures() image_meta = self.service.show(self.context, 'image1') self.assertDateTimesFilled(image_meta) + image_meta = self.service.show(self.context, 'image2') + self.assertDateTimesFilled(image_meta) def test_detail_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() image_meta = self.service.detail(self.context)[0] self.assertDateTimesFilled(image_meta) + image_meta = self.service.detail(self.context)[1] + self.assertDateTimesFilled(image_meta) def test_get_makes_datetimes(self): self.client.images = self._make_datetime_fixtures() writer = NullWriter() image_meta = self.service.get(self.context, 'image1', writer) self.assertDateTimesFilled(image_meta) + image_meta = self.service.get(self.context, 'image2', writer) + self.assertDateTimesFilled(image_meta) def _make_datetime_fixtures(self): - fixtures = {'image1': {'name': 'image1', 'is_public': True, - 'created_at': self.NOW_GLANCE_FORMAT, - 'updated_at': self.NOW_GLANCE_FORMAT, - 'deleted_at': self.NOW_GLANCE_FORMAT}} + fixtures = { + 'image1': { + 'name': 'image1', + 'is_public': True, + 'created_at': self.NOW_GLANCE_FORMAT, + 'updated_at': self.NOW_GLANCE_FORMAT, + 'deleted_at': self.NOW_GLANCE_FORMAT, + }, + 'image2': { + 'name': 'image2', + 'is_public': True, + 'created_at': self.NOW_GLANCE_OLD_FORMAT, + 'updated_at': self.NOW_GLANCE_OLD_FORMAT, + 'deleted_at': self.NOW_GLANCE_OLD_FORMAT, + }, + } return fixtures def _make_none_datetime_fixtures(self): -- cgit From 9c61085ea9612be24e9975ac3fba456874b89f08 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 13:49:51 -0400 Subject: Add more unit tests for lxc --- nova/tests/test_virt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index cee044279..ed7943ace 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -257,7 +257,9 @@ class LibvirtConnTestCase(test.TestCase): check = [ (lambda t: t.find('.').get('type'), 'lxc'), - (lambda t: t.find('./os/type').text, 'exe')] + (lambda t: t.find('./os/type').text, 'exe'), + (lambda t: t.find('./devices/filesystem/source').get('dir'), 'rootfs'), + (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')] for i, (check, expected_result) in enumerate(check): self.assertEqual(check(tree), -- cgit From e44ce1a95baddd4f6d511d8be253167436395cb2 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 14:07:33 -0400 Subject: More pep8 corrections --- nova/tests/test_virt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index ed7943ace..8cee55576 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -258,8 +258,8 @@ class LibvirtConnTestCase(test.TestCase): check = [ (lambda t: t.find('.').get('type'), 'lxc'), (lambda t: t.find('./os/type').text, 'exe'), - (lambda t: t.find('./devices/filesystem/source').get('dir'), 'rootfs'), - (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')] + (lambda t: t.find('./devices/filesystem/source').get('dir'), 'rootfs'), + (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')] for i, (check, expected_result) in enumerate(check): self.assertEqual(check(tree), -- cgit From c8e708af1789fda97674fb4c3904d86de8473a7e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 14:32:03 -0400 Subject: Dont make the test fail --- nova/tests/test_virt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 8cee55576..df29e69c2 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -258,7 +258,6 @@ class LibvirtConnTestCase(test.TestCase): check = [ (lambda t: t.find('.').get('type'), 'lxc'), (lambda t: t.find('./os/type').text, 'exe'), - (lambda t: t.find('./devices/filesystem/source').get('dir'), 'rootfs'), (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')] for i, (check, expected_result) in enumerate(check): @@ -266,6 +265,9 @@ class LibvirtConnTestCase(test.TestCase): expected_result, '%s failed common check %d' % (xml, i)) + target = tree.find('./devices/filesystem/source').get('dir') + self.assertTrue(len(target) > 0) + def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): user_context = context.RequestContext(project=self.project, -- cgit From bb7ed6cb9cf625b675a666866a7f9fb762ca6bd2 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 14:47:25 -0400 Subject: use self.flags in virt test --- nova/tests/test_virt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index df29e69c2..e6beb8e2e 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -246,7 +246,7 @@ class LibvirtConnTestCase(test.TestCase): {'allocated': True, 'instance_id': instance_ref['id']}) - FLAGS.libvirt_type = 'lxc' + self.flags(libvirt_type='lxc') conn = libvirt_conn.LibvirtConnection(True) uri = conn.get_uri() -- cgit From 78a9ec232cde1172fa4c639ebdcbf88967bf8e9c Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 15:10:34 -0400 Subject: Fixed superfluous parentheses around locals(). --- nova/image/glance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index 34fc78e71..d8f51429e 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -230,13 +230,13 @@ def _parse_glance_iso8601_timestamp(timestamp): """ Parse a subset of iso8601 timestamps into datetime objects """ - ISO_FORMATS = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"] + iso_formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"] - for iso_format in ISO_FORMATS: + for iso_format in iso_formats: try: return datetime.datetime.strptime(timestamp, iso_format) except ValueError: pass raise ValueError(_("""%(timestamp)s does not follow any of the \ -signatures: %(ISO_FORMATS)s""") % (locals())) +signatures: %(ISO_FORMATS)s""") % locals()) -- cgit From dbbceaebec3ca2a729582f9851f718b2b7c3f3b9 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 15:57:18 -0400 Subject: Fix up libvirt.xml.template --- nova/virt/libvirt.xml.template | 138 ++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 70 deletions(-) diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index 894b216e9..36d18ed95 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -3,47 +3,45 @@ ${memory_kb} #if $type == 'lxc' - #set $disk_prefix = '' - #set $disk_bus = '' - exe - /sbin/init + #set $disk_prefix = '' + #set $disk_bus = '' + exe + /sbin/init +#else if $type == 'uml' + #set $disk_prefix = 'ubd' + #set $disk_bus = 'uml' + uml + /usr/bin/linux + /dev/ubda #else - #if $type == 'uml' - #set $disk_prefix = 'ubd' - #set $disk_bus = 'uml' - uml - /usr/bin/linux - /dev/ubda - #else - #if $type == 'xen' - #set $disk_prefix = 'sd' - #set $disk_bus = 'scsi' - linux - /dev/xvda - #else - #set $disk_prefix = 'vd' - #set $disk_bus = 'virtio' - hvm - #end if - #if $getVar('rescue', False) - ${basepath}/kernel.rescue - ${basepath}/ramdisk.rescue - #else - #if $getVar('kernel', None) - ${kernel} - #if $type == 'xen' - ro - #else - root=/dev/vda console=ttyS0 - #end if - #if $getVar('ramdisk', None) - ${ramdisk} - #end if - #else - - #end if - #end if - #end if + #if $type == 'xen' + #set $disk_prefix = 'sd' + #set $disk_bus = 'scsi' + linux + /dev/xvda + #else + #set $disk_prefix = 'vd' + #set $disk_bus = 'virtio' + hvm + #end if + #if $getVar('rescue', False) + ${basepath}/kernel.rescue + ${basepath}/ramdisk.rescue + #else + #if $getVar('kernel', None) + ${kernel} + #if $type == 'xen' + ro + #else + root=/dev/vda console=ttyS0 + #end if + #if $getVar('ramdisk', None) + ${ramdisk} + #end if + #else + + #end if + #end if #end if @@ -52,36 +50,36 @@ ${vcpus} #if $type == 'lxc' - - - - + + + + #else - #if $getVar('rescue', False) - - - - - - - - - - - #else - - - - - - #if $getVar('local', False) - - - - - - #end if -#end if + #if $getVar('rescue', False) + + + + + + + + + + + #else + + + + + + #if $getVar('local', False) + + + + + + #end if + #end if #end if #for $nic in $nics @@ -91,7 +89,7 @@ - + #if $getVar('nic.extra_params', False) ${nic.extra_params} #end if -- cgit From 633917f56200cc11ef26717b8305ef0ccbe76569 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 16:42:09 -0400 Subject: Made param descriptions sphinx compatible. --- nova/api/openstack/images.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 54e08438a..5fd1f0163 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -51,8 +51,8 @@ class Controller(wsgi.Controller): """ Initialize new `ImageController`. - @param compute_service: `nova.compute.api:API` - @param image_service: `nova.image.service:BaseImageService` + :param compute_service: `nova.compute.api:API` + :param image_service: `nova.image.service:BaseImageService` """ _default_service = utils.import_object(flags.FLAGS.image_service) @@ -63,7 +63,7 @@ class Controller(wsgi.Controller): """ Return an index listing of images available to the request. - @param req: `wsgi.Request` object + :param req: `wsgi.Request` object """ context = req.environ['nova.context'] images = self._image_service.index(context) @@ -75,7 +75,7 @@ class Controller(wsgi.Controller): """ Return a detailed index listing of images available to the request. - @param req: `wsgi.Request` object. + :param req: `wsgi.Request` object. """ context = req.environ['nova.context'] images = self._image_service.detail(context) @@ -87,8 +87,8 @@ class Controller(wsgi.Controller): """ Return detailed information about a specific image. - @param req: `wsgi.Request` object - @param id: Image identifier (integer) + :param req: `wsgi.Request` object + :param id: Image identifier (integer) """ context = req.environ['nova.context'] @@ -110,8 +110,8 @@ class Controller(wsgi.Controller): """ Delete an image, if allowed. - @param req: `wsgi.Request` object - @param id: Image identifier (integer) + :param req: `wsgi.Request` object + :param id: Image identifier (integer) """ image_id = id context = req.environ['nova.context'] @@ -122,7 +122,7 @@ class Controller(wsgi.Controller): """ Snapshot a server instance and save the image. - @param req: `wsgi.Request` object + :param req: `wsgi.Request` object """ context = req.environ['nova.context'] content_type = req.get_content_type() -- cgit From c1ed5fc3dfeecef281df45cd2e6779fa21c63bf5 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 28 Mar 2011 22:00:17 +0100 Subject: style fixes --- nova/network/xenapi_net.py | 6 ++++-- nova/tests/fake_utils.py | 14 ++++++++++---- nova/tests/test_xenapi.py | 28 +++++++--------------------- nova/virt/xenapi/fake.py | 28 ++++++++++++++-------------- 4 files changed, 35 insertions(+), 41 deletions(-) diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 8603fd842..9a99602d9 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -27,7 +27,7 @@ from nova import flags from nova import log as logging from nova import utils from nova.virt.xenapi_conn import XenAPISession -from nova.virt.xenapi.network_utils import NetworkHelper +from nova.virt.xenapi import network_utils LOG = logging.getLogger("nova.xenapi_net") @@ -44,7 +44,9 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): session = XenAPISession(url, username, password) # Check whether bridge already exists # Retrieve network whose name_label is "bridge" - network_ref = NetworkHelper.find_network_with_name_label(session, bridge) + network_ref = network_utils.NetworkHelper.find_network_with_name_label( + session, + bridge) if network_ref == None: # If bridge does not exists # 1 - create network diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py index 23996ba95..be59970c9 100644 --- a/nova/tests/fake_utils.py +++ b/nova/tests/fake_utils.py @@ -47,14 +47,20 @@ def fake_execute_set_repliers(repliers): def fake_execute_default_reply_handler(*ignore_args, **ignore_kwargs): - """A reply handler for commands that haven't been added to the reply - list. Returns empty strings for stdout and stderr.""" + """A reply handler for commands that haven't been added to the reply list. + + Returns empty strings for stdout and stderr. + + """ return '', '' def fake_execute(*cmd_parts, **kwargs): - """This function stubs out execute, optionally executing - a preconfigued function to return expected data.""" + """This function stubs out execute. + + It optionally executes a preconfigued function to return expected data. + + """ global _fake_execute_repliers process_input = kwargs.get('process_input', None) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index bc1469223..17e3f55e9 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -14,9 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. -""" -Test suite for XenAPI. -""" +"""Test suite for XenAPI.""" import functools import os @@ -65,9 +63,7 @@ def stub_vm_utils_with_vdi_attached_here(function, should_return=True): class XenAPIVolumeTestCase(test.TestCase): - """ - Unit tests for Volume operations. - """ + """Unit tests for Volume operations.""" def setUp(self): super(XenAPIVolumeTestCase, self).setUp() self.stubs = stubout.StubOutForTesting() @@ -172,9 +168,7 @@ def reset_network(*args): class XenAPIVMTestCase(test.TestCase): - """ - Unit tests for VM operations. - """ + """Unit tests for VM operations.""" def setUp(self): super(XenAPIVMTestCase, self).setUp() self.manager = manager.AuthManager() @@ -538,9 +532,7 @@ class XenAPIVMTestCase(test.TestCase): class XenAPIDiffieHellmanTestCase(test.TestCase): - """ - Unit tests for Diffie-Hellman code. - """ + """Unit tests for Diffie-Hellman code.""" def setUp(self): super(XenAPIDiffieHellmanTestCase, self).setUp() self.alice = SimpleDH() @@ -564,9 +556,7 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): class XenAPIMigrateInstance(test.TestCase): - """ - Unit test for verifying migration-related actions. - """ + """Unit test for verifying migration-related actions.""" def setUp(self): super(XenAPIMigrateInstance, self).setUp() @@ -621,9 +611,7 @@ class XenAPIMigrateInstance(test.TestCase): class XenAPIDetermineDiskImageTestCase(test.TestCase): - """ - Unit tests for code that detects the ImageType. - """ + """Unit tests for code that detects the ImageType.""" def setUp(self): super(XenAPIDetermineDiskImageTestCase, self).setUp() glance_stubs.stubout_glance_client(self.stubs, @@ -642,9 +630,7 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): self.assertEqual(disk_type, dt) def test_instance_disk(self): - """ - If a kernel is specified then the image type is DISK (aka machine). - """ + """If a kernel is specified, the image type is DISK (aka machine).""" FLAGS.xenapi_image_service = 'objectstore' self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_MACHINE self.fake_instance.kernel_id = glance_stubs.FakeGlance.IMAGE_KERNEL diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index d79312a60..4434dbf0b 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -60,7 +60,7 @@ from nova import exception from nova import log as logging -_CLASSES = ['host', 'network', 'session', 'SR', 'VBD', \ +_CLASSES = ['host', 'network', 'session', 'SR', 'VBD', 'PBD', 'VDI', 'VIF', 'PIF', 'VM', 'VLAN', 'task'] _db_content = {} @@ -200,19 +200,19 @@ def create_local_srs(): def _create_local_sr(host_ref): - sr_ref = \ - _create_object('SR', - {'name_label': 'Local storage', - 'type': 'lvm', - 'content_type': 'user', - 'shared': False, - 'physical_size': str(1 << 30), - 'physical_utilisation': str(0), - 'virtual_allocation': str(0), - 'other_config': {'i18n-original-value-name_label': \ - 'Local storage', - 'i18n-key': 'local-storage'}, - 'VDIs': []}) + sr_ref = _create_object( + 'SR', + {'name_label': 'Local storage', + 'type': 'lvm', + 'content_type': 'user', + 'shared': False, + 'physical_size': str(1 << 30), + 'physical_utilisation': str(0), + 'virtual_allocation': str(0), + 'other_config': { + 'i18n-original-value-name_label': 'Local storage', + 'i18n-key': 'local-storage'}, + 'VDIs': []}) pbd_ref = create_pbd('', host_ref, sr_ref, True) _db_content['SR'][sr_ref]['PBDs'] = [pbd_ref] return sr_ref -- cgit From 45e3deee1581580bed56d1bdfaaf9f4814fe7963 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 17:13:37 -0400 Subject: Updated docstrings to satisfy. --- nova/api/openstack/images.py | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 5fd1f0163..ad748b4ca 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -32,10 +32,8 @@ FLAGS = flags.FLAGS class Controller(wsgi.Controller): - """ - Base `wsgi.Controller` for retrieving and displaying images in the - OpenStack API. Version-inspecific code goes here. - """ + """Base `wsgi.Controller` for retrieving and displaying images in the + OpenStack API. Version-inspecific code goes here.""" _serialization_metadata = { 'application/xml': { @@ -48,8 +46,7 @@ class Controller(wsgi.Controller): } def __init__(self, image_service=None, compute_service=None): - """ - Initialize new `ImageController`. + """Initialize new `ImageController`. :param compute_service: `nova.compute.api:API` :param image_service: `nova.image.service:BaseImageService` @@ -60,8 +57,7 @@ class Controller(wsgi.Controller): self._image_service = image_service or _default_service def index(self, req): - """ - Return an index listing of images available to the request. + """Return an index listing of images available to the request. :param req: `wsgi.Request` object """ @@ -72,8 +68,7 @@ class Controller(wsgi.Controller): return dict(images=[builder(image, detail=False) for image in images]) def detail(self, req): - """ - Return a detailed index listing of images available to the request. + """Return a detailed index listing of images available to the request. :param req: `wsgi.Request` object. """ @@ -84,8 +79,7 @@ class Controller(wsgi.Controller): return dict(images=[builder(image, detail=True) for image in images]) def show(self, req, id): - """ - Return detailed information about a specific image. + """Return detailed information about a specific image. :param req: `wsgi.Request` object :param id: Image identifier (integer) @@ -107,8 +101,7 @@ class Controller(wsgi.Controller): return dict(image=self.get_builder(req).build(image, detail=True)) def delete(self, req, id): - """ - Delete an image, if allowed. + """Delete an image, if allowed. :param req: `wsgi.Request` object :param id: Image identifier (integer) @@ -119,8 +112,7 @@ class Controller(wsgi.Controller): return webob.exc.HTTPNoContent() def create(self, req): - """ - Snapshot a server instance and save the image. + """Snapshot a server instance and save the image. :param req: `wsgi.Request` object """ @@ -146,26 +138,18 @@ class Controller(wsgi.Controller): class ControllerV10(Controller): - """ - Version 1.0 specific controller logic. - """ + """Version 1.0 specific controller logic.""" def get_builder(self, request): - """ - Property to get the ViewBuilder class we need to use. - """ + """Property to get the ViewBuilder class we need to use.""" base_url = request.application_url return images_view.ViewBuilderV10(base_url) class ControllerV11(Controller): - """ - Version 1.1 specific controller logic. - """ + """Version 1.1 specific controller logic.""" def get_builder(self, request): - """ - Property to get the ViewBuilder class we need to use. - """ + """Property to get the ViewBuilder class we need to use.""" base_url = request.application_url return images_view.ViewBuilderV11(base_url) -- cgit From 6efd9dc30870008750c9754de4672d3eea656cce Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Mon, 28 Mar 2011 17:15:54 -0400 Subject: Updated docstrings to satisfy. --- nova/api/openstack/views/images.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 9db73bd4b..8f5568f6c 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -19,29 +19,21 @@ import os.path class ViewBuilder(object): - """ - Base class for generating responses to OpenStack API requests for - information about images. - """ + """Base class for generating responses to OpenStack API requests for + information about images.""" def __init__(self, base_url): - """ - Initialize new `ViewBuilder`. - """ + """Initialize new `ViewBuilder`.""" self._url = base_url def _format_dates(self, image): - """ - Update all date fields to ensure standardized formatting. - """ + """Update all date fields to ensure standardized formatting.""" for attr in ['created_at', 'updated_at', 'deleted_at']: if image.get(attr) is not None: image[attr] = image[attr].strftime('%Y-%m-%dT%H:%M:%SZ') def _format_status(self, image): - """ - Update the status field to standardize format. - """ + """Update the status field to standardize format.""" status_mapping = { 'pending': 'queued', 'decrypting': 'preparing', @@ -56,15 +48,11 @@ class ViewBuilder(object): image['status'] = image['status'].upper() def generate_href(self, image_id): - """ - Return an href string pointing to this object. - """ + """Return an href string pointing to this object.""" return os.path.join(self._url, "images", str(image_id)) def build(self, image_obj, detail=False): - """ - Return a standardized image structure for display by the API. - """ + """Return a standardized image structure for display by the API.""" properties = image_obj.get("properties", {}) self._format_dates(image_obj) @@ -97,18 +85,15 @@ class ViewBuilder(object): class ViewBuilderV10(ViewBuilder): + """OpenStack API v1.0 Image Builder""" pass class ViewBuilderV11(ViewBuilder): - """ - OpenStack API v1.1 Image Builder - """ + """OpenStack API v1.1 Image Builder""" def build(self, image_obj, detail=False): - """ - Return a standardized image structure for display by the API. - """ + """Return a standardized image structure for display by the API.""" image = ViewBuilder.build(self, image_obj, detail) href = self.generate_href(image_obj["id"]) -- cgit From cd81e06c19893b44568a8cef37a1de30b726e236 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Mon, 28 Mar 2011 22:25:11 +0100 Subject: fix docstrings --- nova/virt/xenapi/vmops.py | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 147419f90..a9514bd61 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -279,8 +279,7 @@ class VMOps(object): "start") def snapshot(self, instance, image_id): - """ - Create snapshot from a running VM instance + """Create snapshot from a running VM instance. :param instance: instance to be snapshotted :param image_id: id of image to upload to @@ -300,6 +299,7 @@ class VMOps(object): 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer that will bundle the VHDs together and then push the bundle into Glance. + """ template_vm_ref = None try: @@ -332,8 +332,7 @@ class VMOps(object): return def migrate_disk_and_power_off(self, instance, dest): - """ - Copies a VHD from one host machine to another + """Copies a VHD from one host machine to another. :param instance: the instance that owns the VHD in question. :param dest: the destination host machine. @@ -428,13 +427,14 @@ class VMOps(object): self._session.wait_for_task(task, instance.id) def set_admin_password(self, instance, new_pass): - """ - Set the root/admin password on the VM instance. This is done via - an agent running on the VM. Communication between nova and the agent - is done via writing xenstore records. Since communication is done over - the XenAPI RPC calls, we need to encrypt the password. We're using a - simple Diffie-Hellman class instead of the more advanced one in - M2Crypto for compatibility with the agent code. + """Set the root/admin password on the VM instance. + + This is done via an agent running on the VM. Communication between nova + and the agent is done via writing xenstore records. Since communication + is done over the XenAPI RPC calls, we need to encrypt the password. + We're using a simple Diffie-Hellman class instead of the more advanced + one in M2Crypto for compatibility with the agent code. + """ # Need to uniquely identify this request. transaction_id = str(uuid.uuid4()) @@ -467,12 +467,14 @@ class VMOps(object): return resp_dict['message'] def inject_file(self, instance, path, contents): - """ - Write a file to the VM instance. The path to which it is to be - written and the contents of the file need to be supplied; both will - be base64-encoded to prevent errors with non-ASCII characters being - transmitted. If the agent does not support file injection, or the user - has disabled it, a NotImplementedError will be raised. + """Write a file to the VM instance. + + The path to which it is to be written and the contents of the file + need to be supplied; both will be base64-encoded to prevent errors + with non-ASCII characters being transmitted. If the agent does not + support file injection, or the user has disabled it, a + NotImplementedError will be raised. + """ # Files/paths must be base64-encoded for transmission to agent b64_path = base64.b64encode(path) @@ -556,8 +558,7 @@ class VMOps(object): VMHelper.destroy_vbd(self._session, vbd_ref) def _destroy_kernel_ramdisk(self, instance, vm_ref): - """ - Three situations can occur: + """Three situations can occur: 1. We have neither a ramdisk nor a kernel, in which case we are a RAW image and can omit this step @@ -567,6 +568,7 @@ class VMOps(object): 3. We have both, in which case we safely remove both the kernel and the ramdisk. + """ instance_id = instance.id if not instance.kernel_id and not instance.ramdisk_id: @@ -614,11 +616,11 @@ class VMOps(object): self._session.call_xenapi("Async.VM.destroy", rescue_vm_ref) def destroy(self, instance): - """ - Destroy VM instance + """Destroy VM instance. This is the method exposed by xenapi_conn.destroy(). The rest of the destroy_* methods are internal. + """ instance_id = instance.id LOG.info(_("Destroying VM for Instance %(instance_id)s") % locals()) @@ -627,13 +629,13 @@ class VMOps(object): def _destroy(self, instance, vm_ref, shutdown=True, destroy_kernel_ramdisk=True): - """ - Destroys VM instance by performing: + """Destroys VM instance by performing: 1. A shutdown if requested. 2. Destroying associated VDIs. 3. Destroying kernel and ramdisk files (if necessary). 4. Destroying that actual VM record. + """ if vm_ref is None: LOG.warning(_("VM is not present, skipping destroy...")) @@ -681,8 +683,8 @@ class VMOps(object): self._wait_with_callback(instance.id, task, callback) def rescue(self, instance, callback): - """ - Rescue the specified instance + """Rescue the specified instance. + - shutdown the instance VM. - set 'bootlock' to prevent the instance from starting in rescue. - spawn a rescue VM (the vm name-label will be instance-N-rescue). @@ -709,10 +711,12 @@ class VMOps(object): self._session.call_xenapi("Async.VBD.plug", rescue_vbd_ref) def unrescue(self, instance, callback): - """Unrescue the specified instance + """Unrescue the specified instance. + - unplug the instance VM's disk from the rescue VM. - teardown the rescue VM. - release the bootlock to allow the instance VM to start. + """ rescue_vm_ref = VMHelper.lookup(self._session, "%s-rescue" % instance.name) @@ -729,10 +733,11 @@ class VMOps(object): self._start(instance, original_vm_ref) def poll_rescued_instances(self, timeout): - """ - Look for expirable rescued instances + """Look for expirable rescued instances. + - forcibly exit rescue mode for any instances that have been in rescue mode for >= the provided timeout + """ last_ran = self.poll_rescue_last_ran if not last_ran: -- cgit From f67b18b61297b4cb0d641695de01e52fd37ddd1c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 28 Mar 2011 16:27:33 -0700 Subject: displays an error message if a command fails, so that the user knows something went wrong --- bin/nova-manage | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index cf0caf47e..f7308abe5 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1098,8 +1098,8 @@ def main(): script_name = argv.pop(0) if len(argv) < 1: print script_name + " category action []" - print "Available categories:" - for k, _ in CATEGORIES: + print _("Available categories:") + for k, _v in CATEGORIES: print "\t%s" % k sys.exit(2) category = argv.pop(0) @@ -1110,7 +1110,7 @@ def main(): actions = methods_of(command_object) if len(argv) < 1: print script_name + " category action []" - print "Available actions for %s category:" % category + print _("Available actions for %s category:") % category for k, _v in actions: print "\t%s" % k sys.exit(2) @@ -1122,9 +1122,12 @@ def main(): fn(*argv) sys.exit(0) except TypeError: - print "Possible wrong number of arguments supplied" + print _("Possible wrong number of arguments supplied") print "%s %s: %s" % (category, action, fn.__doc__) raise + except: + print _("Command failed, please check log for more info") + raise if __name__ == '__main__': main() -- cgit From 0e81c4175cad97359e848c993efd9a91eb981174 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 28 Mar 2011 21:22:53 -0400 Subject: Pass along the nbd flags although we dont support it just yet --- nova/virt/disk.py | 13 ++++++++----- nova/virt/libvirt_conn.py | 5 +++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 15cd49789..11a7a969e 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -116,13 +116,14 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): _unlink_device(device, nbd) -def setup_container(image, container_dir=None): +def setup_container(image, container_dir=None, nbd=False): """Setup the LXC container It will mount the loopback image to the container directory in order - to create the root filesystem for the container + to create the root filesystem for the container. + + LXC does not support qcow2 images yet. """ - nbd = "False" device = _link_device(image, nbd) out, err = utils.execute('sudo', 'mount', device, container_dir) if err: @@ -131,11 +132,13 @@ def setup_container(image, container_dir=None): _unlink_device(device, nbd) -def destroy_container(target, instance): +def destroy_container(target, instance, nbd=False): """Destroy the container once it terminates It will umount the container that is mounted, try to find the loopback device associated with the container and delete it. + + LXC does not support qcow2 images yet. """ try: container_dir = '%s/rootfs' % target @@ -145,7 +148,7 @@ def destroy_container(target, instance): for loop in out.splitlines(): if instance['name'] in loop: device = loop.split(loop, ':') - utils.execute('sudo', 'losetup', '--detach', device) + _unlink_device(device, nbd) def _link_device(image, nbd): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 66d0d195b..af2dac9e8 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -342,7 +342,7 @@ class LibvirtConnection(driver.ComputeDriver): LOG.info(_('instance %(instance_name)s: deleting instance files' ' %(target)s') % locals()) if FLAGS.libvirt_type == 'lxc': - disk.destroy_container(target, instance) + disk.destroy_container(target, instance,nbd=FLAGS.use_cow_images) if os.path.exists(target): shutil.rmtree(target) @@ -797,7 +797,8 @@ class LibvirtConnection(driver.ComputeDriver): if FLAGS.libvirt_type == 'lxc': disk.setup_container(basepath('disk'), - container_dir=container_dir) + container_dir=container_dir, + nbd=FLAGS.use_cow_images) except Exception as e: # This could be a windows image, or a vmdk format disk LOG.warn(_('instance %(inst_name)s: ignoring error injecting' -- cgit From 73f05da6400fe7f4324cf98c7d0706fb68a62870 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Tue, 29 Mar 2011 11:20:16 +0100 Subject: sorted pep8 errors that were introduced during previous fixes --- nova/virt/xenapi/vmops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a9514bd61..c96c35a6e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -430,7 +430,7 @@ class VMOps(object): """Set the root/admin password on the VM instance. This is done via an agent running on the VM. Communication between nova - and the agent is done via writing xenstore records. Since communication + and the agent is done via writing xenstore records. Since communication is done over the XenAPI RPC calls, we need to encrypt the password. We're using a simple Diffie-Hellman class instead of the more advanced one in M2Crypto for compatibility with the agent code. @@ -467,12 +467,12 @@ class VMOps(object): return resp_dict['message'] def inject_file(self, instance, path, contents): - """Write a file to the VM instance. + """Write a file to the VM instance. The path to which it is to be written and the contents of the file need to be supplied; both will be base64-encoded to prevent errors with non-ASCII characters being transmitted. If the agent does not - support file injection, or the user has disabled it, a + support file injection, or the user has disabled it, a NotImplementedError will be raised. """ -- cgit From ad6735df0de8676768353516eee4af62af2c993c Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 29 Mar 2011 08:56:42 -0400 Subject: Catch the error that mount might through a bit better --- nova/virt/disk.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 11a7a969e..8adef744f 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -124,11 +124,11 @@ def setup_container(image, container_dir=None, nbd=False): LXC does not support qcow2 images yet. """ - device = _link_device(image, nbd) - out, err = utils.execute('sudo', 'mount', device, container_dir) - if err: - raise exception.Error(_('Failed to mount filesystem: %s') - % err) + try: + device = _link_device(image, nbd) + utils.execute('sudo', 'mount', device, container_dir) + except Exception, exn: + LOG.exception(_('Failed to mount filesystem: %s'), exn) _unlink_device(device, nbd) -- cgit From c4c9c0e5b70305ac06494bde35fcd18fdf60798e Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 29 Mar 2011 09:51:02 -0400 Subject: Tweaking docstrings just in case. --- nova/api/openstack/images.py | 3 +-- nova/api/openstack/views/images.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index ad748b4ca..d672e3a0e 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -32,8 +32,7 @@ FLAGS = flags.FLAGS class Controller(wsgi.Controller): - """Base `wsgi.Controller` for retrieving and displaying images in the - OpenStack API. Version-inspecific code goes here.""" + """Base `wsgi.Controller` for retrieving/displaying images.""" _serialization_metadata = { 'application/xml': { diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 8f5568f6c..3807fa95f 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -19,8 +19,7 @@ import os.path class ViewBuilder(object): - """Base class for generating responses to OpenStack API requests for - information about images.""" + """Base class for generating responses to OpenStack API image requests.""" def __init__(self, base_url): """Initialize new `ViewBuilder`.""" -- cgit From beec33e8dbffbe3ea02eccec4952705698db377a Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 29 Mar 2011 10:01:54 -0400 Subject: Fix pep8 error --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c4d41881d..ae3a967a4 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -347,7 +347,7 @@ class LibvirtConnection(driver.ComputeDriver): LOG.info(_('instance %(instance_name)s: deleting instance files' ' %(target)s') % locals()) if FLAGS.libvirt_type == 'lxc': - disk.destroy_container(target, instance,nbd=FLAGS.use_cow_images) + disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images) if os.path.exists(target): shutil.rmtree(target) -- cgit From 07d985f8db30863bb9171e14fccbdb51d7b35f11 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 29 Mar 2011 12:06:52 -0400 Subject: Switch string concat style. --- nova/image/glance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index d8f51429e..fdf468594 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -238,5 +238,5 @@ def _parse_glance_iso8601_timestamp(timestamp): except ValueError: pass - raise ValueError(_("""%(timestamp)s does not follow any of the \ -signatures: %(ISO_FORMATS)s""") % locals()) + raise ValueError(_("%(timestamp)s does not follow any of the " + "signatures: %(ISO_FORMATS)s") % locals()) -- cgit From 2f89d5541aa11b8654b197ffe24d3fd13e945da6 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 29 Mar 2011 12:12:26 -0400 Subject: Import order. --- nova/api/openstack/images.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index d672e3a0e..e77100d7b 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,9 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. -import webob.exc import datetime +import webob.exc + from nova import compute from nova import exception from nova import flags -- cgit From 3b8aa1fed7cd6d1deb580eec0af283947060c04d Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Tue, 29 Mar 2011 21:26:17 +0400 Subject: Added checks that exists at least one network marked inhected in libvirt and xenapi --- nova/virt/libvirt_conn.py | 5 ++++- nova/virt/xenapi/vm_utils.py | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 80eb64f3c..7dd09813d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -803,6 +803,7 @@ class LibvirtConnection(driver.ComputeDriver): nets = [] ifc_template = open(FLAGS.injected_network_template).read() ifc_num = -1 + have_injected_networks = False admin_context = context.get_admin_context() for (network_ref, mapping) in network_info: ifc_num += 1 @@ -810,6 +811,7 @@ class LibvirtConnection(driver.ComputeDriver): if not 'injected' in network_ref: continue + have_injected_networks = True address = mapping['ips'][0]['ip'] address_v6 = None if FLAGS.use_ipv6: @@ -825,7 +827,8 @@ class LibvirtConnection(driver.ComputeDriver): 'netmask_v6': network_ref['netmask_v6']} nets.append(net_info) - net = str(Template(ifc_template, + if have_injected_networks: + net = str(Template(ifc_template, searchList=[{'interfaces': nets, 'use_ipv6': FLAGS.use_ipv6}])) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 2288ea8a5..18c29134d 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1108,11 +1108,13 @@ def _prepare_injectables(inst, networks_info): if networks_info: ifc_num = -1 interfaces_info = [] + have_injected_networks = False for (network_ref, info) in networks_info: ifc_num += 1 if not network_ref['injected']: continue + have_injected_networks = True ip_v4 = ip_v6 = None if 'ips' in info and len(info['ips']) > 0: ip_v4 = info['ips'][0] @@ -1131,7 +1133,9 @@ def _prepare_injectables(inst, networks_info): 'gateway_v6': ip_v6 and ip_v6['gateway'] or '', 'use_ipv6': FLAGS.use_ipv6} interfaces_info.append(interface_info) - net = str(template(template_data, + + if have_injected_networks: + net = str(template(template_data, searchList=[{'interfaces': interfaces_info, 'use_ipv6': FLAGS.use_ipv6}])) return key, net -- cgit From 8f4176f289142e48d4a2c584ad1ce270dfa53d82 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 29 Mar 2011 14:56:46 -0400 Subject: Fix up docstring --- nova/virt/disk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 8adef744f..ddea1a1f7 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -117,7 +117,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): def setup_container(image, container_dir=None, nbd=False): - """Setup the LXC container + """Setup the LXC container. It will mount the loopback image to the container directory in order to create the root filesystem for the container. @@ -133,7 +133,7 @@ def setup_container(image, container_dir=None, nbd=False): def destroy_container(target, instance, nbd=False): - """Destroy the container once it terminates + """Destroy the container once it terminates. It will umount the container that is mounted, try to find the loopback device associated with the container and delete it. -- cgit From b161ae983edbd9e8c302907e8951075546eafc48 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Tue, 29 Mar 2011 23:30:06 +0400 Subject: style fix --- nova/virt/libvirt_conn.py | 4 ++-- nova/virt/xenapi/vm_utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7dd09813d..aaa0fe8d3 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -829,8 +829,8 @@ class LibvirtConnection(driver.ComputeDriver): if have_injected_networks: net = str(Template(ifc_template, - searchList=[{'interfaces': nets, - 'use_ipv6': FLAGS.use_ipv6}])) + searchList=[{'interfaces': nets, + 'use_ipv6': FLAGS.use_ipv6}])) if key or net: inst_name = inst['name'] diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 18c29134d..d07d60800 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1136,6 +1136,6 @@ def _prepare_injectables(inst, networks_info): if have_injected_networks: net = str(template(template_data, - searchList=[{'interfaces': interfaces_info, - 'use_ipv6': FLAGS.use_ipv6}])) + searchList=[{'interfaces': interfaces_info, + 'use_ipv6': FLAGS.use_ipv6}])) return key, net -- cgit From 05a654211ab902cbb5b1b345dd3285efb1c6bf71 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 29 Mar 2011 15:48:02 -0400 Subject: Style fixes --- nova/tests/test_virt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index e6beb8e2e..958c8e3e2 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -237,13 +237,13 @@ class LibvirtConnTestCase(test.TestCase): network_ref = db.project_get_network(context.get_admin_context(), self.project.id) - fixed_ip = {'address': self.test_ip, - 'network_id': network_ref['id']} + fixed_ip = {'address': self.test_ip, + 'network_id': network_ref['id']} ctxt = context.get_admin_context() fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) db.fixed_ip_update(ctxt, self.test_ip, - {'allocated': True, + {'allocated': True, 'instance_id': instance_ref['id']}) self.flags(libvirt_type='lxc') -- cgit From 07076b1b9caf7f11c74686d546161994e2e2d691 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 29 Mar 2011 15:32:44 -0500 Subject: Make Dnsmasq_interface configurable --- bin/nova-dhcpbridge | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 7ef51feba..f42dfd6b5 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -48,6 +48,7 @@ flags.DECLARE('auth_driver', 'nova.auth.manager') flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('update_dhcp_on_disassociate', 'nova.network.manager') +flags.DEFINE_string('dnsmasq_interface', 'br0', 'Default Dnsmasq interface') LOG = logging.getLogger('nova.dhcpbridge') @@ -103,7 +104,8 @@ def main(): utils.default_flagfile(flagfile) argv = FLAGS(sys.argv) logging.setup() - interface = os.environ.get('DNSMASQ_INTERFACE', 'br0') + # check ENV first so we don't break any older deploys + interface = os.environ.get('DNSMASQ_INTERFACE', FLAGS.dnsmasq_interface) if int(os.environ.get('TESTING', '0')): from nova.tests import fake_flags action = argv[1] -- cgit From 8cfc3d5e6b033a99fc47b3df8ac7e798d107240a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 29 Mar 2011 13:43:03 -0700 Subject: don't print the error message on sys.exit(0) --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index f7308abe5..25695482f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1125,7 +1125,7 @@ def main(): print _("Possible wrong number of arguments supplied") print "%s %s: %s" % (category, action, fn.__doc__) raise - except: + except Exception: print _("Command failed, please check log for more info") raise -- cgit