Merge "VMware Compute Driver OVF Support"
This commit is contained in:
commit
ce09c50c92
@ -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):
|
||||
|
@ -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})
|
||||
|
@ -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_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).
|
||||
:vmwareapi_api_retry_count: The API retry count in case of failure such as
|
||||
network failures (socket errors etc.)
|
||||
(default: 10).
|
||||
|
||||
: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: 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)
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
@ -45,7 +45,7 @@ def ensure_vlan_bridge(self, session, network):
|
||||
# Check if the vlan_interface physical network adapter exists on the
|
||||
# host.
|
||||
if not network_util.check_if_vlan_interface_exists(session,
|
||||
vlan_interface):
|
||||
vlan_interface):
|
||||
raise exception.NetworkAdapterNotFound(adapter=vlan_interface)
|
||||
|
||||
# Get the vSwitch associated with the Physical Adapter
|
||||
|
@ -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
@ -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"),
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user