diff options
| author | Dan Wendlandt <dan@nicira.com> | 2011-08-12 17:33:30 -0700 |
|---|---|---|
| committer | Dan Wendlandt <dan@nicira.com> | 2011-08-12 17:33:30 -0700 |
| commit | 86d2109b8f3b27e460aa41c11924166cb07d7bb4 (patch) | |
| tree | 99b79011748fc0b67df507db103779237caa40a9 /nova/virt | |
| parent | 18f09f165b5dca5f11253b143045b2ff7327532d (diff) | |
| parent | fe0bde67193ce76376e72a7263b89240a63722a8 (diff) | |
| download | nova-86d2109b8f3b27e460aa41c11924166cb07d7bb4.tar.gz nova-86d2109b8f3b27e460aa41c11924166cb07d7bb4.tar.xz nova-86d2109b8f3b27e460aa41c11924166cb07d7bb4.zip | |
merge in trunk, resolving conflicts with ttx's branch to switch from using sudo to run_as_root=True
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/disk.py | 47 | ||||
| -rw-r--r-- | nova/virt/driver.py | 35 | ||||
| -rw-r--r-- | nova/virt/fake.py | 10 | ||||
| -rw-r--r-- | nova/virt/hyperv.py | 10 | ||||
| -rw-r--r-- | nova/virt/libvirt.xml.template | 33 | ||||
| -rw-r--r-- | nova/virt/libvirt/connection.py | 230 | ||||
| -rw-r--r-- | nova/virt/libvirt/vif.py | 19 | ||||
| -rw-r--r-- | nova/virt/vmwareapi/io_util.py | 5 | ||||
| -rw-r--r-- | nova/virt/vmwareapi/vif.py | 2 | ||||
| -rw-r--r-- | nova/virt/vmwareapi/vim_util.py | 10 | ||||
| -rw-r--r-- | nova/virt/vmwareapi/vmware_images.py | 6 | ||||
| -rw-r--r-- | nova/virt/vmwareapi_conn.py | 6 | ||||
| -rw-r--r-- | nova/virt/xenapi/vm_utils.py | 19 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 157 | ||||
| -rw-r--r-- | nova/virt/xenapi/volume_utils.py | 4 | ||||
| -rw-r--r-- | nova/virt/xenapi_conn.py | 38 |
16 files changed, 396 insertions, 235 deletions
diff --git a/nova/virt/disk.py b/nova/virt/disk.py index f8aea1f34..19f3ec185 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -73,7 +73,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): try: if not partition is None: # create partition - out, err = utils.execute('sudo', 'kpartx', '-a', device) + out, err = utils.execute('kpartx', '-a', device, run_as_root=True) if err: raise exception.Error(_('Failed to load partition: %s') % err) mapped_device = '/dev/mapper/%sp%s' % (device.split('/')[-1], @@ -90,14 +90,14 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): mapped_device) # Configure ext2fs so that it doesn't auto-check every N boots - out, err = utils.execute('sudo', 'tune2fs', - '-c', 0, '-i', 0, mapped_device) + out, err = utils.execute('tune2fs', '-c', 0, '-i', 0, + mapped_device, run_as_root=True) tmpdir = tempfile.mkdtemp() try: # mount loopback to dir - out, err = utils.execute( - 'sudo', 'mount', mapped_device, tmpdir) + out, err = utils.execute('mount', mapped_device, tmpdir, + run_as_root=True) if err: raise exception.Error(_('Failed to mount filesystem: %s') % err) @@ -106,14 +106,14 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): inject_data_into_fs(tmpdir, key, net, utils.execute) finally: # unmount device - utils.execute('sudo', 'umount', mapped_device) + utils.execute('umount', mapped_device, run_as_root=True) finally: # remove temporary directory utils.execute('rmdir', tmpdir) finally: if not partition is None: # remove partitions - utils.execute('sudo', 'kpartx', '-d', device) + utils.execute('kpartx', '-d', device, run_as_root=True) finally: _unlink_device(device, nbd) @@ -128,7 +128,7 @@ def setup_container(image, container_dir=None, nbd=False): """ try: device = _link_device(image, nbd) - utils.execute('sudo', 'mount', device, container_dir) + utils.execute('mount', device, container_dir, run_as_root=True) except Exception, exn: LOG.exception(_('Failed to mount filesystem: %s'), exn) _unlink_device(device, nbd) @@ -144,9 +144,9 @@ def destroy_container(target, instance, nbd=False): """ try: container_dir = '%s/rootfs' % target - utils.execute('sudo', 'umount', container_dir) + utils.execute('umount', container_dir, run_as_root=True) finally: - out, err = utils.execute('sudo', 'losetup', '-a') + out, err = utils.execute('losetup', '-a', run_as_root=True) for loop in out.splitlines(): if instance['name'] in loop: device = loop.split(loop, ':') @@ -157,7 +157,7 @@ def _link_device(image, nbd): """Link image to device using loopback or nbd""" if nbd: device = _allocate_device() - utils.execute('sudo', 'qemu-nbd', '-c', device, image) + utils.execute('qemu-nbd', '-c', device, image, run_as_root=True) # NOTE(vish): this forks into another process, so give it a chance # to set up before continuuing for i in xrange(FLAGS.timeout_nbd): @@ -166,7 +166,8 @@ def _link_device(image, nbd): time.sleep(1) raise exception.Error(_('nbd device %s did not show up') % device) else: - out, err = utils.execute('sudo', 'losetup', '--find', '--show', image) + out, err = utils.execute('losetup', '--find', '--show', image, + run_as_root=True) if err: raise exception.Error(_('Could not attach image to loopback: %s') % err) @@ -176,10 +177,10 @@ def _link_device(image, nbd): def _unlink_device(device, nbd): """Unlink image from device using loopback or nbd""" if nbd: - utils.execute('sudo', 'qemu-nbd', '-d', device) + utils.execute('qemu-nbd', '-d', device, run_as_root=True) _free_device(device) else: - utils.execute('sudo', 'losetup', '--detach', device) + utils.execute('losetup', '--detach', device, run_as_root=True) _DEVICES = ['/dev/nbd%s' % i for i in xrange(FLAGS.max_nbd_devices)] @@ -220,12 +221,12 @@ def _inject_key_into_fs(key, fs, execute=None): fs is the path to the base of the filesystem into which to inject the key. """ sshdir = os.path.join(fs, 'root', '.ssh') - utils.execute('sudo', 'mkdir', '-p', sshdir) # existing dir doesn't matter - utils.execute('sudo', 'chown', 'root', sshdir) - utils.execute('sudo', 'chmod', '700', sshdir) + utils.execute('mkdir', '-p', sshdir, run_as_root=True) + utils.execute('chown', 'root', sshdir, run_as_root=True) + utils.execute('chmod', '700', sshdir, run_as_root=True) keyfile = os.path.join(sshdir, 'authorized_keys') - utils.execute('sudo', 'tee', '-a', keyfile, - process_input='\n' + key.strip() + '\n') + utils.execute('tee', '-a', keyfile, + process_input='\n' + key.strip() + '\n', run_as_root=True) def _inject_net_into_fs(net, fs, execute=None): @@ -234,8 +235,8 @@ def _inject_net_into_fs(net, fs, execute=None): net is the contents of /etc/network/interfaces. """ netdir = os.path.join(os.path.join(fs, 'etc'), 'network') - utils.execute('sudo', 'mkdir', '-p', netdir) # existing dir doesn't matter - utils.execute('sudo', 'chown', 'root:root', netdir) - utils.execute('sudo', 'chmod', 755, netdir) + utils.execute('mkdir', '-p', netdir, run_as_root=True) + utils.execute('chown', 'root:root', netdir, run_as_root=True) + utils.execute('chmod', 755, netdir, run_as_root=True) netfile = os.path.join(netdir, 'interfaces') - utils.execute('sudo', 'tee', netfile, process_input=net) + utils.execute('tee', netfile, process_input=net, run_as_root=True) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 4f3cfefad..df4a66ac2 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -32,6 +32,33 @@ class InstanceInfo(object): self.state = state +def block_device_info_get_root(block_device_info): + block_device_info = block_device_info or {} + return block_device_info.get('root_device_name') + + +def block_device_info_get_swap(block_device_info): + block_device_info = block_device_info or {} + return block_device_info.get('swap') or {'device_name': None, + 'swap_size': 0} + + +def swap_is_usable(swap): + return swap and swap['device_name'] and swap['swap_size'] > 0 + + +def block_device_info_get_ephemerals(block_device_info): + block_device_info = block_device_info or {} + ephemerals = block_device_info.get('ephemerals') or [] + return ephemerals + + +def block_device_info_get_mapping(block_device_info): + block_device_info = block_device_info or {} + block_device_mapping = block_device_info.get('block_device_mapping') or [] + return block_device_mapping + + class ComputeDriver(object): """Base class for compute drivers. @@ -65,8 +92,8 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def spawn(self, context, instance, network_info, - block_device_mapping=None): + def spawn(self, context, instance, + network_info=None, block_device_info=None): """Launch a VM for the specified instance""" raise NotImplementedError() @@ -282,6 +309,10 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + raise NotImplementedError() + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" # TODO(Vek): Need to pass context in for access to auth_token diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 80abcc644..880702af1 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -129,8 +129,8 @@ class FakeConnection(driver.ComputeDriver): info_list.append(self._map_to_instance_info(instance)) return info_list - def spawn(self, context, instance, network_info, - block_device_mapping=None): + def spawn(self, context, instance, + network_info=None, block_device_info=None): """ Create a new instance/VM/domain on the virtualization platform. @@ -294,7 +294,7 @@ class FakeConnection(driver.ComputeDriver): """ pass - def destroy(self, instance, network_info): + def destroy(self, instance, network_info, cleanup=True): key = instance.name if key in self.instances: del self.instances[key] @@ -512,6 +512,10 @@ class FakeConnection(driver.ComputeDriver): """Return fake Host Status of ram, disk, network.""" return self.host_status + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 3428a7fc1..03a78db1f 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -138,8 +138,8 @@ class HyperVConnection(driver.ComputeDriver): return instance_infos - def spawn(self, context, instance, network_info, - block_device_mapping=None): + def spawn(self, context, instance, + network_info=None, block_device_info=None): """ Create a new VM and start it.""" vm = self._lookup(instance.name) if vm is not None: @@ -374,7 +374,7 @@ class HyperVConnection(driver.ComputeDriver): raise exception.InstanceNotFound(instance_id=instance.id) self._set_vm_state(instance.name, 'Reboot') - def destroy(self, instance, network_info): + def destroy(self, instance, network_info, cleanup=True): """Destroy the VM. Also destroy the associated VHD disk files""" LOG.debug(_("Got request to destroy vm %s"), instance.name) vm = self._lookup(instance.name) @@ -499,6 +499,10 @@ class HyperVConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index a75636390..210e2b0fb 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -3,24 +3,22 @@ <memory>${memory_kb}</memory> <os> #if $type == 'lxc' - #set $disk_prefix = '' #set $disk_bus = '' <type>exe</type> <init>/sbin/init</init> #else if $type == 'uml' - #set $disk_prefix = 'ubd' #set $disk_bus = 'uml' <type>uml</type> <kernel>/usr/bin/linux</kernel> - <root>/dev/ubda</root> + #set $root_device_name = $getVar('root_device_name', '/dev/ubda') + <root>${root_device_name}</root> #else #if $type == 'xen' - #set $disk_prefix = 'sd' #set $disk_bus = 'scsi' <type>linux</type> - <root>/dev/xvda</root> + #set $root_device_name = $getVar('root_device_name', '/dev/xvda') + <root>${root_device_name}</root> #else - #set $disk_prefix = 'vd' #set $disk_bus = 'virtio' <type>hvm</type> #end if @@ -33,7 +31,8 @@ #if $type == 'xen' <cmdline>ro</cmdline> #else - <cmdline>root=/dev/vda console=ttyS0</cmdline> + #set $root_device_name = $getVar('root_device_name', '/dev/vda') + <cmdline>root=${root_device_name} console=ttyS0</cmdline> #end if #if $getVar('ramdisk', None) <initrd>${ramdisk}</initrd> @@ -71,16 +70,30 @@ <disk type='file'> <driver type='${driver_type}'/> <source file='${basepath}/disk'/> - <target dev='${disk_prefix}a' bus='${disk_bus}'/> + <target dev='${root_device}' bus='${disk_bus}'/> </disk> #end if - #if $getVar('local', False) + #if $getVar('local_device', False) <disk type='file'> <driver type='${driver_type}'/> <source file='${basepath}/disk.local'/> - <target dev='${disk_prefix}b' bus='${disk_bus}'/> + <target dev='${local_device}' bus='${disk_bus}'/> </disk> #end if + #for $eph in $ephemerals + <disk type='block'> + <driver type='${driver_type}'/> + <source dev='${basepath}/${eph.device_path}'/> + <target dev='${eph.device}' bus='${disk_bus}'/> + </disk> + #end for + #if $getVar('swap_device', False) + <disk type='file'> + <driver type='${driver_type}'/> + <source file='${basepath}/disk.swap'/> + <target dev='${swap_device}' bus='${disk_bus}'/> + </disk> + #end if #for $vol in $volumes <disk type='${vol.type}'> <driver type='raw'/> diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 0acf25d28..6d043577a 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -43,7 +43,6 @@ import os import random import re import shutil -import subprocess import sys import tempfile import time @@ -54,6 +53,7 @@ from xml.etree import ElementTree from eventlet import greenthread from eventlet import tpool +from nova import block_device from nova import context as nova_context from nova import db from nova import exception @@ -121,8 +121,6 @@ flags.DEFINE_integer('live_migration_bandwidth', 0, 'Define live migration behavior') flags.DEFINE_string('qemu_img', 'qemu-img', 'binary to use for qemu-img commands') -flags.DEFINE_bool('start_guests_on_host_boot', False, - 'Whether to restart guests when the host reboots') flags.DEFINE_string('libvirt_vif_type', 'bridge', 'Type of VIF to create.') flags.DEFINE_string('libvirt_vif_driver', @@ -153,8 +151,8 @@ def _late_load_cheetah(): Template = t.Template -def _strip_dev(mount_path): - return re.sub(r'^/dev/', '', mount_path) +def _get_eph_disk(ephemeral): + return 'disk.eph' + str(ephemeral['num']) class LibvirtConnection(driver.ComputeDriver): @@ -173,27 +171,8 @@ class LibvirtConnection(driver.ComputeDriver): self.vif_driver = utils.import_object(FLAGS.libvirt_vif_driver) def init_host(self, host): - # Adopt existing VM's running here - ctxt = nova_context.get_admin_context() - for instance in db.instance_get_all_by_host(ctxt, host): - try: - LOG.debug(_('Checking state of %s'), instance['name']) - state = self.get_info(instance['name'])['state'] - except exception.NotFound: - state = power_state.SHUTOFF - - LOG.debug(_('Current state of %(name)s was %(state)s.'), - {'name': instance['name'], 'state': state}) - db.instance_set_state(ctxt, instance['id'], state) - - # NOTE(justinsb): We no longer delete SHUTOFF instances, - # the user may want to power them back on - - if state != power_state.RUNNING: - continue - self.firewall_driver.setup_basic_filtering(instance) - self.firewall_driver.prepare_instance_filter(instance) - self.firewall_driver.apply_instance_filter(instance) + # NOTE(nsokolov): moved instance restarting to ComputeManager + pass def _get_connection(self): if not self._wrapped_conn or not self._test_connection(): @@ -370,7 +349,7 @@ class LibvirtConnection(driver.ComputeDriver): """Returns the xml for the disk mounted at device""" try: doc = libxml2.parseDoc(xml) - except: + except Exception: return None ctx = doc.xpathNewContext() try: @@ -413,9 +392,7 @@ class LibvirtConnection(driver.ComputeDriver): nova.image.get_image_service(image_href) snapshot = snapshot_image_service.show(context, snapshot_image_id) - metadata = {'disk_format': base['disk_format'], - 'container_format': base['container_format'], - 'is_public': False, + metadata = {'is_public': False, 'status': 'active', 'name': snapshot['name'], 'properties': { @@ -430,6 +407,12 @@ class LibvirtConnection(driver.ComputeDriver): arch = base['properties']['architecture'] metadata['properties']['architecture'] = arch + if 'disk_format' in base: + metadata['disk_format'] = base['disk_format'] + + if 'container_format' in base: + metadata['container_format'] = base['container_format'] + # Make the snapshot snapshot_name = uuid.uuid4().hex snapshot_xml = """ @@ -591,24 +574,18 @@ class LibvirtConnection(driver.ComputeDriver): # NOTE(ilyaalekseyev): Implementation like in multinics # for xenapi(tr3buchet) @exception.wrap_exception() - def spawn(self, context, instance, network_info, - block_device_mapping=None): + def spawn(self, context, instance, + network_info=None, block_device_info=None): xml = self.to_xml(instance, False, network_info=network_info, - block_device_mapping=block_device_mapping) - block_device_mapping = block_device_mapping or [] + block_device_info=block_device_info) self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(context, instance, xml, network_info=network_info, - block_device_mapping=block_device_mapping) + block_device_info=block_device_info) domain = self._create_new_domain(xml) LOG.debug(_("instance %s: is running"), instance['name']) self.firewall_driver.apply_instance_filter(instance) - if FLAGS.start_guests_on_host_boot: - LOG.debug(_("instance %s: setting autostart ON") % - instance['name']) - domain.setAutostart(1) - def _wait_for_boot(): """Called at an interval until the VM is running.""" instance_name = instance['name'] @@ -634,9 +611,10 @@ class LibvirtConnection(driver.ComputeDriver): if virsh_output.startswith('/dev/'): LOG.info(_("cool, it's a device")) - out, err = utils.execute('sudo', 'dd', + out, err = utils.execute('dd', "if=%s" % virsh_output, 'iflag=nonblock', + run_as_root=True, check_exit_code=False) return out else: @@ -659,7 +637,7 @@ class LibvirtConnection(driver.ComputeDriver): console_log = os.path.join(FLAGS.instances_path, instance['name'], 'console.log') - utils.execute('sudo', 'chown', os.getuid(), console_log) + utils.execute('chown', os.getuid(), console_log, run_as_root=True) if FLAGS.libvirt_type == 'xen': # Xen is special @@ -707,10 +685,10 @@ class LibvirtConnection(driver.ComputeDriver): ajaxterm_cmd = 'sudo socat - %s' \ % get_pty_for_instance(instance['name']) - cmd = '%s/tools/ajaxterm/ajaxterm.py --command "%s" -t %s -p %s' \ - % (utils.novadir(), ajaxterm_cmd, token, port) + cmd = ['%s/tools/ajaxterm/ajaxterm.py' % utils.novadir(), + '--command', ajaxterm_cmd, '-t', token, '-p', port] - subprocess.Popen(cmd, shell=True) + utils.execute(cmd) return {'token': token, 'host': host, 'port': port} def get_host_ip_addr(self): @@ -781,11 +759,14 @@ class LibvirtConnection(driver.ComputeDriver): utils.execute('truncate', target, '-s', "%dG" % local_gb) # TODO(vish): should we format disk by default? + def _create_swap(self, target, swap_gb): + """Create a swap file of specified size""" + self._create_local(target, swap_gb) + utils.execute('mkswap', target) + def _create_image(self, context, inst, libvirt_xml, suffix='', disk_images=None, network_info=None, - block_device_mapping=None): - block_device_mapping = block_device_mapping or [] - + block_device_info=None): if not suffix: suffix = '' @@ -844,8 +825,8 @@ class LibvirtConnection(driver.ComputeDriver): size = None root_fname += "_sm" - if not self._volume_in_mapping(self.root_mount_device, - block_device_mapping): + if not self._volume_in_mapping(self.default_root_device, + block_device_info): self._cache_image(fn=self._fetch_image, context=context, target=basepath('disk'), @@ -856,13 +837,38 @@ class LibvirtConnection(driver.ComputeDriver): project_id=inst['project_id'], size=size) - if inst_type['local_gb'] and not self._volume_in_mapping( - self.local_mount_device, block_device_mapping): + local_gb = inst['local_gb'] + if local_gb and not self._volume_in_mapping( + self.default_local_device, block_device_info): self._cache_image(fn=self._create_local, target=basepath('disk.local'), - fname="local_%s" % inst_type['local_gb'], + fname="local_%s" % local_gb, + cow=FLAGS.use_cow_images, + local_gb=local_gb) + + for eph in driver.block_device_info_get_ephemerals(block_device_info): + self._cache_image(fn=self._create_local, + target=basepath(_get_eph_disk(eph)), + fname="local_%s" % eph['size'], cow=FLAGS.use_cow_images, - local_gb=inst_type['local_gb']) + local_gb=eph['size']) + + swap_gb = 0 + + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + swap_gb = swap['swap_size'] + elif (inst_type['swap'] > 0 and + not self._volume_in_mapping(self.default_swap_device, + block_device_info)): + swap_gb = inst_type['swap'] + + if swap_gb > 0: + self._cache_image(fn=self._create_swap, + target=basepath('disk.swap'), + fname="swap_%s" % swap_gb, + cow=FLAGS.use_cow_images, + swap_gb=swap_gb) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -906,7 +912,7 @@ class LibvirtConnection(driver.ComputeDriver): 'netmask': netmask, 'gateway': mapping['gateway'], 'broadcast': mapping['broadcast'], - 'dns': mapping['dns'], + 'dns': ' '.join(mapping['dns']), 'address_v6': address_v6, 'gateway6': gateway_v6, 'netmask_v6': netmask_v6} @@ -941,18 +947,37 @@ class LibvirtConnection(driver.ComputeDriver): ' data into image %(img_id)s (%(e)s)') % locals()) if FLAGS.libvirt_type == 'uml': - utils.execute('sudo', 'chown', 'root', basepath('disk')) - - root_mount_device = 'vda' # FIXME for now. it's hard coded. - local_mount_device = 'vdb' # FIXME for now. it's hard coded. - - def _volume_in_mapping(self, mount_device, block_device_mapping): - mount_device_ = _strip_dev(mount_device) - for vol in block_device_mapping: - vol_mount_device = _strip_dev(vol['mount_device']) - if vol_mount_device == mount_device_: - return True - return False + utils.execute('chown', 'root', basepath('disk'), run_as_root=True) + + if FLAGS.libvirt_type == 'uml': + _disk_prefix = 'ubd' + elif FLAGS.libvirt_type == 'xen': + _disk_prefix = 'sd' + elif FLAGS.libvirt_type == 'lxc': + _disk_prefix = '' + else: + _disk_prefix = 'vd' + + default_root_device = _disk_prefix + 'a' + default_local_device = _disk_prefix + 'b' + default_swap_device = _disk_prefix + 'c' + + def _volume_in_mapping(self, mount_device, block_device_info): + block_device_list = [block_device.strip_dev(vol['mount_device']) + for vol in + driver.block_device_info_get_mapping( + block_device_info)] + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + block_device_list.append( + block_device.strip_dev(swap['device_name'])) + block_device_list += [block_device.strip_dev(ephemeral['device_name']) + for ephemeral in + driver.block_device_info_get_ephemerals( + block_device_info)] + + LOG.debug(_("block_device_list %s"), block_device_list) + return block_device.strip_dev(mount_device) in block_device_list def _get_volume_device_info(self, device_path): if device_path.startswith('/dev/'): @@ -964,8 +989,9 @@ class LibvirtConnection(driver.ComputeDriver): raise exception.InvalidDevicePath(path=device_path) def _prepare_xml_info(self, instance, rescue=False, network_info=None, - block_device_mapping=None): - block_device_mapping = block_device_mapping or [] + block_device_info=None): + block_device_mapping = driver.block_device_info_get_mapping( + block_device_info) # TODO(adiantum) remove network_info creation code # when multinics will be completed if not network_info: @@ -984,17 +1010,27 @@ class LibvirtConnection(driver.ComputeDriver): driver_type = 'raw' for vol in block_device_mapping: - vol['mount_device'] = _strip_dev(vol['mount_device']) + vol['mount_device'] = block_device.strip_dev(vol['mount_device']) (vol['type'], vol['protocol'], vol['name']) = \ self._get_volume_device_info(vol['device_path']) - ebs_root = self._volume_in_mapping(self.root_mount_device, - block_device_mapping) - if self._volume_in_mapping(self.local_mount_device, - block_device_mapping): - local_gb = False - else: - local_gb = inst_type['local_gb'] + ebs_root = self._volume_in_mapping(self.default_root_device, + block_device_info) + + local_device = False + if not (self._volume_in_mapping(self.default_local_device, + block_device_info) or + 0 in [eph['num'] for eph in + driver.block_device_info_get_ephemerals( + block_device_info)]): + if instance['local_gb'] > 0: + local_device = self.default_local_device + + ephemerals = [] + for eph in driver.block_device_info_get_ephemerals(block_device_info): + ephemerals.append({'device_path': _get_eph_disk(eph), + 'device': block_device.strip_dev( + eph['device_name'])}) xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], @@ -1003,12 +1039,35 @@ class LibvirtConnection(driver.ComputeDriver): 'memory_kb': inst_type['memory_mb'] * 1024, 'vcpus': inst_type['vcpus'], 'rescue': rescue, - 'local': local_gb, + 'disk_prefix': self._disk_prefix, 'driver_type': driver_type, 'vif_type': FLAGS.libvirt_vif_type, 'nics': nics, 'ebs_root': ebs_root, - 'volumes': block_device_mapping} + 'local_device': local_device, + 'volumes': block_device_mapping, + 'ephemerals': ephemerals} + + root_device_name = driver.block_device_info_get_root(block_device_info) + if root_device_name: + xml_info['root_device'] = block_device.strip_dev(root_device_name) + xml_info['root_device_name'] = root_device_name + else: + # NOTE(yamahata): + # for nova.api.ec2.cloud.CloudController.get_metadata() + xml_info['root_device'] = self.default_root_device + db.instance_update( + nova_context.get_admin_context(), instance['id'], + {'root_device_name': '/dev/' + self.default_root_device}) + + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + xml_info['swap_device'] = block_device.strip_dev( + swap['device_name']) + elif (inst_type['swap'] > 0 and + not self._volume_in_mapping(self.default_swap_device, + block_device_info)): + xml_info['swap_device'] = self.default_swap_device if FLAGS.vnc_enabled and FLAGS.libvirt_type not in ('lxc', 'uml'): xml_info['vncserver_host'] = FLAGS.vncserver_host @@ -1024,12 +1083,11 @@ class LibvirtConnection(driver.ComputeDriver): return xml_info def to_xml(self, instance, rescue=False, network_info=None, - block_device_mapping=None): - block_device_mapping = block_device_mapping or [] + block_device_info=None): # TODO(termie): cache? LOG.debug(_('instance %s: starting toXML method'), instance['name']) xml_info = self._prepare_xml_info(instance, rescue, network_info, - block_device_mapping) + block_device_info) xml = str(Template(self.libvirt_xml, searchList=[xml_info])) LOG.debug(_('instance %s: finished toXML method'), instance['name']) return xml @@ -1103,7 +1161,7 @@ class LibvirtConnection(driver.ComputeDriver): try: doc = libxml2.parseDoc(xml) - except: + except Exception: return [] ctx = doc.xpathNewContext() @@ -1144,7 +1202,7 @@ class LibvirtConnection(driver.ComputeDriver): try: doc = libxml2.parseDoc(xml) - except: + except Exception: return [] ctx = doc.xpathNewContext() @@ -1584,6 +1642,10 @@ class LibvirtConnection(driver.ComputeDriver): """See xenapi_conn.py implementation.""" pass + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + pass + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index f42eeec98..57f5aa87c 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -25,6 +25,7 @@ from nova.network import linux_net from nova.virt.libvirt import netutils from nova import utils from nova.virt.vif import VIFDriver +from nova import exception LOG = logging.getLogger('nova.virt.libvirt.vif') @@ -104,16 +105,18 @@ class LibvirtOpenVswitchDriver(VIFDriver): dev = "tap-%s" % vif_id iface_id = "nova-" + vif_id if not linux_net._device_exists(dev): - utils.execute('sudo', 'ip', 'tuntap', 'add', dev, 'mode', 'tap') - utils.execute('sudo', 'ip', 'link', 'set', dev, 'up') - utils.execute('sudo', 'ovs-vsctl', '--', '--may-exist', 'add-port', + utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap', + run_as_root=True) + utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True) + utils.execute('ovs-vsctl', '--', '--may-exist', 'add-port', FLAGS.libvirt_ovs_bridge, dev, '--', 'set', 'Interface', dev, "external-ids:iface-id=%s" % iface_id, '--', 'set', 'Interface', dev, "external-ids:iface-status=active", '--', 'set', 'Interface', dev, - "external-ids:attached-mac=%s" % mapping['mac']) + "external-ids:attached-mac=%s" % mapping['mac'], + run_as_root=True) result = { 'script': '', @@ -127,10 +130,10 @@ class LibvirtOpenVswitchDriver(VIFDriver): vif_id = str(instance['id']) + "-" + str(network['id']) dev = "tap-%s" % vif_id try: - utils.execute('sudo', 'ovs-vsctl', 'del-port', - network['bridge'], dev) - utils.execute('sudo', 'ip', 'link', 'delete', dev) - except: + utils.execute('ovs-vsctl', 'del-port', + network['bridge'], dev, run_as_root=True) + utils.execute('ip', 'link', 'delete', dev, run_as_root=True) + except exception.ProcessExecutionError: LOG.warning(_("Failed while unplugging vif of instance '%s'"), instance['name']) raise diff --git a/nova/virt/vmwareapi/io_util.py b/nova/virt/vmwareapi/io_util.py index 2ec773b7b..409242800 100644 --- a/nova/virt/vmwareapi/io_util.py +++ b/nova/virt/vmwareapi/io_util.py @@ -68,7 +68,10 @@ class GlanceWriteThread(object): """Ensures that image data is written to in the glance client and that
it is in correct ('active')state."""
- def __init__(self, input, glance_client, image_id, image_meta={}):
+ def __init__(self, input, glance_client, image_id, image_meta=None):
+ if not image_meta:
+ image_meta = {}
+
self.input = input
self.glance_client = glance_client
self.image_id = image_id
diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py index b3e43b209..fb6548b34 100644 --- a/nova/virt/vmwareapi/vif.py +++ b/nova/virt/vmwareapi/vif.py @@ -63,7 +63,7 @@ class VMWareVlanBridgeDriver(VIFDriver): vswitch_associated = network_utils.get_vswitch_for_vlan_interface( session, vlan_interface) if vswitch_associated is None: - raise exception.SwicthNotFoundForNetworkAdapter( + raise exception.SwitchNotFoundForNetworkAdapter( adapter=vlan_interface) # Check whether bridge already exists and retrieve the the ref of the # network whose name_label is "bridge" diff --git a/nova/virt/vmwareapi/vim_util.py b/nova/virt/vmwareapi/vim_util.py index 11214231c..e03daddac 100644 --- a/nova/virt/vmwareapi/vim_util.py +++ b/nova/virt/vmwareapi/vim_util.py @@ -95,9 +95,12 @@ def build_recursive_traversal_spec(client_factory): def build_property_spec(client_factory, type="VirtualMachine",
- properties_to_collect=["name"],
+ properties_to_collect=None,
all_properties=False):
"""Builds the Property Spec."""
+ if not properties_to_collect:
+ properties_to_collect = ["name"]
+
property_spec = client_factory.create('ns0:PropertySpec')
property_spec.all = all_properties
property_spec.pathSet = properties_to_collect
@@ -155,8 +158,11 @@ def get_dynamic_property(vim, mobj, type, property_name): return property_value
-def get_objects(vim, type, properties_to_collect=["name"], all=False):
+def get_objects(vim, type, properties_to_collect=None, all=False):
"""Gets the list of objects of the type specified."""
+ if not properties_to_collect:
+ properties_to_collect = ["name"]
+
client_factory = vim.client.factory
object_spec = build_object_spec(client_factory,
vim.get_service_content().rootFolder,
diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py index 70adba74f..f5f75dae2 100644 --- a/nova/virt/vmwareapi/vmware_images.py +++ b/nova/virt/vmwareapi/vmware_images.py @@ -33,11 +33,15 @@ QUEUE_BUFFER_SIZE = 10 def start_transfer(read_file_handle, data_size, write_file_handle=None,
- glance_client=None, image_id=None, image_meta={}):
+ glance_client=None, image_id=None, image_meta=None):
"""Start the data transfer from the reader to the writer.
Reader writes to the pipe and the writer reads from the pipe. This means
that the total transfer time boils down to the slower of the read/write
and not the addition of the two times."""
+
+ if not image_meta:
+ image_meta = {}
+
# The pipe that acts as an intermediate store of data for reader to write
# to and writer to grab from.
thread_safe_pipe = io_util.ThreadSafePipe(QUEUE_BUFFER_SIZE, data_size)
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 3d209fa99..243ee64f5 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -137,7 +137,7 @@ class VMWareESXConnection(driver.ComputeDriver): """Reboot VM instance."""
self._vmops.reboot(instance, network_info)
- def destroy(self, instance, network_info):
+ def destroy(self, instance, network_info, cleanup=True):
"""Destroy VM instance."""
self._vmops.destroy(instance, network_info)
@@ -191,6 +191,10 @@ class VMWareESXConnection(driver.ComputeDriver): """This method is supported only by libvirt."""
return
+ def host_power_action(self, host, action):
+ """Reboots, shuts down or powers up the host."""
+ pass
+
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""
pass
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 63bc191cf..6c44d53d4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -797,7 +797,7 @@ def get_vdi_for_vm_safely(session, vm_ref): else: num_vdis = len(vdi_refs) if num_vdis != 1: - raise exception.Exception(_("Unexpected number of VDIs" + raise exception.Error(_("Unexpected number of VDIs" "(%(num_vdis)s) found" " for VM %(vm_ref)s") % locals()) @@ -967,7 +967,7 @@ def _stream_disk(dev, image_type, virtual_size, image_file): offset = MBR_SIZE_BYTES _write_partition(virtual_size, dev) - utils.execute('sudo', 'chown', os.getuid(), '/dev/%s' % dev) + utils.execute('chown', os.getuid(), '/dev/%s' % dev, run_as_root=True) with open('/dev/%s' % dev, 'wb') as f: f.seek(offset) @@ -986,10 +986,11 @@ def _write_partition(virtual_size, dev): def execute(*cmd, **kwargs): return utils.execute(*cmd, **kwargs) - execute('sudo', 'parted', '--script', dest, 'mklabel', 'msdos') - execute('sudo', 'parted', '--script', dest, 'mkpart', 'primary', + execute('parted', '--script', dest, 'mklabel', 'msdos', run_as_root=True) + execute('parted', '--script', dest, 'mkpart', 'primary', '%ds' % primary_first, - '%ds' % primary_last) + '%ds' % primary_last, + run_as_root=True) LOG.debug(_('Writing partition table %s done.'), dest) @@ -1002,9 +1003,9 @@ def get_name_label_for_image(image): def _mount_filesystem(dev_path, dir): """mounts the device specified by dev_path in dir""" try: - out, err = utils.execute('sudo', 'mount', + out, err = utils.execute('mount', '-t', 'ext2,ext3', - dev_path, dir) + dev_path, dir, run_as_root=True) except exception.ProcessExecutionError as e: err = str(e) return err @@ -1056,7 +1057,7 @@ def _mounted_processing(device, key, net): disk.inject_data_into_fs(tmpdir, key, net, utils.execute) finally: - utils.execute('sudo', 'umount', dev_path) + utils.execute('umount', dev_path, run_as_root=True) else: LOG.info(_('Failed to mount filesystem (expected for ' 'non-linux instances): %s') % err) @@ -1095,6 +1096,8 @@ def _prepare_injectables(inst, networks_info): ip_v6 = info['ip6s'][0] if len(info['dns']) > 0: dns = info['dns'][0] + else: + dns = '' interface_info = {'name': 'eth%d' % ifc_num, 'address': ip_v4 and ip_v4['ip'] or '', 'netmask': ip_v4 and ip_v4['netmask'] or '', diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b3b812a48..b9cd59946 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -122,7 +122,7 @@ class VMOps(object): network_info) if resize_instance: self.resize_instance(instance, vdi_uuid) - self._spawn(instance, vm_ref) + self._start(instance, vm_ref=vm_ref) def _start(self, instance, vm_ref=None): """Power on a VM instance""" @@ -282,6 +282,7 @@ class VMOps(object): 'architecture': instance.architecture}) def _check_agent_version(): + LOG.debug(_("Querying agent version")) if instance.os_type == 'windows': # Windows will generally perform a setup process on first boot # that can take a couple of minutes and then reboot. So we @@ -292,7 +293,6 @@ class VMOps(object): else: version = self.get_agent_version(instance) if not version: - LOG.info(_('No agent version returned by instance')) return LOG.info(_('Instance agent version: %s') % version) @@ -327,6 +327,10 @@ class VMOps(object): LOG.debug(_("Setting admin password")) self.set_admin_password(instance, admin_password) + def _reset_network(): + LOG.debug(_("Resetting network")) + self.reset_network(instance, vm_ref) + # NOTE(armando): Do we really need to do this in virt? # NOTE(tr3buchet): not sure but wherever we do it, we need to call # reset_network afterwards @@ -341,7 +345,7 @@ class VMOps(object): _check_agent_version() _inject_files() _set_admin_password() - self.reset_network(instance, vm_ref) + _reset_network() return True except Exception, exc: LOG.warn(exc) @@ -597,13 +601,13 @@ class VMOps(object): transaction_id = str(uuid.uuid4()) args = {'id': transaction_id} resp = self._make_agent_call('version', instance, '', args) - if resp is None: - # No response from the agent - return - resp_dict = json.loads(resp) + if resp['returncode'] != '0': + LOG.error(_('Failed to query agent version: %(resp)r') % + locals()) + return None # Some old versions of the Windows agent have a trailing \\r\\n # (ie CRLF escaped) for some reason. Strip that off. - return resp_dict['message'].replace('\\r\\n', '') + return resp['message'].replace('\\r\\n', '') if timeout: vm_ref = self._get_vm_opaque_ref(instance) @@ -634,13 +638,10 @@ class VMOps(object): transaction_id = str(uuid.uuid4()) args = {'id': transaction_id, 'url': url, 'md5sum': md5sum} resp = self._make_agent_call('agentupdate', instance, '', args) - if resp is None: - # No response from the agent - return - resp_dict = json.loads(resp) - if resp_dict['returncode'] != '0': - raise RuntimeError(resp_dict['message']) - return resp_dict['message'] + if resp['returncode'] != '0': + LOG.error(_('Failed to update agent: %(resp)r') % locals()) + return None + return resp['message'] def set_admin_password(self, instance, new_pass): """Set the root/admin password on the VM instance. @@ -659,18 +660,13 @@ class VMOps(object): key_init_args = {'id': key_init_transaction_id, 'pub': str(dh.get_public())} resp = self._make_agent_call('key_init', instance, '', key_init_args) - if resp is None: - # No response from the agent - return - resp_dict = json.loads(resp) # Successful return code from key_init is 'D0' - if resp_dict['returncode'] != 'D0': - # There was some sort of error; the message will contain - # a description of the error. - raise RuntimeError(resp_dict['message']) + if resp['returncode'] != 'D0': + LOG.error(_('Failed to exchange keys: %(resp)r') % locals()) + return None # Some old versions of the Windows agent have a trailing \\r\\n # (ie CRLF escaped) for some reason. Strip that off. - agent_pub = int(resp_dict['message'].replace('\\r\\n', '')) + agent_pub = int(resp['message'].replace('\\r\\n', '')) dh.compute_shared(agent_pub) # Some old versions of Linux and Windows agent expect trailing \n # on password to work correctly. @@ -679,17 +675,14 @@ class VMOps(object): password_transaction_id = str(uuid.uuid4()) password_args = {'id': password_transaction_id, 'enc_pass': enc_pass} resp = self._make_agent_call('password', instance, '', password_args) - if resp is None: - # No response from the agent - return - resp_dict = json.loads(resp) # Successful return code from password is '0' - if resp_dict['returncode'] != '0': - raise RuntimeError(resp_dict['message']) + if resp['returncode'] != '0': + LOG.error(_('Failed to update password: %(resp)r') % locals()) + return None db.instance_update(nova_context.get_admin_context(), instance['id'], dict(admin_pass=new_pass)) - return resp_dict['message'] + return resp['message'] def inject_file(self, instance, path, contents): """Write a file to the VM instance. @@ -712,12 +705,10 @@ class VMOps(object): # If the agent doesn't support file injection, a NotImplementedError # will be raised with the appropriate message. resp = self._make_agent_call('inject_file', instance, '', args) - resp_dict = json.loads(resp) - if resp_dict['returncode'] != '0': - # There was some other sort of error; the message will contain - # a description of the error. - raise RuntimeError(resp_dict['message']) - return resp_dict['message'] + if resp['returncode'] != '0': + LOG.error(_('Failed to inject file: %(resp)r') % locals()) + return None + return resp['message'] def _shutdown(self, instance, vm_ref, hard=True): """Shutdown an instance.""" @@ -743,6 +734,17 @@ class VMOps(object): except self.XenAPI.Failure, exc: LOG.exception(exc) + def _find_rescue_vbd_ref(self, vm_ref, rescue_vm_ref): + """Find and return the rescue VM's vbd_ref. + + We use the second VBD here because swap is first with the root file + system coming in second.""" + vbd_ref = self._session.get_xenapi().VM.get_VBDs(vm_ref)[1] + vdi_ref = self._session.get_xenapi().VBD.get_record(vbd_ref)["VDI"] + + return VMHelper.create_vbd(self._session, rescue_vm_ref, vdi_ref, 1, + False) + def _shutdown_rescue(self, rescue_vm_ref): """Shutdown a rescue instance.""" self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm_ref) @@ -914,7 +916,7 @@ class VMOps(object): True) self._wait_with_callback(instance.id, task, callback) - def rescue(self, context, instance, callback, network_info): + def rescue(self, context, instance, _callback, network_info): """Rescue the specified instance. - shutdown the instance VM. @@ -934,15 +936,11 @@ class VMOps(object): instance._rescue = True self.spawn_rescue(context, instance, network_info) rescue_vm_ref = VMHelper.lookup(self._session, instance.name) - - vbd_ref = self._session.get_xenapi().VM.get_VBDs(vm_ref)[0] - vdi_ref = self._session.get_xenapi().VBD.get_record(vbd_ref)["VDI"] - rescue_vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref, - vdi_ref, 1, False) + rescue_vbd_ref = self._find_rescue_vbd_ref(vm_ref, rescue_vm_ref) self._session.call_xenapi("Async.VBD.plug", rescue_vbd_ref) - def unrescue(self, instance, callback): + def unrescue(self, instance, _callback): """Unrescue the specified instance. - unplug the instance VM's disk from the rescue VM. @@ -1024,11 +1022,23 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' + def host_power_action(self, host, action): + """Reboots or shuts down the host.""" + args = {"action": json.dumps(action)} + methods = {"reboot": "host_reboot", "shutdown": "host_shutdown"} + json_resp = self._call_xenhost(methods[action], args) + resp = json.loads(json_resp) + return resp["power_action"] + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" args = {"enabled": json.dumps(enabled)} - json_resp = self._call_xenhost("set_host_enabled", args) - resp = json.loads(json_resp) + xenapi_resp = self._call_xenhost("set_host_enabled", args) + try: + resp = json.loads(xenapi_resp) + except TypeError as e: + # Already logged; return the message + return xenapi_resp.details[-1] return resp["status"] def _call_xenhost(self, method, arg_dict): @@ -1044,7 +1054,7 @@ class VMOps(object): #args={"params": arg_dict}) ret = self._session.wait_for_task(task, task_id) except self.XenAPI.Failure as e: - ret = None + ret = e LOG.error(_("The call to %(method)s returned an error: %(e)s.") % locals()) return ret @@ -1159,8 +1169,19 @@ class VMOps(object): def _make_agent_call(self, method, vm, path, addl_args=None): """Abstracts out the interaction with the agent xenapi plugin.""" - return self._make_plugin_call('agent', method=method, vm=vm, + ret = self._make_plugin_call('agent', method=method, vm=vm, path=path, addl_args=addl_args) + if isinstance(ret, dict): + return ret + try: + return json.loads(ret) + except TypeError: + instance_id = vm.id + LOG.error(_('The agent call to %(method)s returned an invalid' + ' response: %(ret)r. VM id=%(instance_id)s;' + ' path=%(path)s; args=%(addl_args)r') % locals()) + return {'returncode': 'error', + 'message': 'unable to deserialize response'} def _make_plugin_call(self, plugin, method, vm, path, addl_args=None, vm_ref=None): @@ -1178,20 +1199,20 @@ class VMOps(object): ret = self._session.wait_for_task(task, instance_id) except self.XenAPI.Failure, e: ret = None - err_trace = e.details[-1] - err_msg = err_trace.splitlines()[-1] - strargs = str(args) + err_msg = e.details[-1].splitlines()[-1] if 'TIMEOUT:' in err_msg: LOG.error(_('TIMEOUT: The call to %(method)s timed out. ' - 'VM id=%(instance_id)s; args=%(strargs)s') % locals()) + 'VM id=%(instance_id)s; args=%(args)r') % locals()) + return {'returncode': 'timeout', 'message': err_msg} elif 'NOT IMPLEMENTED:' in err_msg: LOG.error(_('NOT IMPLEMENTED: The call to %(method)s is not' ' supported by the agent. VM id=%(instance_id)s;' - ' args=%(strargs)s') % locals()) - raise NotImplementedError(err_msg) + ' args=%(args)r') % locals()) + return {'returncode': 'notimplemented', 'message': err_msg} else: LOG.error(_('The call to %(method)s returned an error: %(e)s. ' - 'VM id=%(instance_id)s; args=%(strargs)s') % locals()) + 'VM id=%(instance_id)s; args=%(args)r') % locals()) + return {'returncode': 'error', 'message': err_msg} return ret def add_to_xenstore(self, vm, path, key, value): @@ -1313,12 +1334,6 @@ class VMOps(object): ######################################################################## -def _runproc(cmd): - pipe = subprocess.PIPE - return subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe, - stderr=pipe, close_fds=True) - - class SimpleDH(object): """ This class wraps all the functionality needed to implement @@ -1375,22 +1390,18 @@ class SimpleDH(object): mpi = M2Crypto.m2.bn_to_mpi(bn) return mpi - def _run_ssl(self, text, extra_args=None): - if not extra_args: - extra_args = '' - cmd = 'enc -aes-128-cbc -A -a -pass pass:%s -nosalt %s' % ( - self._shared, extra_args) - proc = _runproc('openssl %s' % cmd) - proc.stdin.write(text) - proc.stdin.close() - proc.wait() - err = proc.stderr.read() + def _run_ssl(self, text, decrypt=False): + cmd = ['openssl', 'aes-128-cbc', '-A', '-a', '-pass', + 'pass:%s' % self._shared, '-nosalt'] + if decrypt: + cmd.append('-d') + out, err = utils.execute(*cmd, process_input=text) if err: raise RuntimeError(_('OpenSSL error: %s') % err) - return proc.stdout.read() + return out def encrypt(self, text): return self._run_ssl(text).strip('\n') def decrypt(self, text): - return self._run_ssl(text, '-d') + return self._run_ssl(text, decrypt=True) diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 7821a4f7e..5d5eb824f 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -252,10 +252,10 @@ def _get_target(volume_id): volume_id) result = (None, None) try: - (r, _e) = utils.execute('sudo', 'iscsiadm', + (r, _e) = utils.execute('iscsiadm', '-m', 'discovery', '-t', 'sendtargets', - '-p', volume_ref['host']) + '-p', volume_ref['host'], run_as_root=True) except exception.ProcessExecutionError, exc: LOG.exception(exc) else: diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 39afbd650..76b6c57fc 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -184,14 +184,14 @@ class XenAPIConnection(driver.ComputeDriver): def list_instances_detail(self): return self._vmops.list_instances_detail() - def spawn(self, context, instance, network_info, - block_device_mapping=None): + def spawn(self, context, instance, + network_info=None, block_device_info=None): """Create VM instance""" self._vmops.spawn(context, instance, network_info) def revert_migration(self, instance): """Reverts a resize, powering back on the instance""" - self._vmops.revert_resize(instance) + self._vmops.revert_migration(instance) def finish_migration(self, context, instance, disk_info, network_info, resize_instance=False): @@ -217,7 +217,7 @@ class XenAPIConnection(driver.ComputeDriver): """ self._vmops.inject_file(instance, b64_path, b64_contents) - def destroy(self, instance, network_info): + def destroy(self, instance, network_info, cleanup=True): """Destroy VM instance""" self._vmops.destroy(instance, network_info) @@ -242,13 +242,13 @@ class XenAPIConnection(driver.ComputeDriver): """resume the specified instance""" self._vmops.resume(instance, callback) - def rescue(self, context, instance, callback, network_info): + def rescue(self, context, instance, _callback, network_info): """Rescue the specified instance""" - self._vmops.rescue(context, instance, callback, network_info) + self._vmops.rescue(context, instance, _callback, network_info) - def unrescue(self, instance, callback, network_info): + def unrescue(self, instance, _callback, network_info): """Unrescue the specified instance""" - self._vmops.unrescue(instance, callback) + self._vmops.unrescue(instance, _callback) def poll_rescued_instances(self, timeout): """Poll for rescued instances""" @@ -332,6 +332,19 @@ class XenAPIConnection(driver.ComputeDriver): True, run the update first.""" return self.HostState.get_host_stats(refresh=refresh) + def host_power_action(self, host, action): + """The only valid values for 'action' on XenServer are 'reboot' or + 'shutdown', even though the API also accepts 'startup'. As this is + not technically possible on XenServer, since the host is the same + physical machine as the hypervisor, if this is requested, we need to + raise an exception. + """ + if action in ("reboot", "shutdown"): + return self._vmops.host_power_action(host, action) + else: + msg = _("Host startup on XenServer is not supported.") + raise NotImplementedError(msg) + def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" return self._vmops.set_host_enabled(host, enabled) @@ -394,11 +407,10 @@ class XenAPISession(object): try: name = self._session.xenapi.task.get_name_label(task) status = self._session.xenapi.task.get_status(task) + # Ensure action is never > 255 + action = dict(action=name[:255], error=None) if id: - action = dict( - instance_id=int(id), - action=name[0:255], # Ensure action is never > 255 - error=None) + action["instance_id"] = int(id) if status == "pending": return elif status == "success": @@ -441,7 +453,7 @@ class XenAPISession(object): params = None try: params = eval(exc.details[3]) - except: + except Exception: raise exc raise self.XenAPI.Failure(params) else: |
