Merge "VMware image clone strategy settings and overrides"
This commit is contained in:
commit
302e686be9
@ -82,6 +82,7 @@ def stub_out_db_instance_api(stubs):
|
|||||||
'mac_addresses': [{'address': values['mac_address']}],
|
'mac_addresses': [{'address': values['mac_address']}],
|
||||||
'root_gb': type_data['root_gb'],
|
'root_gb': type_data['root_gb'],
|
||||||
'node': values['node'],
|
'node': values['node'],
|
||||||
|
'metadata': []
|
||||||
}
|
}
|
||||||
|
|
||||||
return base_options
|
return base_options
|
||||||
|
3
nova/tests/virt/vmwareapi/test_configdrive.py
Normal file → Executable file
3
nova/tests/virt/vmwareapi/test_configdrive.py
Normal file → Executable file
@ -75,7 +75,8 @@ class ConfigDriveTestCase(test.TestCase):
|
|||||||
'reservation_id': 'r-3t8muvr0',
|
'reservation_id': 'r-3t8muvr0',
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'uuid': 'fake-uuid',
|
'uuid': 'fake-uuid',
|
||||||
'node': self.node_name}
|
'node': self.node_name,
|
||||||
|
'metadata': []}
|
||||||
|
|
||||||
class FakeInstanceMetadata(object):
|
class FakeInstanceMetadata(object):
|
||||||
def __init__(self, instance, content=None, extra_md=None):
|
def __init__(self, instance, content=None, extra_md=None):
|
||||||
|
27
nova/tests/virt/vmwareapi/test_vmwareapi_vmops.py
Normal file → Executable file
27
nova/tests/virt/vmwareapi/test_vmwareapi_vmops.py
Normal file → Executable file
@ -76,3 +76,30 @@ class VMwareVMOpsTestCase(test.TestCase):
|
|||||||
self.flags(network_api_class='nova.network.quantumv2.api.API')
|
self.flags(network_api_class='nova.network.quantumv2.api.API')
|
||||||
ops = vmops.VMwareVMOps(None, None, None)
|
ops = vmops.VMwareVMOps(None, None, None)
|
||||||
self.assertTrue(ops._is_neutron)
|
self.assertTrue(ops._is_neutron)
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_nf(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone(None, False)
|
||||||
|
self.assertFalse(value, "No overrides present but still overridden!")
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_nt(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone(None, True)
|
||||||
|
self.assertTrue(value, "No overrides present but still overridden!")
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_ny(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone(None, "yes")
|
||||||
|
self.assertTrue(value, "No overrides present but still overridden!")
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_ft(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone(False, True)
|
||||||
|
self.assertFalse(value,
|
||||||
|
"image level metadata failed to override global")
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_nt(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone("no", True)
|
||||||
|
self.assertFalse(value,
|
||||||
|
"image level metadata failed to override global")
|
||||||
|
|
||||||
|
def test_use_linked_clone_override_yf(self):
|
||||||
|
value = vmops.VMwareVMOps.decide_linked_clone("yes", False)
|
||||||
|
self.assertTrue(value,
|
||||||
|
"image level metadata failed to override global")
|
||||||
|
8
nova/utils.py
Normal file → Executable file
8
nova/utils.py
Normal file → Executable file
@ -50,6 +50,7 @@ from nova.openstack.common import lockutils
|
|||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.openstack.common import processutils
|
from nova.openstack.common import processutils
|
||||||
from nova.openstack.common.rpc import common as rpc_common
|
from nova.openstack.common.rpc import common as rpc_common
|
||||||
|
from nova.openstack.common import strutils
|
||||||
from nova.openstack.common import timeutils
|
from nova.openstack.common import timeutils
|
||||||
|
|
||||||
notify_decorator = 'nova.notifications.notify_decorator'
|
notify_decorator = 'nova.notifications.notify_decorator'
|
||||||
@ -1285,3 +1286,10 @@ def get_image_from_system_metadata(system_meta):
|
|||||||
image_meta['properties'] = properties
|
image_meta['properties'] = properties
|
||||||
|
|
||||||
return image_meta
|
return image_meta
|
||||||
|
|
||||||
|
|
||||||
|
def get_boolean(value):
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return strutils.bool_from_string(value)
|
||||||
|
@ -159,6 +159,10 @@ class VMwareESXDriver(driver.ComputeDriver):
|
|||||||
self._host = host.Host(self._session)
|
self._host = host.Host(self._session)
|
||||||
self._host_state = None
|
self._host_state = None
|
||||||
|
|
||||||
|
#TODO(hartsocks): back-off into a configuration test module.
|
||||||
|
if CONF.vmware.use_linked_clone is None:
|
||||||
|
raise error_util.UseLinkedCloneConfigurationFault()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host_state(self):
|
def host_state(self):
|
||||||
if not self._host_state:
|
if not self._host_state:
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"""
|
"""
|
||||||
Exception classes and SOAP response error checking module.
|
Exception classes and SOAP response error checking module.
|
||||||
"""
|
"""
|
||||||
|
from nova import exception
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common.gettextutils import _
|
||||||
|
|
||||||
@ -96,3 +97,25 @@ class FaultCheckers(object):
|
|||||||
raise VimFaultException(fault_list, Exception(_("Error(s) %s "
|
raise VimFaultException(fault_list, Exception(_("Error(s) %s "
|
||||||
"occurred in the call to RetrievePropertiesEx") %
|
"occurred in the call to RetrievePropertiesEx") %
|
||||||
exc_msg_list))
|
exc_msg_list))
|
||||||
|
|
||||||
|
|
||||||
|
class VMwareDriverException(exception.NovaException):
|
||||||
|
"""Base class for all exceptions raised by the VMware Driver.
|
||||||
|
|
||||||
|
All exceptions raised by the VMwareAPI drivers should raise
|
||||||
|
an exception descended from this class as a root. This will
|
||||||
|
allow the driver to potentially trap problems related to its
|
||||||
|
own internal configuration before halting the nova-compute
|
||||||
|
node.
|
||||||
|
"""
|
||||||
|
msg_fmt = _("VMware Driver fault.")
|
||||||
|
|
||||||
|
|
||||||
|
class VMwareDriverConfigurationException(VMwareDriverException):
|
||||||
|
"""Base class for all configuration exceptions.
|
||||||
|
"""
|
||||||
|
msg_fmt = _("VMware Driver configuration fault.")
|
||||||
|
|
||||||
|
|
||||||
|
class UseLinkedCloneConfigurationFault(VMwareDriverConfigurationException):
|
||||||
|
msg_fmt = _("No default value for use_linked_clone found.")
|
||||||
|
70
nova/virt/vmwareapi/vmops.py
Normal file → Executable file
70
nova/virt/vmwareapi/vmops.py
Normal file → Executable file
@ -73,6 +73,7 @@ VMWARE_POWER_STATES = {
|
|||||||
'suspended': power_state.SUSPENDED}
|
'suspended': power_state.SUSPENDED}
|
||||||
VMWARE_PREFIX = 'vmware'
|
VMWARE_PREFIX = 'vmware'
|
||||||
|
|
||||||
|
VMWARE_LINKED_CLONE = 'vmware_linked_clone'
|
||||||
|
|
||||||
RESIZE_TOTAL_STEPS = 4
|
RESIZE_TOTAL_STEPS = 4
|
||||||
|
|
||||||
@ -156,6 +157,10 @@ class VMwareVMOps(object):
|
|||||||
data_store_ref = ds[0]
|
data_store_ref = ds[0]
|
||||||
data_store_name = ds[1]
|
data_store_name = ds[1]
|
||||||
|
|
||||||
|
#TODO(hartsocks): this pattern is confusing, reimplement as methods
|
||||||
|
# The use of nested functions in this file makes for a confusing and
|
||||||
|
# hard to maintain file. At some future date, refactor this method to
|
||||||
|
# be a full-fledged method. This will also make unit testing easier.
|
||||||
def _get_image_properties():
|
def _get_image_properties():
|
||||||
"""
|
"""
|
||||||
Get the Size of the flat vmdk file that is there on the storage
|
Get the Size of the flat vmdk file that is there on the storage
|
||||||
@ -173,11 +178,18 @@ class VMwareVMOps(object):
|
|||||||
"preallocated")
|
"preallocated")
|
||||||
# Get the network card type from the image properties.
|
# Get the network card type from the image properties.
|
||||||
vif_model = image_properties.get("hw_vif_model", "VirtualE1000")
|
vif_model = image_properties.get("hw_vif_model", "VirtualE1000")
|
||||||
|
|
||||||
|
# Fetch the image_linked_clone data here. It is retrieved
|
||||||
|
# with the above network based API call. To retrieve it
|
||||||
|
# later will necessitate additional network calls using the
|
||||||
|
# identical method. Consider this a cache.
|
||||||
|
image_linked_clone = image_properties.get(VMWARE_LINKED_CLONE)
|
||||||
|
|
||||||
return (vmdk_file_size_in_kb, os_type, adapter_type, disk_type,
|
return (vmdk_file_size_in_kb, os_type, adapter_type, disk_type,
|
||||||
vif_model)
|
vif_model, image_linked_clone)
|
||||||
|
|
||||||
(vmdk_file_size_in_kb, os_type, adapter_type,
|
(vmdk_file_size_in_kb, os_type, adapter_type,
|
||||||
disk_type, vif_model) = _get_image_properties()
|
disk_type, vif_model, image_linked_clone) = _get_image_properties()
|
||||||
|
|
||||||
vm_folder_ref = self._get_vmfolder_ref()
|
vm_folder_ref = self._get_vmfolder_ref()
|
||||||
node_mo_id = vm_util.get_mo_id_from_instance(instance)
|
node_mo_id = vm_util.get_mo_id_from_instance(instance)
|
||||||
@ -362,7 +374,13 @@ class VMwareVMOps(object):
|
|||||||
self._default_root_device, block_device_info)
|
self._default_root_device, block_device_info)
|
||||||
|
|
||||||
if not ebs_root:
|
if not ebs_root:
|
||||||
linked_clone = CONF.vmware.use_linked_clone
|
# this logic allows for instances or images to decide
|
||||||
|
# for themselves which strategy is best for them.
|
||||||
|
|
||||||
|
linked_clone = VMwareVMOps.decide_linked_clone(
|
||||||
|
image_linked_clone,
|
||||||
|
CONF.vmware.use_linked_clone
|
||||||
|
)
|
||||||
if linked_clone:
|
if linked_clone:
|
||||||
upload_folder = self._instance_path_base
|
upload_folder = self._instance_path_base
|
||||||
upload_name = instance['image_ref']
|
upload_name = instance['image_ref']
|
||||||
@ -516,6 +534,52 @@ class VMwareVMOps(object):
|
|||||||
"cdrom %(file_path)s"),
|
"cdrom %(file_path)s"),
|
||||||
{'instance_name': instance_name, 'file_path': file_path})
|
{'instance_name': instance_name, 'file_path': file_path})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decide_linked_clone(image_linked_clone, global_linked_clone):
|
||||||
|
"""Explicit decision logic: whether to use linked clone on a vmdk.
|
||||||
|
|
||||||
|
This is *override* logic not boolean logic.
|
||||||
|
|
||||||
|
1. let the image over-ride if set at all
|
||||||
|
2. default to the global setting
|
||||||
|
|
||||||
|
In math terms, I need to allow:
|
||||||
|
glance image to override global config.
|
||||||
|
|
||||||
|
That is g vs c. "g" for glance. "c" for Config.
|
||||||
|
|
||||||
|
So, I need g=True vs c=False to be True.
|
||||||
|
And, I need g=False vs c=True to be False.
|
||||||
|
And, I need g=None vs c=True to be True.
|
||||||
|
|
||||||
|
Some images maybe independently best tuned for use_linked_clone=True
|
||||||
|
saving datastorage space. Alternatively a whole OpenStack install may
|
||||||
|
be tuned to performance use_linked_clone=False but a single image
|
||||||
|
in this environment may be best configured to save storage space and
|
||||||
|
set use_linked_clone=True only for itself.
|
||||||
|
|
||||||
|
The point is: let each layer of control override the layer beneath it.
|
||||||
|
|
||||||
|
rationale:
|
||||||
|
For technical discussion on the clone strategies and their trade-offs
|
||||||
|
see: https://www.vmware.com/support/ws5/doc/ws_clone_typeofclone.html
|
||||||
|
|
||||||
|
:param image_linked_clone: boolean or string or None
|
||||||
|
:param global_linked_clone: boolean or string or None
|
||||||
|
:return: Boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
value = None
|
||||||
|
|
||||||
|
# Consider the values in order of override.
|
||||||
|
if image_linked_clone is not None:
|
||||||
|
value = image_linked_clone
|
||||||
|
else:
|
||||||
|
# this will never be not-set by this point.
|
||||||
|
value = global_linked_clone
|
||||||
|
|
||||||
|
return utils.get_boolean(value)
|
||||||
|
|
||||||
def snapshot(self, context, instance, snapshot_name, update_task_state):
|
def snapshot(self, context, instance, snapshot_name, update_task_state):
|
||||||
"""Create snapshot from a running VM instance.
|
"""Create snapshot from a running VM instance.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user