Merge "VMware Compute Driver OVF Support"

This commit is contained in:
Jenkins 2013-01-28 20:13:43 +00:00 committed by Gerrit Code Review
commit ce09c50c92
9 changed files with 829 additions and 181 deletions

View File

@ -317,7 +317,15 @@ class InstanceSuspendFailure(Invalid):
class InstanceResumeFailure(Invalid): class InstanceResumeFailure(Invalid):
message = _("Failed to resume server") + ": %(reason)s." message = _("Failed to resume instance: %(reason)s.")
class InstancePowerOnFailure(Invalid):
message = _("Failed to power on instance: %(reason)s.")
class InstancePowerOffFailure(Invalid):
message = _("Failed to power off instance: %(reason)s.")
class InstanceRebootFailure(Invalid): class InstanceRebootFailure(Invalid):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc. # Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC. # Copyright 2011 OpenStack LLC.
# #
@ -41,7 +42,9 @@ class VMwareAPIVMTestCase(test.TestCase):
self.context = context.RequestContext('fake', 'fake', is_admin=False) self.context = context.RequestContext('fake', 'fake', is_admin=False)
self.flags(vmwareapi_host_ip='test_url', self.flags(vmwareapi_host_ip='test_url',
vmwareapi_host_username='test_username', vmwareapi_host_username='test_username',
vmwareapi_host_password='test_pass') vmwareapi_host_password='test_pass',
vnc_enabled=False,
use_linked_clone=False)
self.user_id = 'fake' self.user_id = 'fake'
self.project_id = 'fake' self.project_id = 'fake'
self.context = context.RequestContext(self.user_id, self.project_id) self.context = context.RequestContext(self.user_id, self.project_id)
@ -211,7 +214,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING) self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance) self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1}) info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.PAUSED) self._check_vm_info(info, power_state.SUSPENDED)
self.assertRaises(exception.InstanceRebootFailure, self.conn.reboot, self.assertRaises(exception.InstanceRebootFailure, self.conn.reboot,
self.instance, self.network_info, 'SOFT') self.instance, self.network_info, 'SOFT')
@ -221,7 +224,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING) self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance) self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1}) info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.PAUSED) self._check_vm_info(info, power_state.SUSPENDED)
def test_suspend_non_existent(self): def test_suspend_non_existent(self):
self._create_instance_in_the_db() self._create_instance_in_the_db()
@ -234,7 +237,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING) self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance) self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1}) info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.PAUSED) self._check_vm_info(info, power_state.SUSPENDED)
self.conn.resume(self.instance, self.network_info) self.conn.resume(self.instance, self.network_info)
info = self.conn.get_info({'name': 1}) info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING) self._check_vm_info(info, power_state.RUNNING)
@ -251,6 +254,43 @@ class VMwareAPIVMTestCase(test.TestCase):
self.assertRaises(exception.InstanceResumeFailure, self.conn.resume, self.assertRaises(exception.InstanceResumeFailure, self.conn.resume,
self.instance, self.network_info) self.instance, self.network_info)
def test_power_on(self):
self._create_vm()
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING)
self.conn.power_off(self.instance)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.SHUTDOWN)
self.conn.power_on(self.instance)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING)
def test_power_on_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(exception.InstanceNotFound, self.conn.power_on,
self.instance)
def test_power_off(self):
self._create_vm()
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING)
self.conn.power_off(self.instance)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.SHUTDOWN)
def test_power_off_non_existent(self):
self._create_instance_in_the_db()
self.assertRaises(exception.InstanceNotFound, self.conn.power_off,
self.instance)
def test_power_off_suspended(self):
self._create_vm()
self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.SUSPENDED)
self.assertRaises(exception.InstancePowerOffFailure,
self.conn.power_off, self.instance)
def test_get_info(self): def test_get_info(self):
self._create_vm() self._create_vm()
info = self.conn.get_info({'name': 1}) info = self.conn.get_info({'name': 1})

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc. # Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC. # Copyright 2011 OpenStack LLC.
# #
@ -20,16 +21,19 @@ A connection to the VMware ESX platform.
**Related Flags** **Related Flags**
:vmwareapi_host_ip: IPAddress of VMware ESX server. :vmwareapi_host_ip: IP address of VMware ESX server.
:vmwareapi_host_username: Username for connection to VMware ESX Server. :vmwareapi_host_username: Username for connection to VMware ESX Server.
:vmwareapi_host_password: Password for connection to VMware ESX Server. :vmwareapi_host_password: Password for connection to VMware ESX Server.
:vmwareapi_task_poll_interval: The interval (seconds) used for polling of :vmwareapi_task_poll_interval: The interval (seconds) used for polling of
remote tasks remote tasks
(default: 1.0). (default: 5.0).
:vmwareapi_api_retry_count: The API retry count in case of failure such as :vmwareapi_api_retry_count: The API retry count in case of failure such as
network failures (socket errors etc.) network failures (socket errors etc.)
(default: 10). (default: 10).
:vnc_port: VNC starting port (default: 5900)
:vnc_port_total: Total number of VNC ports (default: 10000)
:vnc_password: VNC password
:use_linked_clone: Whether to use linked clone (default: True)
""" """
import time import time
@ -78,6 +82,18 @@ vmwareapi_opts = [
'socket error, etc. ' 'socket error, etc. '
'Used only if compute_driver is ' 'Used only if compute_driver is '
'vmwareapi.VMwareESXDriver.'), 'vmwareapi.VMwareESXDriver.'),
cfg.IntOpt('vnc_port',
default=5900,
help='VNC starting port'),
cfg.IntOpt('vnc_port_total',
default=10000,
help='Total number of VNC ports'),
cfg.StrOpt('vnc_password',
default=None,
help='VNC password'),
cfg.BoolOpt('use_linked_clone',
default=True,
help='Whether to use linked clone'),
] ]
CONF = cfg.CONF CONF = cfg.CONF
@ -116,7 +132,8 @@ class VMwareESXDriver(driver.ComputeDriver):
host_username, host_password, host_username, host_password,
api_retry_count, scheme=scheme) api_retry_count, scheme=scheme)
self._volumeops = volumeops.VMwareVolumeOps(self._session) self._volumeops = volumeops.VMwareVolumeOps(self._session)
self._vmops = vmops.VMwareVMOps(self._session) self._vmops = vmops.VMwareVMOps(self._session, self.virtapi,
self._volumeops)
self._host = host.Host(self._session) self._host = host.Host(self._session)
self._host_state = None self._host_state = None
@ -142,7 +159,8 @@ class VMwareESXDriver(driver.ComputeDriver):
def spawn(self, context, instance, image_meta, injected_files, def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None): admin_password, network_info=None, block_device_info=None):
"""Create VM instance.""" """Create VM instance."""
self._vmops.spawn(context, instance, image_meta, network_info) self._vmops.spawn(context, instance, image_meta, network_info,
block_device_info)
def snapshot(self, context, instance, name, update_task_state): def snapshot(self, context, instance, name, update_task_state):
"""Create snapshot from a running VM instance.""" """Create snapshot from a running VM instance."""
@ -174,6 +192,61 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Resume the suspended VM instance.""" """Resume the suspended VM instance."""
self._vmops.resume(instance) self._vmops.resume(instance)
def rescue(self, context, instance, network_info, image_meta,
rescue_password):
"""Rescue the specified instance."""
self._vmops.rescue(context, instance, network_info, image_meta)
def unrescue(self, instance, network_info):
"""Unrescue the specified instance."""
self._vmops.unrescue(instance)
def power_off(self, instance):
"""Power off the specified instance."""
self._vmops.power_off(instance)
def power_on(self, instance):
"""Power on the specified instance."""
self._vmops.power_on(instance)
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info,
block_device_info=None):
"""
Transfers the disk of a running instance in multiple phases, turning
off the instance before the end.
"""
return self._vmops.migrate_disk_and_power_off(context, instance,
dest, instance_type)
def confirm_migration(self, migration, instance, network_info):
"""Confirms a resize, destroying the source VM."""
self._vmops.confirm_migration(migration, instance, network_info)
def finish_revert_migration(self, instance, network_info,
block_device_info=None):
"""Finish reverting a resize, powering back on the instance."""
self._vmops.finish_revert_migration(instance)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False,
block_device_info=None):
"""Completes a resize, turning on the migrated instance."""
self._vmops.finish_migration(context, migration, instance, disk_info,
network_info, image_meta, resize_instance)
def live_migration(self, context, instance_ref, dest,
post_method, recover_method, block_migration=False,
migrate_data=None):
"""Live migration of an instance to another host."""
self._vmops.live_migration(context, instance_ref, dest,
post_method, recover_method,
block_migration)
def poll_rebooting_instances(self, timeout, instances):
"""Poll for rebooting instances."""
self._vmops.poll_rebooting_instances(timeout, instances)
def get_info(self, instance): def get_info(self, instance):
"""Return info about the VM instance.""" """Return info about the VM instance."""
return self._vmops.get_info(instance) return self._vmops.get_info(instance)
@ -186,6 +259,10 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Return snapshot of console.""" """Return snapshot of console."""
return self._vmops.get_console_output(instance) return self._vmops.get_console_output(instance)
def get_vnc_console(self, instance):
"""Return link to instance's VNC console."""
return self._vmops.get_vnc_console(instance)
def get_volume_connector(self, instance): def get_volume_connector(self, instance):
"""Return volume connector information.""" """Return volume connector information."""
return self._volumeops.get_volume_connector(instance) return self._volumeops.get_volume_connector(instance)

