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):
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):

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@ -41,7 +42,9 @@ class VMwareAPIVMTestCase(test.TestCase):
self.context = context.RequestContext('fake', 'fake', is_admin=False)
self.flags(vmwareapi_host_ip='test_url',
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.project_id = 'fake'
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.conn.suspend(self.instance)
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.instance, self.network_info, 'SOFT')
@ -221,7 +224,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance)
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):
self._create_instance_in_the_db()
@ -234,7 +237,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance)
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)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING)
@ -251,6 +254,43 @@ class VMwareAPIVMTestCase(test.TestCase):
self.assertRaises(exception.InstanceResumeFailure, self.conn.resume,
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):
self._create_vm()
info = self.conn.get_info({'name': 1})

View File

@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@ -20,16 +21,19 @@ A connection to the VMware ESX platform.
**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_password: Password for connection to VMware ESX Server.
:vmwareapi_task_poll_interval: The interval (seconds) used for polling of
remote tasks
(default: 1.0).
(default: 5.0).
:vmwareapi_api_retry_count: The API retry count in case of failure such as
network failures (socket errors etc.)
(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
@ -78,6 +82,18 @@ vmwareapi_opts = [
'socket error, etc. '
'Used only if compute_driver is '
'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
@ -116,7 +132,8 @@ class VMwareESXDriver(driver.ComputeDriver):
host_username, host_password,
api_retry_count, scheme=scheme)
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_state = None
@ -142,7 +159,8 @@ class VMwareESXDriver(driver.ComputeDriver):
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
"""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):
"""Create snapshot from a running VM instance."""
@ -174,6 +192,61 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Resume the suspended VM 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):
"""Return info about the VM instance."""
return self._vmops.get_info(instance)
@ -186,6 +259,10 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Return snapshot of console."""
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):
"""Return volume connector information."""
return self._volumeops.get_volume_connector(instance)

View File

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

View File

@ -361,6 +361,27 @@ def delete_virtual_disk_spec(client_factory, device):
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):
"""Builds the dummy VM create spec."""
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
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):
"""Get reference to the VM with the name specified."""
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
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@ -17,7 +18,6 @@
"""
Utility functions for Image transfer.
"""
import StringIO
from nova import exception
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)
# 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
# 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.
elif image_service and image_id:
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)
metadata = image_service.show(context, image_id)
file_size = int(metadata['size'])
f = StringIO.StringIO()
image_service.download(context, image_id, f)
read_file_handle = read_write_util.GlanceFileRead(f)
read_iter = image_service.download(context, image_id)
read_file_handle = read_write_util.GlanceFileRead(read_iter)
write_file_handle = read_write_util.VMwareHTTPWriteFile(
kwargs.get("host"),
kwargs.get("data_center_name"),
@ -122,10 +121,9 @@ def upload_image(context, image, instance, **kwargs):
file_size = read_file_handle.get_size()
(image_service, image_id) = glance.get_remote_image_service(context, image)
# The properties and other fields that we need to set for the image.
image_metadata = {"is_public": True,
"disk_format": "vmdk",
image_metadata = {"disk_format": "vmdk",
"container_format": "bare",
"type": "vmdk",
"size": file_size,
"properties": {"vmware_adaptertype":
kwargs.get("adapter_type"),
"vmware_ostype": kwargs.get("os_type"),

View File

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