View File

@ -140,7 +140,7 @@ class VMwareHTTPWriteFile(VMwareHTTPFile):
self.conn.getresponse() self.conn.getresponse()
except Exception, excep: except Exception, excep:
LOG.debug(_("Exception during HTTP connection close in " LOG.debug(_("Exception during HTTP connection close in "
"VMwareHTTpWrite. Exception is %s") % excep) "VMwareHTTPWrite. Exception is %s") % excep)
super(VMwareHTTPWriteFile, self).close() super(VMwareHTTPWriteFile, self).close()

View File

@ -45,7 +45,7 @@ def ensure_vlan_bridge(self, session, network):
# Check if the vlan_interface physical network adapter exists on the # Check if the vlan_interface physical network adapter exists on the
# host. # host.
if not network_util.check_if_vlan_interface_exists(session, if not network_util.check_if_vlan_interface_exists(session,
vlan_interface): vlan_interface):
raise exception.NetworkAdapterNotFound(adapter=vlan_interface) raise exception.NetworkAdapterNotFound(adapter=vlan_interface)
# Get the vSwitch associated with the Physical Adapter # Get the vSwitch associated with the Physical Adapter

View File

@ -361,6 +361,27 @@ def delete_virtual_disk_spec(client_factory, device):
return virtual_device_config return virtual_device_config
def clone_vm_spec(client_factory, location,
power_on=False, snapshot=None, template=False):
"""Builds the VM clone spec."""
clone_spec = client_factory.create('ns0:VirtualMachineCloneSpec')
clone_spec.location = location
clone_spec.powerOn = power_on
clone_spec.snapshot = snapshot
clone_spec.template = template
return clone_spec
def relocate_vm_spec(client_factory, datastore=None, host=None,
disk_move_type="moveAllDiskBackingsAndAllowSharing"):
"""Builds the VM relocation spec."""
rel_spec = client_factory.create('ns0:VirtualMachineRelocateSpec')
rel_spec.datastore = datastore
rel_spec.diskMoveType = disk_move_type
rel_spec.host = host
return rel_spec
def get_dummy_vm_create_spec(client_factory, name, data_store_name): def get_dummy_vm_create_spec(client_factory, name, data_store_name):
"""Builds the dummy VM create spec.""" """Builds the dummy VM create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
@ -424,6 +445,31 @@ def get_add_vswitch_port_group_spec(client_factory, vswitch_name,
return vswitch_port_group_spec return vswitch_port_group_spec
def get_vnc_config_spec(client_factory, port, password):
"""Builds the vnc config spec."""
virtual_machine_config_spec = client_factory.create(
'ns0:VirtualMachineConfigSpec')
opt_enabled = client_factory.create('ns0:OptionValue')
opt_enabled.key = "RemoteDisplay.vnc.enabled"
opt_enabled.value = "true"
opt_port = client_factory.create('ns0:OptionValue')
opt_port.key = "RemoteDisplay.vnc.port"
opt_port.value = port
opt_pass = client_factory.create('ns0:OptionValue')
opt_pass.key = "RemoteDisplay.vnc.password"
opt_pass.value = password
virtual_machine_config_spec.extraConfig = [opt_enabled, opt_port, opt_pass]
return virtual_machine_config_spec
def search_datastore_spec(client_factory, file_name):
"""Builds the datastore search spec."""
search_spec = client_factory.create('ns0:HostDatastoreBrowserSearchSpec')
search_spec.matchPattern = [file_name]
return search_spec
def get_vm_ref_from_name(session, vm_name): def get_vm_ref_from_name(session, vm_name):
"""Get reference to the VM with the name specified.""" """Get reference to the VM with the name specified."""
vms = session._call_method(vim_util, "get_objects", vms = session._call_method(vim_util, "get_objects",

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc. # Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC. # Copyright 2011 OpenStack LLC.
# #
@ -17,7 +18,6 @@
""" """
Utility functions for Image transfer. Utility functions for Image transfer.
""" """
import StringIO
from nova import exception from nova import exception
from nova.image import glance from nova.image import glance
@ -56,7 +56,7 @@ def start_transfer(context, read_file_handle, data_size,
write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle) write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle)
# In case of VMware - Glance transfer, we relinquish VMware HTTP file read # In case of VMware - Glance transfer, we relinquish VMware HTTP file read
# handle to Glance Client instance, but to be sure of the transfer we need # handle to Glance Client instance, but to be sure of the transfer we need
# to be sure of the status of the image on glnace changing to active. # to be sure of the status of the image on glance changing to active.
# The GlanceWriteThread handles the same for us. # The GlanceWriteThread handles the same for us.
elif image_service and image_id: elif image_service and image_id:
write_thread = io_util.GlanceWriteThread(context, thread_safe_pipe, write_thread = io_util.GlanceWriteThread(context, thread_safe_pipe,
@ -93,9 +93,8 @@ def fetch_image(context, image, instance, **kwargs):
(image_service, image_id) = glance.get_remote_image_service(context, image) (image_service, image_id) = glance.get_remote_image_service(context, image)
metadata = image_service.show(context, image_id) metadata = image_service.show(context, image_id)
file_size = int(metadata['size']) file_size = int(metadata['size'])
f = StringIO.StringIO() read_iter = image_service.download(context, image_id)
image_service.download(context, image_id, f) read_file_handle = read_write_util.GlanceFileRead(read_iter)
read_file_handle = read_write_util.GlanceFileRead(f)
write_file_handle = read_write_util.VMwareHTTPWriteFile( write_file_handle = read_write_util.VMwareHTTPWriteFile(
kwargs.get("host"), kwargs.get("host"),
kwargs.get("data_center_name"), kwargs.get("data_center_name"),
@ -122,10 +121,9 @@ def upload_image(context, image, instance, **kwargs):
file_size = read_file_handle.get_size() file_size = read_file_handle.get_size()
(image_service, image_id) = glance.get_remote_image_service(context, image) (image_service, image_id) = glance.get_remote_image_service(context, image)
# The properties and other fields that we need to set for the image. # The properties and other fields that we need to set for the image.
image_metadata = {"is_public": True, image_metadata = {"disk_format": "vmdk",
"disk_format": "vmdk",
"container_format": "bare", "container_format": "bare",
"type": "vmdk", "size": file_size,
"properties": {"vmware_adaptertype": "properties": {"vmware_adaptertype":
kwargs.get("adapter_type"), kwargs.get("adapter_type"),
"vmware_ostype": kwargs.get("os_type"), "vmware_ostype": kwargs.get("os_type"),

View File

@ -18,7 +18,6 @@
Management class for Storage-related functions (attach, detach, etc). Management class for Storage-related functions (attach, detach, etc).
""" """
from nova import context
from nova import exception from nova import exception
from nova.openstack.common import cfg from nova.openstack.common import cfg
from nova.openstack.common import log as logging from nova.openstack.common import log as logging
@ -110,7 +109,8 @@ class VMwareVolumeOps(object):
iqn = volume_util.get_host_iqn(self._session) iqn = volume_util.get_host_iqn(self._session)
return { return {
'ip': CONF.vmwareapi_host_ip, 'ip': CONF.vmwareapi_host_ip,
'initiator': iqn 'initiator': iqn,
'host': CONF.vmwareapi_host_ip
} }
def attach_volume(self, connection_info, instance, mountpoint): def attach_volume(self, connection_info, instance, mountpoint):