From a02fa7dd1f6b30929829b40e1627127099f2e224 Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Tue, 28 Mar 2017 12:38:45 -0600 Subject: [PATCH 01/17] [2016.3] Bump previous version to 2016.3.6 --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index a4e591c4ebc5..9c8ecd428115 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -214,7 +214,7 @@ def inner(fn, *iargs, **ikwargs): version = salt.version.__version__ latest_release = '2016.11.3' # latest release -previous_release = '2016.3.5' # latest release from previous branch +previous_release = '2016.3.6' # latest release from previous branch previous_release_dir = '2016.3' # path on web server for previous branch next_release = '' # next release next_release_dir = '' # path on web server for next release branch From 7c144888da439fb002348e81cd1f55b93e4ff391 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Tue, 28 Mar 2017 16:29:58 -0400 Subject: [PATCH 02/17] Backporting changes in vmware cloud driver from develop branch to 2016.11 branch --- salt/cloud/clouds/vmware.py | 361 ++++++++++++++++++------------ salt/utils/vmware.py | 426 ++++++++++++++++++++++++++++++++++-- 2 files changed, 632 insertions(+), 155 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index e659be0c78f1..713fba655aac 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -116,7 +116,7 @@ # Import python libs from __future__ import absolute_import from random import randint -from re import findall +from re import findall, split, search, compile import pprint import logging import time @@ -126,6 +126,7 @@ # Import salt libs import salt.utils import salt.utils.cloud +import salt.utils.network import salt.utils.xmlutil import salt.utils.vmware from salt.exceptions import SaltCloudSystemExit @@ -133,15 +134,10 @@ # Import salt cloud libs import salt.config as config -# Attempt to import pyVim and pyVmomi libs -ESX_5_5_NAME_PORTION = 'VMware ESXi 5.5' -SAFE_ESX_5_5_CONTROLLER_KEY_INDEX = 200 -FLATTEN_DISK_FULL_CLONE = 'moveAllDiskBackingsAndDisallowSharing' -COPY_ALL_DISKS_FULL_CLONE = 'moveAllDiskBackingsAndAllowSharing' -CURRENT_STATE_LINKED_CLONE = 'moveChildMostDiskBacking' -QUICK_LINKED_CLONE = 'createNewChildDiskBacking' - +# Import 3rd-party libs +import salt.ext.six as six try: + # Attempt to import pyVmomi libs from pyVmomi import vim HAS_PYVMOMI = True except Exception: @@ -154,15 +150,13 @@ except Exception: pass -try: - import salt.ext.six as six - HAS_SIX = True -except ImportError: - # Salt version <= 2014.7.0 - try: - import six - except ImportError: - HAS_SIX = False +ESX_5_5_NAME_PORTION = 'VMware ESXi 5.5' +SAFE_ESX_5_5_CONTROLLER_KEY_INDEX = 200 +FLATTEN_DISK_FULL_CLONE = 'moveAllDiskBackingsAndDisallowSharing' +COPY_ALL_DISKS_FULL_CLONE = 'moveAllDiskBackingsAndAllowSharing' +CURRENT_STATE_LINKED_CLONE = 'moveChildMostDiskBacking' +QUICK_LINKED_CLONE = 'createNewChildDiskBacking' + IP_RE = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' @@ -203,7 +197,6 @@ def get_dependencies(): ''' deps = { 'pyVmomi': HAS_PYVMOMI, - 'six': HAS_SIX } return config.check_driver_dependencies( __virtualname__, @@ -267,27 +260,20 @@ def _get_si(): port=port) -def _edit_existing_hard_disk_helper(disk, size_kb): - disk.capacityInKB = size_kb - disk_spec = vim.vm.device.VirtualDeviceSpec() - disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit - disk_spec.device = disk - - return disk_spec - - -def _edit_existing_hard_disk_mode_helper(disk, mode): +def _edit_existing_hard_disk_helper(disk, size_kb=None, size_gb=None, mode=None): + if size_kb or size_gb: + disk.capacityInKB = size_kb if size_kb else int(size_gb * 1024.0 * 1024.0) + if mode: + disk.backing.diskMode = mode disk_spec = vim.vm.device.VirtualDeviceSpec() disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit - disk.backing.diskMode = mode disk_spec.device = disk return disk_spec -def _add_new_hard_disk_helper(disk_label, size_gb, unit_number, controller_key=1000, thin_provision=False): +def _add_new_hard_disk_helper(disk_label, size_gb, unit_number, controller_key=1000, thin_provision=False, datastore=None, vm_name=None): random_key = randint(-2099, -2000) - size_kb = int(size_gb * 1024.0 * 1024.0) disk_spec = vim.vm.device.VirtualDeviceSpec() @@ -299,9 +285,19 @@ def _add_new_hard_disk_helper(disk_label, size_gb, unit_number, controller_key=1 disk_spec.device.deviceInfo = vim.Description() disk_spec.device.deviceInfo.label = disk_label disk_spec.device.deviceInfo.summary = "{0} GB".format(size_gb) + disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() disk_spec.device.backing.thinProvisioned = thin_provision disk_spec.device.backing.diskMode = 'persistent' + + if datastore: + ds_ref = salt.utils.vmware.get_datastore_ref(_get_si(), datastore) + if not ds_ref: + raise SaltCloudSystemExit('Requested {0} disk in datastore {1}, but no such datastore found.'.format(disk_label, datastore)) + datastore_path = '[' + str(ds_ref.name) + '] ' + vm_name + disk_spec.device.backing.fileName = datastore_path + '/' + disk_label + '.vmdk' + disk_spec.device.backing.datastore = ds_ref + disk_spec.device.controllerKey = controller_key disk_spec.device.unitNumber = unit_number disk_spec.device.capacityInKB = size_kb @@ -373,7 +369,7 @@ def _edit_existing_network_adapter(network_adapter, new_network_name, adapter_ty return network_spec -def _add_new_network_adapter_helper(network_adapter_label, network_name, adapter_type, switch_type, container_ref=None): +def _add_new_network_adapter_helper(network_adapter_label, network_name, adapter_type, switch_type, mac, container_ref=None): random_key = randint(-4099, -4000) adapter_type.strip().lower() @@ -419,6 +415,9 @@ def _add_new_network_adapter_helper(network_adapter_label, network_name, adapter switch_type) raise SaltCloudSystemExit(err_msg) + if mac != '': + network_spec.device.addressType = 'assigned' + network_spec.device.macAddress = mac network_spec.device.key = random_key network_spec.device.deviceInfo = vim.Description() network_spec.device.deviceInfo.label = network_adapter_label @@ -605,7 +604,26 @@ def _set_network_adapter_mapping(adapter_specs): return adapter_mapping -def _manage_devices(devices, vm=None, container_ref=None): +def _get_mode_spec(device, mode, disk_spec): + if device.backing.diskMode != mode: + if not disk_spec: + disk_spec = _edit_existing_hard_disk_helper( + disk=device, + mode=mode + ) + else: + disk_spec.device.backing.diskMode = mode + return disk_spec + + +def _get_size_spec(device, size_gb=None, size_kb=None): + if size_kb is None and size_gb is not None: + size_kb = int(size_gb * 1024.0 * 1024.0) + disk_spec = _edit_existing_hard_disk_helper(disk=device, size_kb=size_kb) if device.capacityInKB < size_kb else None + return disk_spec + + +def _manage_devices(devices, vm=None, container_ref=None, new_vm_name=None): unit_number = 0 bus_number = 0 device_specs = [] @@ -625,34 +643,40 @@ def _manage_devices(devices, vm=None, container_ref=None): if isinstance(device, vim.vm.device.VirtualDisk): # this is a hard disk if 'disk' in list(devices.keys()): - # there is at least one disk specified to be created/configured + # there is atleast one disk specified to be created/configured unit_number += 1 existing_disks_label.append(device.deviceInfo.label) if device.deviceInfo.label in list(devices['disk'].keys()): disk_spec = None if 'size' in devices['disk'][device.deviceInfo.label]: - disk_spec = _get_size_spec(device, devices) - size_kb = float( - devices['disk'][device.deviceInfo.label]['size'] - ) * 1024 * 1024 + size_gb = float(devices['disk'][device.deviceInfo.label]['size']) + size_kb = int(size_gb * 1024.0 * 1024.0) else: - # User didn't specify disk size - # in the cloud profile - # so use the existing disk size - log.info('Virtual disk size was not' - ' specified in the cloud profile.' - ' Using existing disk size.') + # User didn't specify disk size in the cloud + # profile so use the existing disk size size_kb = device.capacityInKB + size_gb = size_kb / (1024.0 * 1024.0) + log.debug( + 'Virtual disk size for \'{0}\' was not ' + 'specified in the cloud profile or map file. ' + 'Using existing virtual disk size of \'{1}GB\''.format( + device.deviceInfo.label, + size_gb + ) + ) - if device.capacityInKB < size_kb: - # expand the disk - disk_spec = _edit_existing_hard_disk_helper(device, size_kb) - elif device.capacityInKB > size_kb: + if device.capacityInKB > size_kb: raise SaltCloudSystemExit( - 'The specified disk size is smaller than the ' - 'size of the disk image. It must be equal to ' - 'or greater than the disk image' + 'The specified disk size \'{0}GB\' for \'{1}\' is ' + 'smaller than the disk image size \'{2}GB\'. It must ' + 'be equal to or greater than the disk image'.format( + float(devices['disk'][device.deviceInfo.label]['size']), + device.deviceInfo.label, + float(device.capacityInKB / (1024.0 * 1024.0)) + ) ) + else: + disk_spec = _get_size_spec(device=device, size_kb=size_kb) if 'mode' in devices['disk'][device.deviceInfo.label]: if devices['disk'][device.deviceInfo.label]['mode'] \ @@ -661,12 +685,14 @@ def _manage_devices(devices, vm=None, container_ref=None): 'independent_nonpersistent', 'dependent', ]: - disk_spec = _get_mode_spec(device, devices, disk_spec) + mode = devices['disk'][device.deviceInfo.label]['mode'] + disk_spec = _get_mode_spec(device, mode, disk_spec) else: raise SaltCloudSystemExit('Invalid disk' ' backing mode' ' specified!') - device_specs.append(disk_spec) + if disk_spec is not None: + device_specs.append(disk_spec) elif isinstance(device.backing, vim.vm.device.VirtualEthernetCard.NetworkBackingInfo) or isinstance(device.backing, vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo): # this is a network adapter @@ -723,8 +749,9 @@ def _manage_devices(devices, vm=None, container_ref=None): network_name = devices['network'][network_adapter_label]['name'] adapter_type = devices['network'][network_adapter_label]['adapter_type'] if 'adapter_type' in devices['network'][network_adapter_label] else '' switch_type = devices['network'][network_adapter_label]['switch_type'] if 'switch_type' in devices['network'][network_adapter_label] else '' + mac = devices['network'][network_adapter_label]['mac'] if 'mac' in devices['network'][network_adapter_label] else '' # create the network adapter - network_spec = _add_new_network_adapter_helper(network_adapter_label, network_name, adapter_type, switch_type, container_ref) + network_spec = _add_new_network_adapter_helper(network_adapter_label, network_name, adapter_type, switch_type, mac, container_ref) adapter_mapping = _set_network_adapter_mapping(devices['network'][network_adapter_label]) device_specs.append(network_spec) nics_map.append(adapter_mapping) @@ -769,7 +796,8 @@ def _manage_devices(devices, vm=None, container_ref=None): # create the disk size_gb = float(devices['disk'][disk_label]['size']) thin_provision = bool(devices['disk'][disk_label]['thin_provision']) if 'thin_provision' in devices['disk'][disk_label] else False - disk_spec = _add_new_hard_disk_helper(disk_label, size_gb, unit_number, thin_provision=thin_provision) + datastore = devices['disk'][disk_label].get('datastore', None) + disk_spec = _add_new_hard_disk_helper(disk_label, size_gb, unit_number, thin_provision=thin_provision, datastore=datastore, vm_name=new_vm_name) # when creating both SCSI controller and Hard disk at the same time we need the randomly # assigned (temporary) key of the newly created SCSI controller @@ -822,31 +850,6 @@ def _manage_devices(devices, vm=None, container_ref=None): return ret -def _get_mode_spec(device, devices, disk_spec): - if device.backing.diskMode != \ - devices['disk'][device.deviceInfo.label]['mode']: - if not disk_spec: - disk_spec = \ - _edit_existing_hard_disk_mode_helper( - device, - devices['disk'][device.deviceInfo.label]['mode'] - ) - else: - disk_spec.device.backing.diskMode = \ - devices['disk'][device.deviceInfo.label]['mode'] - return disk_spec - - -def _get_size_spec(device, devices): - size_gb = float(devices['disk'][device.deviceInfo.label]['size']) - size_kb = int(size_gb * 1024.0 * 1024.0) - disk_spec = None - if device.capacityInKB < size_kb: - # expand the disk - disk_spec = _edit_existing_hard_disk_helper(device, size_kb) - return disk_spec - - def _wait_for_vmware_tools(vm_ref, max_wait): time_counter = 0 starttime = time.time() @@ -911,7 +914,16 @@ def _wait_for_ip(vm_ref, max_wait): max_wait_ip = max_wait vmware_tools_status = _wait_for_vmware_tools(vm_ref, max_wait_vmware_tools) if not vmware_tools_status: - return False + # VMware will only report the IP if VMware tools are installed. Try to + # determine the IP using DNS + vm_name = vm_ref.summary.config.name + resolved_ips = salt.utils.network.host_to_ips(vm_name) + log.debug("Timeout waiting for VMware tools. The name {0} resolved " + "to {1}".format(vm_name, str(resolved_ips))) + if isinstance(resolved_ips, list) and len(resolved_ips): + return resolved_ips[0] + else: + return False time_counter = 0 starttime = time.time() while time_counter < max_wait_ip: @@ -1206,6 +1218,29 @@ def _get_snapshots(snapshot_list, current_snapshot=None, parent_snapshot_path="" return snapshots +def _get_snapshot_ref_helper(base_snapshot, snapshot_name): + if base_snapshot.name == snapshot_name: + return base_snapshot + else: + for snapshot in base_snapshot.childSnapshotList: + snapshot_ref = _get_snapshot_ref_helper(snapshot, snapshot_name) + if snapshot_ref is not None: + return snapshot_ref + + +def _get_snapshot_ref_by_name(vm_ref, snapshot_name): + snapshot_ref = None + try: + for root_snapshot in vm_ref.snapshot.rootSnapshotList: + snapshot_ref = _get_snapshot_ref_helper(root_snapshot, snapshot_name) + if snapshot_ref is not None: + break + except (IndexError, AttributeError): + snapshot_ref = None + + return snapshot_ref + + def _upg_tools_helper(vm, reboot=False): # Exit if template if vm.config.template: @@ -1902,6 +1937,9 @@ def list_snapshots(kwargs=None, call=None): return {vm["name"]: _get_snapshots(vm["snapshot"].rootSnapshotList)} else: ret[vm["name"]] = _get_snapshots(vm["snapshot"].rootSnapshotList) + else: + if kwargs and kwargs.get('name') == vm["name"]: + return {} return ret @@ -1953,15 +1991,24 @@ def start(name, call=None): return 'powered on' -def stop(name, call=None): +def stop(name, soft=False, call=None): ''' To stop/power off a VM using its name + .. note:: + + If ``soft=True`` then issues a command to the guest operating system + asking it to perform a clean shutdown of all services. + Default is soft=False + + For ``soft=True`` vmtools should be installed on guest system. + CLI Example: .. code-block:: bash salt-cloud -a stop vmname + salt-cloud -a stop vmname soft=True ''' if call != 'action': raise SaltCloudSystemExit( @@ -1984,8 +2031,11 @@ def stop(name, call=None): return ret try: log.info('Stopping VM {0}'.format(name)) - task = vm["object"].PowerOff() - salt.utils.vmware.wait_for_task(task, name, 'power off') + if soft: + vm["object"].ShutdownGuest() + else: + task = vm["object"].PowerOff() + salt.utils.vmware.wait_for_task(task, name, 'power off') except Exception as exc: log.error( 'Error while powering off VM {0}: {1}'.format( @@ -2051,15 +2101,24 @@ def suspend(name, call=None): return 'suspended' -def reset(name, call=None): +def reset(name, soft=False, call=None): ''' To reset a VM using its name + .. note:: + + If ``soft=True`` then issues a command to the guest operating system + asking it to perform a reboot. Otherwise hypervisor will terminate VM and start it again. + Default is soft=False + + For ``soft=True`` vmtools should be installed on guest system. + CLI Example: .. code-block:: bash salt-cloud -a reset vmname + salt-cloud -a reset vmname soft=True ''' if call != 'action': raise SaltCloudSystemExit( @@ -2082,8 +2141,11 @@ def reset(name, call=None): return ret try: log.info('Resetting VM {0}'.format(name)) - task = vm["object"].ResetVM_Task() - salt.utils.vmware.wait_for_task(task, name, 'reset') + if soft: + vm["object"].RebootGuest() + else: + task = vm["object"].ResetVM_Task() + salt.utils.vmware.wait_for_task(task, name, 'reset') except Exception as exc: log.error( 'Error while resetting VM {0}: {1}'.format( @@ -2330,6 +2392,9 @@ def create(vm_): customization = config.get_cloud_config_value( 'customization', vm_, __opts__, search_global=False, default=True ) + customization_spec = config.get_cloud_config_value( + 'customization_spec', vm_, __opts__, search_global=False, default=None + ) win_password = config.get_cloud_config_value( 'win_password', vm_, __opts__, search_global=False, default=None ) @@ -2343,6 +2408,9 @@ def create(vm_): 'win_user_fullname', vm_, __opts__, search_global=False, default='Windows User' ) + # Get service instance object + si = _get_si() + container_ref = None # If datacenter is specified, set the container reference to start search from it instead @@ -2351,9 +2419,13 @@ def create(vm_): container_ref = datacenter_ref if datacenter_ref else None if 'clonefrom' in vm_: + # If datacenter is specified, set the container reference to start search from it instead + if datacenter: + datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter) + container_ref = datacenter_ref if datacenter_ref else None # Clone VM/template from specified VM/template - object_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, vm_['clonefrom'], container_ref=container_ref) + object_ref = salt.utils.vmware.get_mor_by_property(si, vim.VirtualMachine, vm_['clonefrom'], container_ref=container_ref) if object_ref: clone_type = "template" if object_ref.config.template else "vm" else: @@ -2366,13 +2438,13 @@ def create(vm_): # Either a cluster, or a resource pool must be specified when cloning from template or creating. if resourcepool: - resourcepool_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.ResourcePool, resourcepool, container_ref=container_ref) + resourcepool_ref = salt.utils.vmware.get_mor_by_property(si, vim.ResourcePool, resourcepool, container_ref=container_ref) if not resourcepool_ref: log.error("Specified resource pool: '{0}' does not exist".format(resourcepool)) if not clone_type or clone_type == "template": raise SaltCloudSystemExit('You must specify a resource pool that exists.') elif cluster: - cluster_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.ClusterComputeResource, cluster, container_ref=container_ref) + cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster, container_ref=container_ref) if not cluster_ref: log.error("Specified cluster: '{0}' does not exist".format(cluster)) if not clone_type or clone_type == "template": @@ -2393,7 +2465,7 @@ def create(vm_): # Either a datacenter or a folder can be optionally specified when cloning, required when creating. # If not specified when cloning, the existing VM/template\'s parent folder is used. if folder: - folder_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Folder, folder, container_ref=container_ref) + folder_ref = salt.utils.vmware.get_mor_by_property(si, vim.Folder, folder, container_ref=container_ref) if not folder_ref: log.error("Specified folder: '{0}' does not exist".format(folder)) log.debug("Using folder in which {0} {1} is present".format(clone_type, vm_['clonefrom'])) @@ -2423,12 +2495,12 @@ def create(vm_): # Either a datastore/datastore cluster can be optionally specified. # If not specified, the current datastore is used. if datastore: - datastore_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datastore, datastore, container_ref=container_ref) + datastore_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datastore, datastore, container_ref=container_ref) if datastore_ref: # specific datastore has been specified reloc_spec.datastore = datastore_ref else: - datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.StoragePod, datastore, container_ref=container_ref) + datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.StoragePod, datastore, container_ref=container_ref) if not datastore_cluster_ref: log.error("Specified datastore/datastore cluster: '{0}' does not exist".format(datastore)) log.debug("Using datastore used by the {0} {1}".format(clone_type, vm_['clonefrom'])) @@ -2437,7 +2509,7 @@ def create(vm_): log.debug("Using datastore used by the {0} {1}".format(clone_type, vm_['clonefrom'])) if host: - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host, container_ref=container_ref) + host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host, container_ref=container_ref) if host_ref: reloc_spec.host = host_ref else: @@ -2448,7 +2520,7 @@ def create(vm_): 'You must specify a datastore when creating not cloning.' ) else: - datastore_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datastore, datastore) + datastore_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datastore, datastore) if not datastore_ref: raise SaltCloudSystemExit("Specified datastore: '{0}' does not exist".format(datastore)) @@ -2498,7 +2570,7 @@ def create(vm_): config_spec.memoryMB = memory_mb if devices: - specs = _manage_devices(devices, object_ref) + specs = _manage_devices(devices, vm=object_ref, new_vm_name=vm_name) config_spec.deviceChange = specs['device_specs'] if extra_config: @@ -2520,12 +2592,21 @@ def create(vm_): reloc_spec, template) - if customization and (devices and 'network' in list(devices.keys())): + if customization and customization_spec: + customization_spec = salt.utils.vmware.get_customizationspec_ref(si=si, customization_spec_name=customization_spec) + clone_spec.customization = customization_spec.spec + elif customization and (devices and 'network' in list(devices.keys())): global_ip = vim.vm.customization.GlobalIPSettings() if 'dns_servers' in list(vm_.keys()): global_ip.dnsServerList = vm_['dns_servers'] - hostName = vm_name.split('.')[0] - domainName = vm_name.split('.', 1)[-1] + + non_hostname_chars = compile(r'[^\w-]') + if search(non_hostname_chars, vm_name): + hostName = split(non_hostname_chars, vm_name, maxsplit=1)[0] + else: + hostName = vm_name + domainName = hostName.split('.', 1)[-1] + if 'Windows' not in object_ref.config.guestFullName: identity = vim.vm.customization.LinuxPrep() identity.hostName = vim.vm.customization.FixedName(name=hostName) @@ -2568,6 +2649,9 @@ def create(vm_): pprint.pformat(config_spec)) ) + event_kwargs = vm_.copy() + del event_kwargs['password'] + try: __utils__['cloud.fire_event']( 'event', @@ -2598,9 +2682,6 @@ def create(vm_): folder=folder_ref ) - # get si instance to refer to the content - si = _get_si() - # get recommended datastores recommended_datastores = si.content.storageResourceManager.RecommendDatastores(storageSpec=storage_spec) @@ -2628,7 +2709,7 @@ def create(vm_): ) return {'Error': err_msg} - new_vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, vm_name, container_ref=container_ref) + new_vm_ref = salt.utils.vmware.get_mor_by_property(si, vim.VirtualMachine, vm_name, container_ref=container_ref) # Find how to power on in CreateVM_Task (if possible), for now this will do try: @@ -2769,14 +2850,14 @@ def create_datacenter(kwargs=None, call=None): 'The datacenter name must be a non empty string of less than 80 characters.' ) + # Get the service instance + si = _get_si() + # Check if datacenter already exists - datacenter_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) if datacenter_ref: return {datacenter_name: 'datacenter already exists'} - # Get the service instance - si = _get_si() - folder = si.content.rootFolder # Verify that the folder is of type vim.Folder @@ -2829,15 +2910,18 @@ def create_cluster(kwargs=None, call=None): 'You must specify name of the datacenter where the cluster should be created.' ) + # Get the service instance + si = _get_si() + if not isinstance(datacenter, vim.Datacenter): - datacenter = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datacenter, datacenter) + datacenter = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter) if not datacenter: raise SaltCloudSystemExit( 'The specified datacenter does not exist.' ) # Check if cluster already exists - cluster_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.ClusterComputeResource, cluster_name) + cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster_name) if cluster_ref: return {cluster_name: 'cluster already exists'} @@ -3487,29 +3571,6 @@ def create_snapshot(name, kwargs=None, call=None): vm_ref.snapshot.currentSnapshot)} -def _get_snapshot_ref_helper(base_snapshot, snapshot_name): - if base_snapshot.name == snapshot_name: - return base_snapshot - else: - for snapshot in base_snapshot.childSnapshotList: - snapshot_ref = _get_snapshot_ref_helper(snapshot, snapshot_name) - if snapshot_ref is not None: - return snapshot_ref - - -def _get_snapshot_ref_by_name(vm_ref, snapshot_name): - snapshot_ref = None - try: - for root_snapshot in vm_ref.snapshot.rootSnapshotList: - snapshot_ref = _get_snapshot_ref_helper(root_snapshot, snapshot_name) - if snapshot_ref is not None: - break - except (IndexError, AttributeError): - snapshot_ref = None - - return snapshot_ref - - def revert_to_snapshot(name, kwargs=None, call=None): ''' Revert virtual machine to it's current snapshot. If no snapshot @@ -3706,15 +3767,18 @@ def add_host(kwargs=None, call=None): 'You must specify either the cluster name or the datacenter name.' ) + # Get the service instance + si = _get_si() + if cluster_name: - cluster_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.ClusterComputeResource, cluster_name) + cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster_name) if not cluster_ref: raise SaltCloudSystemExit( 'Specified cluster does not exist.' ) if datacenter_name: - datacenter_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) if not datacenter_ref: raise SaltCloudSystemExit( 'Specified datacenter does not exist.' @@ -3798,7 +3862,10 @@ def remove_host(kwargs=None, call=None): 'You must specify name of the host system.' ) - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + # Get the service instance + si = _get_si() + + host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: raise SaltCloudSystemExit( 'Specified host system does not exist.' @@ -3849,7 +3916,10 @@ def connect_host(kwargs=None, call=None): 'You must specify name of the host system.' ) - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + # Get the service instance + si = _get_si() + + host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: raise SaltCloudSystemExit( 'Specified host system does not exist.' @@ -3898,7 +3968,10 @@ def disconnect_host(kwargs=None, call=None): 'You must specify name of the host system.' ) - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + # Get the service instance + si = _get_si() + + host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: raise SaltCloudSystemExit( 'Specified host system does not exist.' @@ -3954,7 +4027,10 @@ def reboot_host(kwargs=None, call=None): 'You must specify name of the host system.' ) - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + # Get the service instance + si = _get_si() + + host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: raise SaltCloudSystemExit( 'Specified host system does not exist.' @@ -4028,12 +4104,15 @@ def create_datastore_cluster(kwargs=None, call=None): 'You must specify name of the datacenter where the datastore cluster should be created.' ) + # Get the service instance + si = _get_si() + # Check if datastore cluster already exists - datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.StoragePod, datastore_cluster_name) + datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.StoragePod, datastore_cluster_name) if datastore_cluster_ref: return {datastore_cluster_name: 'datastore cluster already exists'} - datacenter_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) if not datacenter_ref: raise SaltCloudSystemExit( 'The specified datacenter does not exist.' diff --git a/salt/utils/vmware.py b/salt/utils/vmware.py index 4730e53f6d96..93537793957a 100644 --- a/salt/utils/vmware.py +++ b/salt/utils/vmware.py @@ -116,7 +116,7 @@ def __virtual__(): return False, 'Missing dependency: The salt.utils.vmware module requires pyVmomi.' -def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None): +def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, credstore=None): ''' Shell out and call the specified esxcli commmand, parse the result and return something sane. @@ -128,6 +128,8 @@ def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None): :param cmd: esxcli command and arguments :param esxi_host: If `host` is a vCenter host, then esxi_host is the ESXi machine on which to execute this command + :param credstore: Optional path to the credential store file + :return: Dictionary ''' @@ -142,6 +144,9 @@ def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None): if protocol is None: protocol = 'https' + if credstore: + esx_cmd += ' --credstore \'{0}\''.format(credstore) + if not esxi_host: # Then we are connecting directly to an ESXi server, # 'host' points at that server, and esxi_host is a reference to the @@ -269,6 +274,40 @@ def _get_service_instance(host, username, password, protocol, return service_instance +def get_customizationspec_ref(si, customization_spec_name): + ''' + Get a reference to a VMware customization spec for the purposes of customizing a clone + + si + ServiceInstance for the vSphere or ESXi server (see get_service_instance) + + customization_spec_name + Name of the customization spec + + ''' + customization_spec_name = si.content.customizationSpecManager.GetCustomizationSpec(name=customization_spec_name) + return customization_spec_name + + +def get_datastore_ref(si, datastore_name): + ''' + Get a reference to a VMware datastore for the purposes of adding/removing disks + + si + ServiceInstance for the vSphere or ESXi server (see get_service_instance) + + datastore_name + Name of the datastore + + ''' + inventory = get_inventory(si) + container = inventory.viewManager.CreateContainerView(inventory.rootFolder, [vim.Datastore], True) + for item in container.view: + if item.name == datastore_name: + return item + return None + + def get_service_instance(host, username=None, password=None, protocol=None, port=None, mechanism='userpass', principal=None, domain=None): @@ -348,10 +387,49 @@ def get_service_instance(host, username=None, password=None, protocol=None, mechanism, principal, domain) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) return service_instance +def get_service_instance_from_managed_object(mo_ref, name=''): + ''' + Retrieves the service instance from a managed object. + + me_ref + Reference to a managed object (of type vim.ManagedEntity). + + name + Name of managed object. This field is optional. + ''' + if not name: + name = mo_ref.name + log.trace('[{0}] Retrieving service instance from managed object' + ''.format(name)) + si = vim.ServiceInstance('ServiceInstance') + si._stub = mo_ref._stub + return si + + +def disconnect(service_instance): + ''' + Function that disconnects from the vCenter server or ESXi host + + service_instance + The Service Instance from which to obtain managed object references. + ''' + log.trace('Disconnecting') + try: + Disconnect(service_instance) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + + def is_connection_to_a_vcenter(service_instance): ''' Function that returns True if the connection is made to a vCenter Server and @@ -360,7 +438,12 @@ def is_connection_to_a_vcenter(service_instance): service_instance The Service Instance from which to obtain managed object references. ''' - api_type = service_instance.content.about.apiType + try: + api_type = service_instance.content.about.apiType + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) log.trace('api_type = {0}'.format(api_type)) if api_type == 'VirtualCenter': return True @@ -553,6 +636,22 @@ def get_inventory(service_instance): return service_instance.RetrieveContent() +def get_root_folder(service_instance): + ''' + Returns the root folder of a vCenter. + + service_instance + The Service Instance Object for which to obtain the root folder. + ''' + try: + log.trace('Retrieving root folder') + return service_instance.RetrieveContent().rootFolder + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + + def get_content(service_instance, obj_type, property_list=None, container_ref=None, traversal_spec=None, local_properties=False): @@ -586,7 +685,7 @@ def get_content(service_instance, obj_type, property_list=None, ''' # Start at the rootFolder if container starting point not specified if not container_ref: - container_ref = service_instance.content.rootFolder + container_ref = get_root_folder(service_instance) # By default, the object reference used as the starting poing for the filter # is the container_ref passed in the function @@ -596,8 +695,14 @@ def get_content(service_instance, obj_type, property_list=None, local_traversal_spec = True # We don't have a specific traversal spec override so we are going to # get everything using a container view - obj_ref = service_instance.content.viewManager.CreateContainerView( - container_ref, [obj_type], True) + try: + obj_ref = service_instance.content.viewManager.CreateContainerView( + container_ref, [obj_type], True) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + # Create 'Traverse All' traversal spec to determine the path for # collection traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( @@ -629,11 +734,21 @@ def get_content(service_instance, obj_type, property_list=None, ) # Retrieve the contents - content = service_instance.content.propertyCollector.RetrieveContents([filter_spec]) + try: + content = service_instance.content.propertyCollector.RetrieveContents([filter_spec]) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) # Destroy the object view if local_traversal_spec: - obj_ref.Destroy() + try: + obj_ref.Destroy() + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) return content @@ -663,7 +778,8 @@ def get_mor_by_property(service_instance, object_type, property_value, property_ object_list = get_mors_with_properties(service_instance, object_type, property_list=[property_name], container_ref=container_ref) for obj in object_list: - if obj[property_name] == property_value: + obj_id = str(obj.get('object', '')).strip('\'"') + if obj[property_name] == property_value or property_value == obj_id: return obj['object'] return None @@ -723,6 +839,54 @@ def get_mors_with_properties(service_instance, object_type, property_list=None, return object_list +def get_properties_of_managed_object(mo_ref, properties): + ''' + Returns specific properties of a managed object, retrieved in an + optimally. + + mo_ref + The managed object reference. + + properties + List of properties of the managed object to retrieve. + ''' + service_instance = get_service_instance_from_managed_object(mo_ref) + log.trace('Retrieving name of {0}'''.format(type(mo_ref).__name__)) + try: + items = get_mors_with_properties(service_instance, + type(mo_ref), + container_ref=mo_ref, + property_list=['name'], + local_properties=True) + mo_name = items[0]['name'] + except vmodl.query.InvalidProperty: + mo_name = '' + log.trace('Retrieving properties \'{0}\' of {1} \'{2}\'' + ''.format(properties, type(mo_ref).__name__, mo_name)) + items = get_mors_with_properties(service_instance, + type(mo_ref), + container_ref=mo_ref, + property_list=properties, + local_properties=True) + if not items: + raise salt.exceptions.VMwareApiError( + 'Properties of managed object \'{0}\' weren\'t ' + 'retrieved'.format(mo_name)) + return items[0] + + +def get_managed_object_name(mo_ref): + ''' + Returns the name of a managed object. + If the name wasn't found, it returns None. + + mo_ref + The managed object reference. + ''' + props = get_properties_of_managed_object(mo_ref, ['name']) + return props.get('name') + + def get_network_adapter_type(adapter_type): ''' Return the network adapter type. @@ -776,6 +940,155 @@ def list_datacenters(service_instance): return list_objects(service_instance, vim.Datacenter) +def get_datacenters(service_instance, datacenter_names=None, + get_all_datacenters=False): + ''' + Returns all datacenters in a vCenter. + + service_instance + The Service Instance Object from which to obtain cluster. + + datacenter_names + List of datacenter names to filter by. Default value is None. + + get_all_datacenters + Flag specifying whether to retrieve all datacenters. + Default value is None. + ''' + items = [i['object'] for i in + get_mors_with_properties(service_instance, + vim.Datacenter, + property_list=['name']) + if get_all_datacenters or + (datacenter_names and i['name'] in datacenter_names)] + return items + + +def get_datacenter(service_instance, datacenter_name): + ''' + Returns a vim.Datacenter managed object. + + service_instance + The Service Instance Object from which to obtain datacenter. + + datacenter_name + The datacenter name + ''' + items = get_datacenters(service_instance, + datacenter_names=[datacenter_name]) + if not items: + raise salt.exceptions.VMwareObjectRetrievalError( + 'Datacenter \'{0}\' was not found'.format(datacenter_name)) + return items[0] + + +def create_datacenter(service_instance, datacenter_name): + ''' + Creates a datacenter. + + .. versionadded:: Nitrogen + + service_instance + The Service Instance Object + + datacenter_name + The datacenter name + ''' + root_folder = get_root_folder(service_instance) + log.trace('Creating datacenter \'{0}\''.format(datacenter_name)) + try: + dc_obj = root_folder.CreateDatacenter(datacenter_name) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + return dc_obj + + +def get_cluster(dc_ref, cluster): + ''' + Returns a cluster in a datacenter. + + dc_ref + The datacenter reference + + cluster + The cluster to be retrieved + ''' + dc_name = get_managed_object_name(dc_ref) + log.trace('Retrieving cluster \'{0}\' from datacenter \'{1}\'' + ''.format(cluster, dc_name)) + si = get_service_instance_from_managed_object(dc_ref, name=dc_name) + traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( + path='hostFolder', + skip=True, + type=vim.Datacenter, + selectSet=[vmodl.query.PropertyCollector.TraversalSpec( + path='childEntity', + skip=False, + type=vim.Folder)]) + items = [i['object'] for i in + get_mors_with_properties(si, + vim.ClusterComputeResource, + container_ref=dc_ref, + property_list=['name'], + traversal_spec=traversal_spec) + if i['name'] == cluster] + if not items: + raise salt.exceptions.VMwareObjectRetrievalError( + 'Cluster \'{0}\' was not found in datacenter ' + '\'{1}\''. format(cluster, dc_name)) + return items[0] + + +def create_cluster(dc_ref, cluster_name, cluster_spec): + ''' + Creates a cluster in a datacenter. + + dc_ref + The parent datacenter reference. + + cluster_name + The cluster name. + + cluster_spec + The cluster spec (vim.ClusterConfigSpecEx). + Defaults to None. + ''' + dc_name = get_managed_object_name(dc_ref) + log.trace('Creating cluster \'{0}\' in datacenter \'{1}\'' + ''.format(cluster_name, dc_name)) + try: + dc_ref.hostFolder.CreateClusterEx(cluster_name, cluster_spec) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + + +def update_cluster(cluster_ref, cluster_spec): + ''' + Updates a cluster in a datacenter. + + cluster_ref + The cluster reference. + + cluster_spec + The cluster spec (vim.ClusterConfigSpecEx). + Defaults to None. + ''' + cluster_name = get_managed_object_name(cluster_ref) + log.trace('Updating cluster \'{0}\''.format(cluster_name)) + try: + task = cluster_ref.ReconfigureComputeResource_Task(cluster_spec, + modify=True) + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + wait_for_task(task, cluster_name, 'ClusterUpdateTask') + + def list_clusters(service_instance): ''' Returns a list of clusters associated with a given service instance. @@ -806,6 +1119,68 @@ def list_datastores(service_instance): return list_objects(service_instance, vim.Datastore) +def get_hosts(service_instance, datacenter_name=None, host_names=None, + cluster_name=None, get_all_hosts=False): + ''' + Returns a list of vim.HostSystem objects representing ESXi hosts + in a vcenter filtered by their names and/or datacenter, cluster membership. + + service_instance + The Service Instance Object from which to obtain the hosts. + + datacenter_name + The datacenter name. Default is None. + + host_names + The host_names to be retrieved. Default is None. + + cluster_name + The cluster name - used to restrict the hosts retrieved. Only used if + the datacenter is set. This argument is optional. + + get_all_hosts + Specifies whether to retrieve all hosts in the container. + Default value is False. + ''' + properties = ['name'] + if not host_names: + host_names = [] + if cluster_name: + properties.append('parent') + if datacenter_name: + start_point = get_datacenter(service_instance, datacenter_name) + if cluster_name: + # Retrieval to test if cluster exists. Cluster existence only makes + # sense if the cluster has been specified + cluster = get_cluster(start_point, cluster_name) + else: + # Assume the root folder is the starting point + start_point = get_root_folder(service_instance) + + # Search for the objects + hosts = get_mors_with_properties(service_instance, + vim.HostSystem, + container_ref=start_point, + property_list=properties) + filtered_hosts = [] + for h in hosts: + # Complex conditions checking if a host should be added to the + # filtered list (either due to its name and/or cluster membership) + name_condition = get_all_hosts or (h['name'] in host_names) + # the datacenter_name needs to be set in order for the cluster + # condition membership to be checked, otherwise the condition is + # ignored + cluster_condition = \ + (not datacenter_name or not cluster_name or + (isinstance(h['parent'], vim.ClusterComputeResource) and + h['parent'].name == cluster_name)) + + if name_condition and cluster_condition: + filtered_hosts.append(h['object']) + + return filtered_hosts + + def list_hosts(service_instance): ''' Returns a list of hosts associated with a given service instance. @@ -910,7 +1285,15 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de ''' time_counter = 0 start_time = time.time() - while task.info.state == 'running' or task.info.state == 'queued': + log.trace('task = {0}, task_type = {1}'.format(task, + task.__class__.__name__)) + try: + task_info = task.info + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + while task_info.state == 'running' or task_info.state == 'queued': if time_counter % sleep_seconds == 0: msg = '[ {0} ] Waiting for {1} task to finish [{2} s]'.format( instance_name, task_type, time_counter) @@ -920,9 +1303,13 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de log.debug(msg) time.sleep(1.0 - ((time.time() - start_time) % 1.0)) time_counter += 1 - log.trace('task = {0}, task_type = {1}'.format(task, - task.__class__.__name__)) - if task.info.state == 'success': + try: + task_info = task.info + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.RuntimeFault as exc: + raise salt.exceptions.VMwareRuntimeError(exc.msg) + if task_info.state == 'success': msg = '[ {0} ] Successfully completed {1} task in {2} seconds'.format( instance_name, task_type, time_counter) if log_level == 'info': @@ -930,7 +1317,18 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de else: log.debug(msg) # task is in a successful state - return task.info.result + return task_info.result else: # task is in an error state - raise task.info.error + try: + raise task_info.error + except vim.fault.VimFault as exc: + raise salt.exceptions.VMwareApiError(exc.msg) + except vmodl.fault.SystemError as exc: + raise salt.exceptions.VMwareSystemError(exc.msg) + except vmodl.fault.InvalidArgument as exc: + exc_message = exc.msg + if exc.faultMessage: + exc_message = '{0} ({1})'.format(exc_message, + exc.faultMessage[0].message) + raise salt.exceptions.VMwareApiError(exc_message) From f532ec5288e23fa1256db2a48f8cba2373826127 Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Wed, 29 Mar 2017 09:34:03 -0600 Subject: [PATCH 03/17] initial 2016.3.7 release notes --- doc/topics/releases/2016.3.7.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/topics/releases/2016.3.7.rst diff --git a/doc/topics/releases/2016.3.7.rst b/doc/topics/releases/2016.3.7.rst new file mode 100644 index 000000000000..ccde8c40db7a --- /dev/null +++ b/doc/topics/releases/2016.3.7.rst @@ -0,0 +1,5 @@ +=========================== +Salt 2016.3.7 Release Notes +=========================== + +Version 2016.3.7 is a bugfix release for :ref:`2016.3.0 `. From 14c6575655f61ea79c1d8b4b283e64add0263e92 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 29 Mar 2017 12:06:03 -0500 Subject: [PATCH 04/17] Add docker-py version to the versions report --- salt/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/version.py b/salt/version.py index df259d3b0dc6..46599d7ae6cd 100644 --- a/salt/version.py +++ b/salt/version.py @@ -566,6 +566,7 @@ def dependency_information(include_salt_cloud=False): ('python-gnupg', 'gnupg', '__version__'), ('mysql-python', 'MySQLdb', '__version__'), ('cherrypy', 'cherrypy', '__version__'), + ('docker-py', 'docker', '__version__'), ] if include_salt_cloud: From 4c0763fa2f5f5ceeb5da0a5c940030d920f57964 Mon Sep 17 00:00:00 2001 From: Elena Konoreva Date: Thu, 30 Mar 2017 02:10:40 +0300 Subject: [PATCH 05/17] Added split to cut off debian_revision from rabbitmq-server version Fixes #40396 --- salt/modules/rabbitmq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/rabbitmq.py b/salt/modules/rabbitmq.py index c9098e686f28..fd4fe1678f1d 100644 --- a/salt/modules/rabbitmq.py +++ b/salt/modules/rabbitmq.py @@ -318,7 +318,7 @@ def check_password(name, password, runas=None): if server_version is None: raise ValueError - server_version = server_version.group(1) + server_version = server_version.group(1).split('-')[0] version = [int(i) for i in server_version.split('.')] except ValueError: version = (0, 0, 0) From 034ef30f7c2bec26be53e52c99bc9c0cc9e4731a Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Thu, 30 Mar 2017 00:53:09 -0400 Subject: [PATCH 06/17] Remove old vmware unit tests --- tests/unit/utils/vmware_test/common_test.py | 474 ------------ .../unit/utils/vmware_test/connection_test.py | 702 ------------------ 2 files changed, 1176 deletions(-) delete mode 100644 tests/unit/utils/vmware_test/common_test.py delete mode 100644 tests/unit/utils/vmware_test/connection_test.py diff --git a/tests/unit/utils/vmware_test/common_test.py b/tests/unit/utils/vmware_test/common_test.py deleted file mode 100644 index 75394cbf4357..000000000000 --- a/tests/unit/utils/vmware_test/common_test.py +++ /dev/null @@ -1,474 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: :email:`Alexandru Bleotu ` - - Tests for common functions in salt.utils.vmware -''' - -# Import python libraries -from __future__ import absolute_import -import logging - -# Import Salt testing libraries -from salttesting import TestCase, skipIf -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, \ - PropertyMock - -# Import Salt libraries -import salt.utils.vmware -# Import Third Party Libs -try: - from pyVmomi import vim - HAS_PYVMOMI = True -except ImportError: - HAS_PYVMOMI = False - -# Get Logging Started -log = logging.getLogger(__name__) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.time.time', MagicMock(return_value=1)) -@patch('salt.utils.vmware.time.sleep', MagicMock(return_value=None)) -class WaitForTaskTestCase(TestCase): - '''Tests for salt.utils.vmware.wait_for_task''' - - def test_info_state_running(self): - mock_task = MagicMock() - # The 'bad' values are invalid in the while loop - prop_mock_state = PropertyMock(side_effect=['running', 'bad', 'bad', - 'success']) - prop_mock_result = PropertyMock() - type(mock_task.info).state = prop_mock_state - type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(prop_mock_state.call_count, 4) - self.assertEqual(prop_mock_result.call_count, 1) - - def test_info_state_queued(self): - mock_task = MagicMock() - # The 'bad' values are invalid in the while loop - prop_mock_state = PropertyMock(side_effect=['bad', 'queued', 'bad', - 'bad', 'success']) - prop_mock_result = PropertyMock() - type(mock_task.info).state = prop_mock_state - type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(prop_mock_state.call_count, 5) - self.assertEqual(prop_mock_result.call_count, 1) - - def test_info_state_success(self): - mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='success') - prop_mock_result = PropertyMock() - type(mock_task.info).state = prop_mock_state - type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(prop_mock_state.call_count, 3) - self.assertEqual(prop_mock_result.call_count, 1) - - def test_info_state_different_no_error_attr(self): - mock_task = MagicMock() - # The 'bad' values are invalid in the while loop - prop_mock_state = PropertyMock(return_value='error') - prop_mock_error = PropertyMock(side_effect=Exception('error exc')) - type(mock_task.info).state = prop_mock_state - type(mock_task.info).error = prop_mock_error - with self.assertRaises(Exception) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(prop_mock_state.call_count, 3) - self.assertEqual(prop_mock_error.call_count, 1) - self.assertEqual('error exc', excinfo.exception.message) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -class GetMorsWithPropertiesTestCase(TestCase): - '''Tests for salt.utils.get_mors_with_properties''' - - si = None - obj_type = None - prop_list = None - container_ref = None - traversal_spec = None - - def setUp(self): - self.si = MagicMock() - self.obj_type = MagicMock() - self.prop_list = MagicMock() - self.container_ref = MagicMock() - self.traversal_spec = MagicMock() - - def test_empty_content(self): - get_content = MagicMock(return_value=[]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=False) - self.assertEqual(ret, []) - - def test_local_properties_set(self): - obj_mock = MagicMock() - # obj.propSet - propSet_prop = PropertyMock(return_value=[]) - type(obj_mock).propSet = propSet_prop - # obj.obj - inner_obj_mock = MagicMock() - obj_prop = PropertyMock(return_value=inner_obj_mock) - type(obj_mock).obj = obj_prop - - get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec, - local_properties=True) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=True) - - def test_one_element_content(self): - obj_mock = MagicMock() - # obj.propSet - propSet_prop = PropertyMock(return_value=[]) - type(obj_mock).propSet = propSet_prop - # obj.obj - inner_obj_mock = MagicMock() - obj_prop = PropertyMock(return_value=inner_obj_mock) - type(obj_mock).obj = obj_prop - get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=False) - self.assertEqual(propSet_prop.call_count, 1) - self.assertEqual(obj_prop.call_count, 1) - self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'object': inner_obj_mock}) - - def test_multiple_element_content(self): - # obj1 - obj1_mock = MagicMock() - # obj1.propSet - obj1_propSet_prop = PropertyMock(return_value=[]) - type(obj1_mock).propSet = obj1_propSet_prop - # obj1.obj - obj1_inner_obj_mock = MagicMock() - obj1_obj_prop = PropertyMock(return_value=obj1_inner_obj_mock) - type(obj1_mock).obj = obj1_obj_prop - # obj2 - obj2_mock = MagicMock() - # obj2.propSet - obj2_propSet_prop = PropertyMock(return_value=[]) - type(obj2_mock).propSet = obj2_propSet_prop - # obj2.obj - obj2_inner_obj_mock = MagicMock() - obj2_obj_prop = PropertyMock(return_value=obj2_inner_obj_mock) - type(obj2_mock).obj = obj2_obj_prop - - get_content = MagicMock(return_value=[obj1_mock, obj2_mock]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=False) - self.assertEqual(obj1_propSet_prop.call_count, 1) - self.assertEqual(obj2_propSet_prop.call_count, 1) - self.assertEqual(obj1_obj_prop.call_count, 1) - self.assertEqual(obj2_obj_prop.call_count, 1) - self.assertEqual(len(ret), 2) - self.assertDictEqual(ret[0], {'object': obj1_inner_obj_mock}) - self.assertDictEqual(ret[1], {'object': obj2_inner_obj_mock}) - - def test_one_elem_one_property(self): - obj_mock = MagicMock() - - # property mock - prop_set_obj_mock = MagicMock() - prop_set_obj_name_prop = PropertyMock(return_value='prop_name') - prop_set_obj_val_prop = PropertyMock(return_value='prop_value') - type(prop_set_obj_mock).name = prop_set_obj_name_prop - type(prop_set_obj_mock).val = prop_set_obj_val_prop - - # obj.propSet - propSet_prop = PropertyMock(return_value=[prop_set_obj_mock]) - type(obj_mock).propSet = propSet_prop - - # obj.obj - inner_obj_mock = MagicMock() - obj_prop = PropertyMock(return_value=inner_obj_mock) - type(obj_mock).obj = obj_prop - - get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec, - local_properties=False) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=False) - self.assertEqual(propSet_prop.call_count, 1) - self.assertEqual(prop_set_obj_name_prop.call_count, 1) - self.assertEqual(prop_set_obj_val_prop.call_count, 1) - self.assertEqual(obj_prop.call_count, 1) - self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'prop_name': 'prop_value', - 'object': inner_obj_mock}) - - def test_one_elem_multiple_properties(self): - obj_mock = MagicMock() - - # property1 mock - prop_set_obj1_mock = MagicMock() - prop_set_obj1_name_prop = PropertyMock(return_value='prop_name1') - prop_set_obj1_val_prop = PropertyMock(return_value='prop_value1') - type(prop_set_obj1_mock).name = prop_set_obj1_name_prop - type(prop_set_obj1_mock).val = prop_set_obj1_val_prop - - # property2 mock - prop_set_obj2_mock = MagicMock() - prop_set_obj2_name_prop = PropertyMock(return_value='prop_name2') - prop_set_obj2_val_prop = PropertyMock(return_value='prop_value2') - type(prop_set_obj2_mock).name = prop_set_obj2_name_prop - type(prop_set_obj2_mock).val = prop_set_obj2_val_prop - - # obj.propSet - propSet_prop = PropertyMock(return_value=[prop_set_obj1_mock, - prop_set_obj2_mock]) - type(obj_mock).propSet = propSet_prop - - # obj.obj - inner_obj_mock = MagicMock() - obj_prop = PropertyMock(return_value=inner_obj_mock) - type(obj_mock).obj = obj_prop - - get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): - ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) - get_content.assert_called_once_with( - self.si, self.obj_type, - property_list=self.prop_list, - container_ref=self.container_ref, - traversal_spec=self.traversal_spec, - local_properties=False) - self.assertEqual(propSet_prop.call_count, 1) - self.assertEqual(prop_set_obj1_name_prop.call_count, 1) - self.assertEqual(prop_set_obj1_val_prop.call_count, 1) - self.assertEqual(prop_set_obj2_name_prop.call_count, 1) - self.assertEqual(prop_set_obj2_val_prop.call_count, 1) - self.assertEqual(obj_prop.call_count, 1) - self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'prop_name1': 'prop_value1', - 'prop_name2': 'prop_value2', - 'object': inner_obj_mock}) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=MagicMock())) -@patch('salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec', - MagicMock(return_value=MagicMock())) -@patch('salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec', - MagicMock(return_value=MagicMock())) -@patch('salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec', - MagicMock(return_value=MagicMock())) -class GetContentTestCase(TestCase): - '''Tests for salt.utils.get_content''' - - # Method names to be patched - traversal_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' - property_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec' - obj_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec' - filter_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec' - - # Class variables - si_mock = None - root_folder_mock = None - root_folder_prop = None - container_view_mock = None - create_container_view_mock = None - result_mock = None - retrieve_contents_mock = None - destroy_mock = None - obj_type_mock = None - traversal_spec_ret_mock = None - traversal_spec_mock = None - property_spec_ret_mock = None - property_spec_mock = None - obj_spec_ret_mock = None - obj_spec_mock = None - filter_spec_ret_mock = None - filter_spec_mock = None - - def setUp(self): - # setup the service instance - self.si_mock = MagicMock() - # RootFolder - self.root_folder_mock = MagicMock() - self.root_folder_prop = PropertyMock(return_value=self.root_folder_mock) - type(self.si_mock.content).rootFolder = self.root_folder_prop - # CreateContainerView() - self.container_view_mock = MagicMock() - self.create_container_view_mock = \ - MagicMock(return_value=self.container_view_mock) - self.si_mock.content.viewManager.CreateContainerView = \ - self.create_container_view_mock - # RetrieveContents() - self.result_mock = MagicMock() - self.retrieve_contents_mock = MagicMock(return_value=self.result_mock) - self.si_mock.content.propertyCollector.RetrieveContents = \ - self.retrieve_contents_mock - # Destroy() - self.destroy_mock = MagicMock() - self.container_view_mock.Destroy = self.destroy_mock - - # override mocks - self.obj_type_mock = MagicMock() - self.traversal_spec_ret_mock = MagicMock() - self.traversal_spec_mock = \ - MagicMock(return_value=self.traversal_spec_ret_mock) - self.property_spec_ret_mock = MagicMock() - self.property_spec_mock = \ - MagicMock(return_value=self.property_spec_ret_mock) - self.obj_spec_ret_mock = MagicMock() - self.obj_spec_mock = \ - MagicMock(return_value=self.obj_spec_ret_mock) - self.filter_spec_ret_mock = MagicMock() - self.filter_spec_mock = \ - MagicMock(return_value=self.filter_spec_ret_mock) - - def test_empty_container_ref(self): - ret = salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(self.root_folder_prop.call_count, 1) - self.create_container_view_mock.assert_called_once_with( - self.root_folder_mock, [self.obj_type_mock], True) - - def test_defined_container_ref(self): - container_ref_mock = MagicMock() - with patch(self.obj_spec_method_name, self.obj_type_mock): - ret = salt.utils.vmware.get_content( - self.si_mock, self.obj_type_mock, - container_ref=container_ref_mock) - self.assertEqual(self.root_folder_prop.call_count, 0) - self.create_container_view_mock.assert_called_once_with( - container_ref_mock, [self.obj_type_mock], True) - - # Also checks destroy is called - def test_local_traversal_spec(self): - with patch(self.traversal_spec_method_name, self.traversal_spec_mock): - with patch(self.obj_spec_method_name, self.obj_spec_mock): - - ret = salt.utils.vmware.get_content(self.si_mock, - self.obj_type_mock) - self.create_container_view_mock.assert_called_once_with( - self.root_folder_mock, [self.obj_type_mock], True) - self.traversal_spec_mock.assert_called_once_with( - name='traverseEntities', path='view', skip=False, - type=vim.view.ContainerView) - self.obj_spec_mock.assert_called_once_with( - obj=self.container_view_mock, - skip=True, - selectSet=[self.traversal_spec_ret_mock]) - # check destroy is called - self.assertEqual(self.destroy_mock.call_count, 1) - - # Also checks destroy is not called - def test_external_traversal_spec(self): - traversal_spec_obj_mock = MagicMock() - with patch(self.traversal_spec_method_name, self.traversal_spec_mock): - with patch(self.obj_spec_method_name, self.obj_spec_mock): - ret = salt.utils.vmware.get_content( - self.si_mock, - self.obj_type_mock, - traversal_spec=traversal_spec_obj_mock) - self.obj_spec_mock.assert_called_once_with( - obj=self.root_folder_mock, - skip=True, - selectSet=[traversal_spec_obj_mock]) - # Check local traversal methods are not called - self.assertEqual(self.create_container_view_mock.call_count, 0) - self.assertEqual(self.traversal_spec_mock.call_count, 0) - # check destroy is not called - self.assertEqual(self.destroy_mock.call_count, 0) - - def test_property_obj_filter_specs_and_contents(self): - with patch(self.traversal_spec_method_name, self.traversal_spec_mock): - with patch(self.property_spec_method_name, self.property_spec_mock): - with patch(self.obj_spec_method_name, self.obj_spec_mock): - with patch(self.filter_spec_method_name, - self.filter_spec_mock): - ret = salt.utils.vmware.get_content( - self.si_mock, - self.obj_type_mock) - self.traversal_spec_mock.assert_called_once_with( - name='traverseEntities', path='view', skip=False, - type=vim.view.ContainerView) - self.property_spec_mock.assert_called_once_with( - type=self.obj_type_mock, all=True, pathSet=None) - self.obj_spec_mock.assert_called_once_with( - obj=self.container_view_mock, skip=True, - selectSet=[self.traversal_spec_ret_mock]) - self.retrieve_contents_mock.assert_called_once_with( - [self.filter_spec_ret_mock]) - self.assertEqual(ret, self.result_mock) - - def test_local_properties_set(self): - container_ref_mock = MagicMock() - with patch(self.traversal_spec_method_name, self.traversal_spec_mock): - with patch(self.property_spec_method_name, self.property_spec_mock): - with patch(self.obj_spec_method_name, self.obj_spec_mock): - salt.utils.vmware.get_content( - self.si_mock, - self.obj_type_mock, - container_ref=container_ref_mock, - local_properties=True) - self.assertEqual(self.traversal_spec_mock.call_count, 0) - self.obj_spec_mock.assert_called_once_with( - obj=container_ref_mock, skip=False, selectSet=None) - - -if __name__ == '__main__': - from integration import run_tests - run_tests(WaitForTaskTestCase, needs_daemon=False) - run_tests(GetMorsWithPropertiesTestCase, needs_daemon=False) - run_tests(GetContentTestCase, needs_daemon=False) diff --git a/tests/unit/utils/vmware_test/connection_test.py b/tests/unit/utils/vmware_test/connection_test.py deleted file mode 100644 index 6ef8ca6604a0..000000000000 --- a/tests/unit/utils/vmware_test/connection_test.py +++ /dev/null @@ -1,702 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: :email:`Alexandru Bleotu ` - - Tests for connections related functions in salt.utils.vmware -''' - -# Import python libraries -from __future__ import absolute_import -import logging -import base64 -import ssl -import sys - -# Import Salt testing libraries -from salttesting import TestCase, skipIf -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, call -import salt.exceptions as excs - -# Import Salt libraries -import salt.utils.vmware -# Import Third Party Libs -try: - from pyVmomi import vim - HAS_PYVMOMI = True -except ImportError: - HAS_PYVMOMI = False - -try: - import gssapi - HAS_GSSAPI = True -except ImportError: - HAS_GSSAPI = False - -if sys.version_info[:3] > (2, 7, 8): - SSL_VALIDATION = True -else: - SSL_VALIDATION = False - -# Get Logging Started -log = logging.getLogger(__name__) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') -@patch('gssapi.Name', MagicMock(return_value='service')) -@patch('gssapi.InitContext', MagicMock()) -class GssapiTokenTest(TestCase): - ''' - Test cases for salt.utils.vmware.get_gssapi_token - ''' - - @patch('salt.utils.vmware.HAS_GSSAPI', False) - def test_no_gssapi(self): - with self.assertRaises(ImportError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', 'domain') - self.assertIn('The gssapi library is not imported.', - excinfo.exception.message) - - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') - def test_service_name(self): - mock_name = MagicMock() - with patch.object(salt.utils.vmware.gssapi, 'Name', mock_name): - - with self.assertRaises(excs.CommandExecutionError): - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') - mock_name.assert_called_once_with('principal/host@domain', - gssapi.C_NT_USER_NAME) - - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') - def test_out_token_defined(self): - mock_context = MagicMock(return_value=MagicMock()) - mock_context.return_value.established = False - mock_context.return_value.step = MagicMock(return_value='out_token') - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): - ret = salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') - self.assertEqual(mock_context.return_value.step.called, 1) - self.assertEqual(ret, base64.b64encode('out_token')) - - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') - def test_out_token_undefined(self): - mock_context = MagicMock(return_value=MagicMock()) - mock_context.return_value.established = False - mock_context.return_value.step = MagicMock(return_value=None) - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') - self.assertEqual(mock_context.return_value.step.called, 1) - self.assertIn('Can\'t receive token', - excinfo.exception.strerror) - - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') - def test_context_extablished(self): - mock_context = MagicMock(return_value=MagicMock()) - mock_context.return_value.established = True - mock_context.return_value.step = MagicMock(return_value='out_token') - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): - mock_context.established = True - mock_context.step = MagicMock(return_value=None) - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') - self.assertEqual(mock_context.step.called, 0) - self.assertIn('Context established, but didn\'t receive token', - excinfo.exception.strerror) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.SmartConnect', MagicMock()) -@patch('salt.utils.vmware.Disconnect', MagicMock()) -@patch('salt.utils.vmware.get_gssapi_token', - MagicMock(return_value='fake_token')) -class PrivateGetServiceInstanceTestCase(TestCase): - '''Tests for salt.utils.vmware._get_service_instance''' - - def test_invalid_mechianism(self): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='invalid_mechanism', - principal='fake principal', - domain='fake_domain') - self.assertIn('Unsupported mechanism', excinfo.exception.strerror) - - def test_userpass_mechanism_empty_username(self): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username=None, - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - self.assertIn('mandatory parameter \'username\'', - excinfo.exception.strerror) - - def test_userpass_mechanism_empty_password(self): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password=None, - protocol='fake_protocol', - port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - self.assertIn('mandatory parameter \'password\'', - excinfo.exception.strerror) - - def test_userpass_mechanism_no_domain(self): - mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='userpass', - principal='fake principal', - domain=None) - mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token=None, - mechanism='userpass') - - def test_userpass_mech_domain_unused(self): - mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username@domain', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username@domain', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token=None, - mechanism='userpass') - mock_sc.reset_mock() - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='domain\\fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='domain\\fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token=None, - mechanism='userpass') - - def test_sspi_empty_principal(self): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal=None, - domain='fake_domain') - self.assertIn('mandatory parameters are missing', - excinfo.exception.strerror) - - def test_sspi_empty_domain(self): - with self.assertRaises(excs.CommandExecutionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain=None) - self.assertIn('mandatory parameters are missing', - excinfo.exception.strerror) - - def test_sspi_get_token_error(self): - mock_token = MagicMock(side_effect=Exception('Exception')) - - with patch('salt.utils.vmware.get_gssapi_token', mock_token): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - mock_token.assert_called_once_with('fake_principal', - 'fake_host.fqdn', - 'fake_domain') - self.assertEqual('Exception', excinfo.exception.strerror) - - def test_sspi_get_token_success_(self): - mock_token = MagicMock(return_value='fake_token') - mock_sc = MagicMock() - - with patch('salt.utils.vmware.get_gssapi_token', mock_token): - with patch('salt.utils.vmware.SmartConnect', mock_sc): - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - mock_token.assert_called_once_with('fake_principal', - 'fake_host.fqdn', - 'fake_domain') - mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token='fake_token', - mechanism='sspi') - - def test_first_attempt_successful_connection(self): - mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token='fake_token', - mechanism='sspi') - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_second_attempt_successful_connection(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - mock_sc = MagicMock(side_effect=[exc, None]) - mock_ssl = MagicMock() - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with patch('ssl._create_unverified_context', - mock_ssl): - - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - mock_ssl.assert_called_once_with() - calls = [call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token='fake_token', - mechanism='sspi'), - call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl.return_value, - b64token='fake_token', - mechanism='sspi')] - mock_sc.assert_has_calls(calls) - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_third_attempt_successful_connection(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') - mock_sc = MagicMock(side_effect=[exc, exc2, None]) - mock_ssl_unverif = MagicMock() - mock_ssl_context = MagicMock() - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with patch('ssl._create_unverified_context', - mock_ssl_unverif): - - with patch('ssl.SSLContext', mock_ssl_context): - - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - mock_ssl_context.assert_called_once_with(ssl.PROTOCOL_TLSv1) - mock_ssl_unverif.assert_called_once_with() - calls = [call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - b64token='fake_token', - mechanism='sspi'), - call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl_unverif.return_value, - b64token='fake_token', - mechanism='sspi'), - call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl_context.return_value, - b64token='fake_token', - mechanism='sspi'), - ] - mock_sc.assert_has_calls(calls) - - def test_first_attempt_unsuccessful_connection_default_error(self): - exc = Exception('Exception') - mock_sc = MagicMock(side_effect=exc) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 1) - self.assertIn('Could not connect to host \'fake_host.fqdn\'', - excinfo.Exception.message) - - def test_first_attempt_unsuccessful_connection_vim_fault(self): - exc = vim.fault.VimFault() - exc.msg = 'VimFault' - mock_sc = MagicMock(side_effect=exc) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 1) - self.assertEqual('VimFault', excinfo.Exception.message) - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_second_attempt_unsuccsessful_connection_default_error(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('Exception') - mock_sc = MagicMock(side_effect=[exc, exc2]) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 2) - self.assertIn('Could not connect to host \'fake_host.fqdn\'', - excinfo.Exception.message) - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_second_attempt_unsuccsessful_connection_vim_fault(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = vim.fault.VimFault() - exc2.msg = 'VimFault' - mock_sc = MagicMock(side_effect=[exc, exc2]) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 2) - self.assertIn('VimFault', excinfo.Exception.message) - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_third_attempt_unsuccessful_connection_detault_error(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') - exc3 = Exception('Exception') - mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 3) - self.assertIn('Exception', excinfo.Exception.message) - - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') - @patch('ssl.SSLContext', MagicMock()) - @patch('ssl._create_unverified_context', MagicMock()) - def test_third_attempt_unsuccessful_connection_vim_fault(self): - exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') - exc3 = vim.fault.VimFault() - exc3.msg = 'VimFault' - mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) - - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with self.assertRaises(excs.VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - - self.assertEqual(mock_sc.call_count, 3) - self.assertIn('VimFault', excinfo.Exception.message) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.GetSi', MagicMock(return_value=None)) -@patch('salt.utils.vmware._get_service_instance', - MagicMock(return_value=MagicMock())) -class GetServiceInstanceTestCase(TestCase): - '''Tests for salt.utils.vmware.get_service_instance''' - - def test_default_params(self): - mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - salt.utils.vmware.get_service_instance( - host='fake_host' - ) - mock_get_si.assert_called_once_with('fake_host', None, None, - 'https', 443, 'userpass', None, - None) - - @patch('salt.utils.is_proxy', MagicMock(return_value=True)) - def test_no_cached_service_instance_same_host_on_proxy(self): - # Service instance is uncached when using class default mock objs - mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' - ) - mock_get_si.assert_called_once_with('fake_host', - 'fake_username', - 'fake_password', - 'fake_protocol', - 1, - 'fake_mechanism', - 'fake_principal', - 'fake_domain') - - def test_cached_service_instance_different_host(self): - mock_si = MagicMock() - mock_si_stub = MagicMock() - mock_disconnect = MagicMock() - mock_get_si = MagicMock(return_value=mock_si) - mock_getstub = MagicMock() - with patch('salt.utils.vmware.GetSi', mock_get_si): - with patch('salt.utils.vmware.GetStub', mock_getstub): - with patch('salt.utils.vmware.Disconnect', mock_disconnect): - salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' - ) - self.assertEqual(mock_get_si.call_count, 1) - self.assertEqual(mock_getstub.call_count, 1) - self.assertEqual(mock_disconnect.call_count, 1) - - def test_uncached_service_instance(self): - # Service instance is uncached when using class default mock objs - mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' - ) - mock_get_si.assert_called_once_with('fake_host', - 'fake_username', - 'fake_password', - 'fake_protocol', - 1, - 'fake_mechanism', - 'fake_principal', - 'fake_domain') - - def test_unauthenticated_service_instance(self): - mock_si_current_time = MagicMock(side_effect=vim.fault.NotAuthenticated) - mock_si = MagicMock() - mock_get_si = MagicMock(return_value=mock_si) - mock_si.CurrentTime = mock_si_current_time - mock_disconnect = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - with patch('salt.utils.vmware.Disconnect', mock_disconnect): - salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', - port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' - ) - self.assertEqual(mock_si_current_time.call_count, 1) - self.assertEqual(mock_disconnect.call_count, 1) - self.assertEqual(mock_get_si.call_count, 2) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -class IsConnectionToAVCenterTestCase(TestCase): - '''Tests for salt.utils.vmware.is_connection_to_a_vcenter''' - - def test_connected_to_a_vcenter(self): - mock_si = MagicMock() - mock_si.content.about.apiType = 'VirtualCenter' - - ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertTrue(ret) - - def test_connected_to_a_host(self): - mock_si = MagicMock() - mock_si.content.about.apiType = 'HostAgent' - - ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertFalse(ret) - - def test_connected_to_invalid_entity(self): - mock_si = MagicMock() - mock_si.content.about.apiType = 'UnsupportedType' - - with self.assertRaises(excs.VMwareApiError) as excinfo: - salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertIn('Unexpected api type \'UnsupportedType\'', - excinfo.exception.strerror) - -if __name__ == '__main__': - from integration import run_tests - run_tests(GssapiTokenTest, needs_daemon=False) - run_tests(PrivateGetServiceInstanceTestCase, needs_daemon=False) - run_tests(GetServiceInstanceTestCase, needs_daemon=False) - run_tests(IsConnectionToAVCenterTestCase, needs_daemon=False) From 36edf0af6426118a6cec17da40524c3a85855cb6 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Thu, 30 Mar 2017 00:54:31 -0400 Subject: [PATCH 07/17] Add additional VMware related exceptions --- salt/exceptions.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/salt/exceptions.py b/salt/exceptions.py index 06e3d1088e6c..bd396aca898d 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -386,6 +386,13 @@ class VMwareSaltError(CommandExecutionError): ''' +class VMwareRuntimeError(VMwareSaltError): + ''' + Used when a runtime error is encountered when communicating with the + vCenter + ''' + + class VMwareConnectionError(VMwareSaltError): ''' Used when the client fails to connect to a either a VMware vCenter server or @@ -393,7 +400,19 @@ class VMwareConnectionError(VMwareSaltError): ''' -class VMwareApiError(VMwareSaltError): +class VMwareObjectRetrievalError(VMwareSaltError): ''' Used when a VMware object cannot be retrieved ''' + + +class VMwareApiError(VMwareSaltError): + ''' + Used when representing a generic VMware API error + ''' + + +class VMwareSystemError(VMwareSaltError): + ''' + Used when representing a generic VMware system error + ''' From dd62310941c38a2207ee3dad2881c30f16f224d8 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Thu, 30 Mar 2017 00:54:57 -0400 Subject: [PATCH 08/17] Adding unit tests for vmware_test --- tests/unit/utils/vmware_test/test_cluster.py | 249 ++++++ tests/unit/utils/vmware_test/test_common.py | 800 +++++++++++++++++ .../unit/utils/vmware_test/test_connection.py | 837 ++++++++++++++++++ .../unit/utils/vmware_test/test_datacenter.py | 183 ++++ tests/unit/utils/vmware_test/test_host.py | 157 ++++ 5 files changed, 2226 insertions(+) create mode 100644 tests/unit/utils/vmware_test/test_cluster.py create mode 100644 tests/unit/utils/vmware_test/test_common.py create mode 100644 tests/unit/utils/vmware_test/test_connection.py create mode 100644 tests/unit/utils/vmware_test/test_datacenter.py create mode 100644 tests/unit/utils/vmware_test/test_host.py diff --git a/tests/unit/utils/vmware_test/test_cluster.py b/tests/unit/utils/vmware_test/test_cluster.py new file mode 100644 index 000000000000..2ea3ec6956ac --- /dev/null +++ b/tests/unit/utils/vmware_test/test_cluster.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +''' +:codeauthor: :email:`Alexandru Bleotu ` + +Tests for cluster related functions in salt.utils.vmware +''' + +# Import python libraries +from __future__ import absolute_import +import logging +# Import Salt testing libraries +from salttesting import TestCase, skipIf +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, call +# Import Salt libraries +from salt.exceptions import VMwareApiError, VMwareRuntimeError, \ + VMwareObjectRetrievalError +import salt.utils.vmware as vmware +# Import Third Party Libs +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +# Get Logging Started +log = logging.getLogger(__name__) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_managed_object_name', + MagicMock()) +@patch('salt.utils.vmware.get_service_instance_from_managed_object', + MagicMock()) +@patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[{'name': 'fake_cluster', + 'object': MagicMock()}])) +class GetClusterTestCase(TestCase): + '''Tests for salt.utils.vmware.get_cluster''' + + def setUp(self): + self.mock_si = MagicMock() + self.mock_dc = MagicMock() + self.mock_cluster1 = MagicMock() + self.mock_cluster2 = MagicMock() + self.mock_entries = [{'name': 'fake_cluster1', + 'object': self.mock_cluster1}, + {'name': 'fake_cluster2', + 'object': self.mock_cluster2}] + + def test_get_managed_object_name_call(self): + mock_get_managed_object_name = MagicMock() + with patch('salt.utils.vmware.get_managed_object_name', + mock_get_managed_object_name): + vmware.get_cluster(self.mock_dc, 'fake_cluster') + mock_get_managed_object_name.assert_called_once_with(self.mock_dc) + + def test_get_service_instance_from_managed_object(self): + mock_dc_name = MagicMock() + mock_get_service_instance_from_managed_object = MagicMock() + with patch('salt.utils.vmware.get_managed_object_name', + MagicMock(return_value=mock_dc_name)): + with patch( + 'salt.utils.vmware.get_service_instance_from_managed_object', + mock_get_service_instance_from_managed_object): + + vmware.get_cluster(self.mock_dc, 'fake_cluster') + mock_get_service_instance_from_managed_object.assert_called_once_with( + self.mock_dc, name=mock_dc_name) + + def test_traversal_spec_init(self): + mock_dc_name = MagicMock() + mock_traversal_spec = MagicMock() + mock_traversal_spec_ini = MagicMock(return_value=mock_traversal_spec) + mock_get_service_instance_from_managed_object = MagicMock() + patch_traversal_spec_str = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' + + with patch(patch_traversal_spec_str, mock_traversal_spec_ini): + vmware.get_cluster(self.mock_dc, 'fake_cluster') + mock_traversal_spec_ini.assert_has_calls( + [call(path='childEntity', + skip=False, + type=vim.Folder), + call(path='hostFolder', + skip=True, + type=vim.Datacenter, + selectSet=[mock_traversal_spec])]) + + def test_get_mors_with_properties_call(self): + mock_get_mors_with_properties = MagicMock( + return_value=[{'name': 'fake_cluster', 'object': MagicMock()}]) + mock_traversal_spec = MagicMock() + patch_traversal_spec_str = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' + with patch( + 'salt.utils.vmware.get_service_instance_from_managed_object', + MagicMock(return_value=self.mock_si)): + + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors_with_properties): + with patch(patch_traversal_spec_str, + MagicMock(return_value=mock_traversal_spec)): + + vmware.get_cluster(self.mock_dc, 'fake_cluster') + mock_get_mors_with_properties.assert_called_once_with( + self.mock_si, vim.ClusterComputeResource, + container_ref=self.mock_dc, + property_list=['name'], + traversal_spec=mock_traversal_spec) + + def test_get_mors_with_properties_returns_empty_array(self): + with patch('salt.utils.vmware.get_managed_object_name', + MagicMock(return_value='fake_dc')): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[])): + with self.assertRaises(VMwareObjectRetrievalError) as excinfo: + vmware.get_cluster(self.mock_dc, 'fake_cluster') + self.assertEqual(excinfo.exception.strerror, + 'Cluster \'fake_cluster\' was not found in ' + 'datacenter \'fake_dc\'') + + def test_cluster_not_found(self): + with patch('salt.utils.vmware.get_managed_object_name', + MagicMock(return_value='fake_dc')): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + with self.assertRaises(VMwareObjectRetrievalError) as excinfo: + vmware.get_cluster(self.mock_dc, 'fake_cluster') + self.assertEqual(excinfo.exception.strerror, + 'Cluster \'fake_cluster\' was not found in ' + 'datacenter \'fake_dc\'') + + def test_cluster_found(self): + with patch('salt.utils.vmware.get_managed_object_name', + MagicMock(return_value='fake_dc')): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + res = vmware.get_cluster(self.mock_dc, 'fake_cluster2') + self.assertEqual(res, self.mock_cluster2) + + +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) +class CreateClusterTestCase(TestCase): + '''Tests for salt.utils.vmware.create_cluster''' + + def setUp(self): + self.mock_create_cluster_ex = MagicMock() + self.mock_dc = MagicMock( + hostFolder=MagicMock(CreateClusterEx=self.mock_create_cluster_ex)) + self.mock_cluster_spec = MagicMock() + + def test_get_managed_object_name(self): + mock_get_managed_object_name = MagicMock() + with patch('salt.utils.vmware.get_managed_object_name', + mock_get_managed_object_name): + vmware.create_cluster(self.mock_dc, 'fake_cluster', + self.mock_cluster_spec) + mock_get_managed_object_name.assert_called_once_with(self.mock_dc) + + def test_create_cluster_call(self): + vmware.create_cluster(self.mock_dc, 'fake_cluster', + self.mock_cluster_spec) + self.mock_create_cluster_ex.assert_called_once_with( + 'fake_cluster', self.mock_cluster_spec) + + def test_create_cluster_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + self.mock_dc.hostFolder.CreateClusterEx = MagicMock( + side_effect=exc) + with self.assertRaises(VMwareApiError) as excinfo: + vmware.create_cluster(self.mock_dc, 'fake_cluster', + self.mock_cluster_spec) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_create_cluster_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.mock_dc.hostFolder.CreateClusterEx = MagicMock( + side_effect=exc) + with self.assertRaises(VMwareRuntimeError) as excinfo: + vmware.create_cluster(self.mock_dc, 'fake_cluster', + self.mock_cluster_spec) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) +@patch('salt.utils.vmware.wait_for_task', MagicMock()) +class UpdateClusterTestCase(TestCase): + '''Tests for salt.utils.vmware.update_cluster''' + + def setUp(self): + self.mock_task = MagicMock() + self.mock_reconfigure_compute_resource_task = \ + MagicMock(return_value=self.mock_task) + self.mock_cluster = MagicMock(ReconfigureComputeResource_Task= + self.mock_reconfigure_compute_resource_task) + self.mock_cluster_spec = MagicMock() + + def test_get_managed_object_name(self): + mock_get_managed_object_name = MagicMock() + with patch('salt.utils.vmware.get_managed_object_name', + mock_get_managed_object_name): + vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) + mock_get_managed_object_name.assert_called_once_with(self.mock_cluster) + + def test_reconfigure_compute_resource_task_call(self): + vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) + self.mock_reconfigure_compute_resource_task.assert_called_once_with( + self.mock_cluster_spec, modify=True) + + def test_reconfigure_compute_resource_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + self.mock_cluster.ReconfigureComputeResource_Task = \ + MagicMock(side_effect=exc) + with self.assertRaises(VMwareApiError) as excinfo: + vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_reconfigure_compute_resource_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.mock_cluster.ReconfigureComputeResource_Task = \ + MagicMock(side_effect=exc) + with self.assertRaises(VMwareRuntimeError) as excinfo: + vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_wait_for_task_call(self): + mock_wait_for_task = MagicMock() + with patch('salt.utils.vmware.get_managed_object_name', + MagicMock(return_value='fake_cluster')): + with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): + vmware.update_cluster(self.mock_cluster, + self.mock_cluster_spec) + mock_wait_for_task.assert_called_once_with( + self.mock_task, 'fake_cluster', 'ClusterUpdateTask') + + +if __name__ == '__main__': + from integration import run_tests + run_tests(GetClusterTestCase, needs_daemon=False) + run_tests(CreateClusterTestCase, needs_daemon=False) + run_tests(UpdateClusterTestCase, needs_daemon=False) diff --git a/tests/unit/utils/vmware_test/test_common.py b/tests/unit/utils/vmware_test/test_common.py new file mode 100644 index 000000000000..9a8c87b9071a --- /dev/null +++ b/tests/unit/utils/vmware_test/test_common.py @@ -0,0 +1,800 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Alexandru Bleotu ` + + Tests for common functions in salt.utils.vmware +''' + +# Import python libraries +from __future__ import absolute_import +import logging + +# Import Salt testing libraries +from salttesting import TestCase, skipIf +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, call, \ + PropertyMock + +# Import Salt libraries +import salt.exceptions as excs +import salt.utils.vmware +# Import Third Party Libs +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +# Get Logging Started +log = logging.getLogger(__name__) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.time.time', MagicMock(return_value=1)) +@patch('salt.utils.vmware.time.sleep', MagicMock(return_value=None)) +class WaitForTaskTestCase(TestCase): + '''Tests for salt.utils.vmware.wait_for_task''' + + def test_first_task_info_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + mock_task = MagicMock() + type(mock_task).info = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_first_task_info_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + mock_task = MagicMock() + type(mock_task).info = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_inner_loop_task_info_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + mock_task = MagicMock() + mock_info1 = MagicMock() + type(mock_task).info = PropertyMock( + side_effect=[mock_info1, exc]) + type(mock_info1).state = PropertyMock(side_effect=['running', 'bad']) + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_inner_loop_task_info_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + mock_task = MagicMock() + mock_info1 = MagicMock() + type(mock_task).info = PropertyMock( + side_effect=[mock_info1, exc]) + type(mock_info1).state = PropertyMock(side_effect=['running', 'bad']) + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_info_state_running(self): + # The 'bad' values are invalid in the while loop + mock_task = MagicMock() + prop_mock_state = PropertyMock(side_effect=['running', 'bad', 'bad', + 'success']) + prop_mock_result = PropertyMock() + type(mock_task.info).state = prop_mock_state + type(mock_task.info).result = prop_mock_result + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(prop_mock_state.call_count, 4) + self.assertEqual(prop_mock_result.call_count, 1) + + def test_info_state_running_continues_loop(self): + mock_task = MagicMock() + # The 'fake' values are required to match all the lookups and end the + # loop + prop_mock_state = PropertyMock(side_effect=['running', 'fake', 'fake', + 'success']) + prop_mock_result = PropertyMock() + type(mock_task.info).state = prop_mock_state + type(mock_task.info).result = prop_mock_result + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(prop_mock_state.call_count, 4) + self.assertEqual(prop_mock_result.call_count, 1) + + def test_info_state_queued_continues_loop(self): + mock_task = MagicMock() + # The 'fake' values are required to match all the lookups and end the + # loop + prop_mock_state = PropertyMock(side_effect=['fake', 'queued', 'fake', + 'fake', 'success']) + prop_mock_result = PropertyMock() + type(mock_task.info).state = prop_mock_state + type(mock_task.info).result = prop_mock_result + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(prop_mock_state.call_count, 5) + self.assertEqual(prop_mock_result.call_count, 1) + + def test_info_state_success(self): + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='success') + prop_mock_result = PropertyMock() + type(mock_task.info).state = prop_mock_state + type(mock_task.info).result = prop_mock_result + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(prop_mock_state.call_count, 3) + self.assertEqual(prop_mock_result.call_count, 1) + + def test_info_error_exception(self): + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='error') + prop_mock_error = PropertyMock(side_effect=Exception('error exc')) + type(mock_task.info).state = prop_mock_state + type(mock_task.info).error = prop_mock_error + with self.assertRaises(Exception) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.message, 'error exc') + + def test_info_error_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='error') + prop_mock_error = PropertyMock(side_effect=exc) + type(mock_task.info).state = prop_mock_state + type(mock_task.info).error = prop_mock_error + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_info_error_system_fault(self): + exc = vmodl.fault.SystemError() + exc.msg = 'SystemError msg' + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='error') + prop_mock_error = PropertyMock(side_effect=exc) + type(mock_task.info).state = prop_mock_state + type(mock_task.info).error = prop_mock_error + with self.assertRaises(excs.VMwareSystemError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, 'SystemError msg') + + def test_info_error_invalid_argument_no_fault_message(self): + exc = vmodl.fault.InvalidArgument() + exc.faultMessage = None + exc.msg = 'InvalidArgumentFault msg' + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='error') + prop_mock_error = PropertyMock(side_effect=exc) + type(mock_task.info).state = prop_mock_state + type(mock_task.info).error = prop_mock_error + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, + 'InvalidArgumentFault msg') + + def test_info_error_invalid_argument_with_fault_message(self): + exc = vmodl.fault.InvalidArgument() + fault_message = vim.LocalizableMessage() + fault_message.message = 'LocalFault msg' + exc.faultMessage = [fault_message] + exc.msg = 'InvalidArgumentFault msg' + mock_task = MagicMock() + prop_mock_state = PropertyMock(return_value='error') + prop_mock_error = PropertyMock(side_effect=exc) + type(mock_task.info).state = prop_mock_state + type(mock_task.info).error = prop_mock_error + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.wait_for_task(mock_task, + 'fake_instance_name', + 'task_type') + self.assertEqual(excinfo.exception.strerror, + 'InvalidArgumentFault msg (LocalFault msg)') + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +class GetMorsWithPropertiesTestCase(TestCase): + '''Tests for salt.utils.get_mors_with_properties''' + + si = None + obj_type = None + prop_list = None + container_ref = None + traversal_spec = None + + def setUp(self): + self.si = MagicMock() + self.obj_type = MagicMock() + self.prop_list = MagicMock() + self.container_ref = MagicMock() + self.traversal_spec = MagicMock() + + def test_empty_content(self): + get_content = MagicMock(return_value=[]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=False) + self.assertEqual(ret, []) + + def test_local_properties_set(self): + obj_mock = MagicMock() + # obj.propSet + propSet_prop = PropertyMock(return_value=[]) + type(obj_mock).propSet = propSet_prop + # obj.obj + inner_obj_mock = MagicMock() + obj_prop = PropertyMock(return_value=inner_obj_mock) + type(obj_mock).obj = obj_prop + + get_content = MagicMock(return_value=[obj_mock]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec, + local_properties=True) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=True) + + def test_one_element_content(self): + obj_mock = MagicMock() + # obj.propSet + propSet_prop = PropertyMock(return_value=[]) + type(obj_mock).propSet = propSet_prop + # obj.obj + inner_obj_mock = MagicMock() + obj_prop = PropertyMock(return_value=inner_obj_mock) + type(obj_mock).obj = obj_prop + get_content = MagicMock(return_value=[obj_mock]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=False) + self.assertEqual(propSet_prop.call_count, 1) + self.assertEqual(obj_prop.call_count, 1) + self.assertEqual(len(ret), 1) + self.assertDictEqual(ret[0], {'object': inner_obj_mock}) + + def test_multiple_element_content(self): + # obj1 + obj1_mock = MagicMock() + # obj1.propSet + obj1_propSet_prop = PropertyMock(return_value=[]) + type(obj1_mock).propSet = obj1_propSet_prop + # obj1.obj + obj1_inner_obj_mock = MagicMock() + obj1_obj_prop = PropertyMock(return_value=obj1_inner_obj_mock) + type(obj1_mock).obj = obj1_obj_prop + # obj2 + obj2_mock = MagicMock() + # obj2.propSet + obj2_propSet_prop = PropertyMock(return_value=[]) + type(obj2_mock).propSet = obj2_propSet_prop + # obj2.obj + obj2_inner_obj_mock = MagicMock() + obj2_obj_prop = PropertyMock(return_value=obj2_inner_obj_mock) + type(obj2_mock).obj = obj2_obj_prop + + get_content = MagicMock(return_value=[obj1_mock, obj2_mock]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=False) + self.assertEqual(obj1_propSet_prop.call_count, 1) + self.assertEqual(obj2_propSet_prop.call_count, 1) + self.assertEqual(obj1_obj_prop.call_count, 1) + self.assertEqual(obj2_obj_prop.call_count, 1) + self.assertEqual(len(ret), 2) + self.assertDictEqual(ret[0], {'object': obj1_inner_obj_mock}) + self.assertDictEqual(ret[1], {'object': obj2_inner_obj_mock}) + + def test_one_elem_one_property(self): + obj_mock = MagicMock() + + # property mock + prop_set_obj_mock = MagicMock() + prop_set_obj_name_prop = PropertyMock(return_value='prop_name') + prop_set_obj_val_prop = PropertyMock(return_value='prop_value') + type(prop_set_obj_mock).name = prop_set_obj_name_prop + type(prop_set_obj_mock).val = prop_set_obj_val_prop + + # obj.propSet + propSet_prop = PropertyMock(return_value=[prop_set_obj_mock]) + type(obj_mock).propSet = propSet_prop + + # obj.obj + inner_obj_mock = MagicMock() + obj_prop = PropertyMock(return_value=inner_obj_mock) + type(obj_mock).obj = obj_prop + + get_content = MagicMock(return_value=[obj_mock]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec, + local_properties=False) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=False) + self.assertEqual(propSet_prop.call_count, 1) + self.assertEqual(prop_set_obj_name_prop.call_count, 1) + self.assertEqual(prop_set_obj_val_prop.call_count, 1) + self.assertEqual(obj_prop.call_count, 1) + self.assertEqual(len(ret), 1) + self.assertDictEqual(ret[0], {'prop_name': 'prop_value', + 'object': inner_obj_mock}) + + def test_one_elem_multiple_properties(self): + obj_mock = MagicMock() + + # property1 mock + prop_set_obj1_mock = MagicMock() + prop_set_obj1_name_prop = PropertyMock(return_value='prop_name1') + prop_set_obj1_val_prop = PropertyMock(return_value='prop_value1') + type(prop_set_obj1_mock).name = prop_set_obj1_name_prop + type(prop_set_obj1_mock).val = prop_set_obj1_val_prop + + # property2 mock + prop_set_obj2_mock = MagicMock() + prop_set_obj2_name_prop = PropertyMock(return_value='prop_name2') + prop_set_obj2_val_prop = PropertyMock(return_value='prop_value2') + type(prop_set_obj2_mock).name = prop_set_obj2_name_prop + type(prop_set_obj2_mock).val = prop_set_obj2_val_prop + + # obj.propSet + propSet_prop = PropertyMock(return_value=[prop_set_obj1_mock, + prop_set_obj2_mock]) + type(obj_mock).propSet = propSet_prop + + # obj.obj + inner_obj_mock = MagicMock() + obj_prop = PropertyMock(return_value=inner_obj_mock) + type(obj_mock).obj = obj_prop + + get_content = MagicMock(return_value=[obj_mock]) + with patch('salt.utils.vmware.get_content', get_content): + ret = salt.utils.vmware.get_mors_with_properties( + self.si, self.obj_type, self.prop_list, + self.container_ref, self.traversal_spec) + get_content.assert_called_once_with( + self.si, self.obj_type, + property_list=self.prop_list, + container_ref=self.container_ref, + traversal_spec=self.traversal_spec, + local_properties=False) + self.assertEqual(propSet_prop.call_count, 1) + self.assertEqual(prop_set_obj1_name_prop.call_count, 1) + self.assertEqual(prop_set_obj1_val_prop.call_count, 1) + self.assertEqual(prop_set_obj2_name_prop.call_count, 1) + self.assertEqual(prop_set_obj2_val_prop.call_count, 1) + self.assertEqual(obj_prop.call_count, 1) + self.assertEqual(len(ret), 1) + self.assertDictEqual(ret[0], {'prop_name1': 'prop_value1', + 'prop_name2': 'prop_value2', + 'object': inner_obj_mock}) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_service_instance_from_managed_object', + MagicMock()) +@patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[MagicMock()])) +class GetPropertiesOfManagedObjectTestCase(TestCase): + '''Tests for salt.utils.get_properties_of_managed_object''' + + def setUp(self): + self.mock_si = MagicMock() + self.fake_mo_ref = vim.ManagedEntity('Fake') + self.mock_props = MagicMock() + self.mock_item_name = {'name': 'fake_name'} + self.mock_item = MagicMock() + + def test_get_service_instance_from_managed_object_call(self): + mock_get_instance_from_managed_object = MagicMock() + with patch( + 'salt.utils.vmware.get_service_instance_from_managed_object', + mock_get_instance_from_managed_object): + + salt.utils.vmware.get_properties_of_managed_object( + self.fake_mo_ref, self.mock_props) + mock_get_instance_from_managed_object.assert_called_once_with( + self.fake_mo_ref) + + def test_get_mors_with_properties_calls(self): + mock_get_mors_with_properties = MagicMock(return_value=[MagicMock()]) + with patch( + 'salt.utils.vmware.get_service_instance_from_managed_object', + MagicMock(return_value=self.mock_si)): + + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors_with_properties): + salt.utils.vmware.get_properties_of_managed_object( + self.fake_mo_ref, self.mock_props) + mock_get_mors_with_properties.assert_has_calls( + [call(self.mock_si, vim.ManagedEntity, + container_ref=self.fake_mo_ref, + property_list=['name'], + local_properties=True), + call(self.mock_si, vim.ManagedEntity, + container_ref=self.fake_mo_ref, + property_list=self.mock_props, + local_properties=True)]) + + def test_managed_object_no_name_property(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(side_effect=[vmodl.query.InvalidProperty(), []])): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_properties_of_managed_object( + self.fake_mo_ref, self.mock_props) + self.assertEqual('Properties of managed object \'\' weren\'t ' + 'retrieved', excinfo.exception.strerror) + + def test_no_items_named_object(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(side_effect=[[self.mock_item_name], []])): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_properties_of_managed_object( + self.fake_mo_ref, self.mock_props) + self.assertEqual('Properties of managed object \'fake_name\' weren\'t ' + 'retrieved', excinfo.exception.strerror) + + +@patch('salt.utils.vmware.get_properties_of_managed_object', + MagicMock(return_value={'key': 'value'})) +class GetManagedObjectName(TestCase): + '''Tests for salt.utils.get_managed_object_name''' + + def setUp(self): + self.mock_mo_ref = MagicMock() + + def test_get_properties_of_managed_object_call(self): + mock_get_properties_of_managed_object = MagicMock() + with patch('salt.utils.vmware.get_properties_of_managed_object', + mock_get_properties_of_managed_object): + salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) + mock_get_properties_of_managed_object.assert_called_once_with( + self.mock_mo_ref, ['name']) + + def test_no_name_in_property_dict(self): + ret = salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) + self.assertIsNone(ret) + + def test_return_managed_object_name(self): + mock_get_properties_of_managed_object = MagicMock() + with patch('salt.utils.vmware.get_properties_of_managed_object', + MagicMock(return_value={'name': 'fake_name'})): + ret = salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) + self.assertEqual(ret, 'fake_name') + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_root_folder', MagicMock()) +@patch('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', + MagicMock(return_value=MagicMock())) +@patch('salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec', + MagicMock(return_value=MagicMock())) +@patch('salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec', + MagicMock(return_value=MagicMock())) +@patch('salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec', + MagicMock(return_value=MagicMock())) +class GetContentTestCase(TestCase): + '''Tests for salt.utils.get_content''' + + # Method names to be patched + traversal_spec_method_name = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' + property_spec_method_name = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec' + obj_spec_method_name = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec' + filter_spec_method_name = \ + 'salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec' + + # Class variables + si_mock = None + root_folder_mock = None + root_folder_prop = None + container_view_mock = None + create_container_view_mock = None + result_mock = None + retrieve_contents_mock = None + destroy_mock = None + obj_type_mock = None + traversal_spec_ret_mock = None + traversal_spec_mock = None + property_spec_ret_mock = None + property_spec_mock = None + obj_spec_ret_mock = None + obj_spec_mock = None + filter_spec_ret_mock = None + filter_spec_mock = None + + def setUp(self): + # setup the service instance + self.si_mock = MagicMock() + # RootFolder + self.root_folder_mock = MagicMock() + self.get_root_folder_mock = \ + MagicMock(return_value=self.root_folder_mock) + # CreateContainerView() + self.container_view_mock = MagicMock() + self.create_container_view_mock = \ + MagicMock(return_value=self.container_view_mock) + self.si_mock.content.viewManager.CreateContainerView = \ + self.create_container_view_mock + # RetrieveContents() + self.result_mock = MagicMock() + self.retrieve_contents_mock = MagicMock(return_value=self.result_mock) + self.si_mock.content.propertyCollector.RetrieveContents = \ + self.retrieve_contents_mock + # Destroy() + self.destroy_mock = MagicMock() + self.container_view_mock.Destroy = self.destroy_mock + + # override mocks + self.obj_type_mock = MagicMock() + self.traversal_spec_ret_mock = MagicMock() + self.traversal_spec_mock = \ + MagicMock(return_value=self.traversal_spec_ret_mock) + self.property_spec_ret_mock = MagicMock() + self.property_spec_mock = \ + MagicMock(return_value=self.property_spec_ret_mock) + self.obj_spec_ret_mock = MagicMock() + self.obj_spec_mock = \ + MagicMock(return_value=self.obj_spec_ret_mock) + self.filter_spec_ret_mock = MagicMock() + self.filter_spec_mock = \ + MagicMock(return_value=self.filter_spec_ret_mock) + + def test_empty_container_ref(self): + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.get_root_folder_mock.assert_called_once_with(self.si_mock) + self.create_container_view_mock.assert_called_once_with( + self.root_folder_mock, [self.obj_type_mock], True) + + def test_defined_container_ref(self): + container_ref_mock = MagicMock() + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with patch(self.obj_spec_method_name, self.obj_type_mock): + salt.utils.vmware.get_content( + self.si_mock, self.obj_type_mock, + container_ref=container_ref_mock) + self.assertEqual(self.get_root_folder_mock.call_count, 0) + self.create_container_view_mock.assert_called_once_with( + container_ref_mock, [self.obj_type_mock], True) + + # Also checks destroy is called + def test_local_traversal_spec(self): + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with patch(self.traversal_spec_method_name, + self.traversal_spec_mock): + with patch(self.obj_spec_method_name, self.obj_spec_mock): + ret = salt.utils.vmware.get_content(self.si_mock, + self.obj_type_mock) + self.create_container_view_mock.assert_called_once_with( + self.root_folder_mock, [self.obj_type_mock], True) + self.traversal_spec_mock.assert_called_once_with( + name='traverseEntities', path='view', skip=False, + type=vim.view.ContainerView) + self.obj_spec_mock.assert_called_once_with( + obj=self.container_view_mock, + skip=True, + selectSet=[self.traversal_spec_ret_mock]) + # check destroy is called + self.assertEqual(self.destroy_mock.call_count, 1) + + def test_create_container_view_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + self.si_mock.content.viewManager.CreateContainerView = \ + MagicMock(side_effect=exc) + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_create_container_view_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.si_mock.content.viewManager.CreateContainerView = \ + MagicMock(side_effect=exc) + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_destroy_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + self.si_mock.content.viewManager.CreateContainerView = MagicMock( + return_value=MagicMock(Destroy=MagicMock(side_effect=exc))) + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_destroy_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.si_mock.content.viewManager.CreateContainerView = MagicMock( + return_value=MagicMock(Destroy=MagicMock(side_effect=exc))) + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + # Also checks destroy is not called + def test_external_traversal_spec(self): + traversal_spec_obj_mock = MagicMock() + with patch('salt.utils.vmware.get_root_folder', + self.get_root_folder_mock): + with patch(self.traversal_spec_method_name, + self.traversal_spec_mock): + with patch(self.obj_spec_method_name, self.obj_spec_mock): + salt.utils.vmware.get_content( + self.si_mock, + self.obj_type_mock, + traversal_spec=traversal_spec_obj_mock) + self.obj_spec_mock.assert_called_once_with( + obj=self.root_folder_mock, + skip=True, + selectSet=[traversal_spec_obj_mock]) + # Check local traversal methods are not called + self.assertEqual(self.create_container_view_mock.call_count, 0) + self.assertEqual(self.traversal_spec_mock.call_count, 0) + # check destroy is not called + self.assertEqual(self.destroy_mock.call_count, 0) + + def test_property_obj_filter_specs_and_contents(self): + with patch(self.traversal_spec_method_name, self.traversal_spec_mock): + with patch(self.property_spec_method_name, self.property_spec_mock): + with patch(self.obj_spec_method_name, self.obj_spec_mock): + with patch(self.filter_spec_method_name, + self.filter_spec_mock): + ret = salt.utils.vmware.get_content( + self.si_mock, + self.obj_type_mock) + self.traversal_spec_mock.assert_called_once_with( + name='traverseEntities', path='view', skip=False, + type=vim.view.ContainerView) + self.property_spec_mock.assert_called_once_with( + type=self.obj_type_mock, all=True, pathSet=None) + self.obj_spec_mock.assert_called_once_with( + obj=self.container_view_mock, skip=True, + selectSet=[self.traversal_spec_ret_mock]) + self.retrieve_contents_mock.assert_called_once_with( + [self.filter_spec_ret_mock]) + self.assertEqual(ret, self.result_mock) + + def test_retrieve_contents_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + self.si_mock.content.propertyCollector.RetrieveContents = \ + MagicMock(side_effect=exc) + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_retrieve_contents_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.si_mock.content.propertyCollector.RetrieveContents = \ + MagicMock(side_effect=exc) + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_local_properties_set(self): + container_ref_mock = MagicMock() + with patch(self.traversal_spec_method_name, self.traversal_spec_mock): + with patch(self.property_spec_method_name, self.property_spec_mock): + with patch(self.obj_spec_method_name, self.obj_spec_mock): + salt.utils.vmware.get_content( + self.si_mock, + self.obj_type_mock, + container_ref=container_ref_mock, + local_properties=True) + self.assertEqual(self.traversal_spec_mock.call_count, 0) + self.obj_spec_mock.assert_called_once_with( + obj=container_ref_mock, skip=False, selectSet=None) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +class GetRootFolderTestCase(TestCase): + '''Tests for salt.utils.get_root_folder''' + + def setUp(self): + self.mock_root_folder = MagicMock() + self.mock_content = MagicMock(rootFolder=self.mock_root_folder) + self.mock_si = MagicMock( + RetrieveContent=MagicMock(return_value=self.mock_content)) + + def test_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + type(self.mock_content).rootFolder = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_root_folder(self.mock_si) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + type(self.mock_content).rootFolder = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.get_root_folder(self.mock_si) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_return(self): + ret = salt.utils.vmware.get_root_folder(self.mock_si) + self.assertEqual(ret, self.mock_root_folder) + +if __name__ == '__main__': + from integration import run_tests + run_tests(WaitForTaskTestCase, needs_daemon=False) + run_tests(GetMorsWithPropertiesTestCase, needs_daemon=False) + run_tests(GetPropertiesOfManagedObjectTestCase, needs_daemon=False) + run_tests(GetManagedObjectName, needs_daemon=False) + run_tests(GetContentTestCase, needs_daemon=False) + run_tests(GetRootFolderTestCase, needs_daemon=False) diff --git a/tests/unit/utils/vmware_test/test_connection.py b/tests/unit/utils/vmware_test/test_connection.py new file mode 100644 index 000000000000..76c8a86dba6e --- /dev/null +++ b/tests/unit/utils/vmware_test/test_connection.py @@ -0,0 +1,837 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Alexandru Bleotu ` + + Tests for connections related functions in salt.utils.vmware +''' + +# Import python libraries +from __future__ import absolute_import +import logging +import base64 +import ssl +import sys + +# Import Salt testing libraries +from salttesting import TestCase, skipIf +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, call, \ + PropertyMock +import salt.exceptions as excs + +# Import Salt libraries +import salt.utils.vmware +# Import Third Party Libs +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +try: + import gssapi + HAS_GSSAPI = True +except ImportError: + HAS_GSSAPI = False + +if sys.version_info[:3] > (2, 7, 8): + SSL_VALIDATION = True +else: + SSL_VALIDATION = False + +# Get Logging Started +log = logging.getLogger(__name__) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') +@patch('gssapi.Name', MagicMock(return_value='service')) +@patch('gssapi.InitContext', MagicMock()) +class GssapiTokenTest(TestCase): + ''' + Test cases for salt.utils.vmware.get_gssapi_token + ''' + + @patch('salt.utils.vmware.HAS_GSSAPI', False) + def test_no_gssapi(self): + with self.assertRaises(ImportError) as excinfo: + salt.utils.vmware.get_gssapi_token('principal', 'host', 'domain') + self.assertIn('The gssapi library is not imported.', + excinfo.exception.message) + + @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + def test_service_name(self): + mock_name = MagicMock() + with patch.object(salt.utils.vmware.gssapi, 'Name', mock_name): + + with self.assertRaises(excs.CommandExecutionError): + salt.utils.vmware.get_gssapi_token('principal', 'host', + 'domain') + mock_name.assert_called_once_with('principal/host@domain', + gssapi.C_NT_USER_NAME) + + @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + def test_out_token_defined(self): + mock_context = MagicMock(return_value=MagicMock()) + mock_context.return_value.established = False + mock_context.return_value.step = MagicMock(return_value='out_token') + with patch.object(salt.utils.vmware.gssapi, 'InitContext', + mock_context): + ret = salt.utils.vmware.get_gssapi_token('principal', 'host', + 'domain') + self.assertEqual(mock_context.return_value.step.called, 1) + self.assertEqual(ret, base64.b64encode('out_token')) + + @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + def test_out_token_undefined(self): + mock_context = MagicMock(return_value=MagicMock()) + mock_context.return_value.established = False + mock_context.return_value.step = MagicMock(return_value=None) + with patch.object(salt.utils.vmware.gssapi, 'InitContext', + mock_context): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware.get_gssapi_token('principal', 'host', + 'domain') + self.assertEqual(mock_context.return_value.step.called, 1) + self.assertIn('Can\'t receive token', + excinfo.exception.strerror) + + @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + def test_context_extablished(self): + mock_context = MagicMock(return_value=MagicMock()) + mock_context.return_value.established = True + mock_context.return_value.step = MagicMock(return_value='out_token') + with patch.object(salt.utils.vmware.gssapi, 'InitContext', + mock_context): + mock_context.established = True + mock_context.step = MagicMock(return_value=None) + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware.get_gssapi_token('principal', 'host', + 'domain') + self.assertEqual(mock_context.step.called, 0) + self.assertIn('Context established, but didn\'t receive token', + excinfo.exception.strerror) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.SmartConnect', MagicMock()) +@patch('salt.utils.vmware.Disconnect', MagicMock()) +@patch('salt.utils.vmware.get_gssapi_token', + MagicMock(return_value='fake_token')) +class PrivateGetServiceInstanceTestCase(TestCase): + '''Tests for salt.utils.vmware._get_service_instance''' + + def test_invalid_mechianism(self): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='invalid_mechanism', + principal='fake principal', + domain='fake_domain') + self.assertIn('Unsupported mechanism', excinfo.exception.strerror) + + def test_userpass_mechanism_empty_username(self): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username=None, + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='userpass', + principal='fake principal', + domain='fake_domain') + self.assertIn('mandatory parameter \'username\'', + excinfo.exception.strerror) + + def test_userpass_mechanism_empty_password(self): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password=None, + protocol='fake_protocol', + port=1, + mechanism='userpass', + principal='fake principal', + domain='fake_domain') + self.assertIn('mandatory parameter \'password\'', + excinfo.exception.strerror) + + def test_userpass_mechanism_no_domain(self): + mock_sc = MagicMock() + with patch('salt.utils.vmware.SmartConnect', mock_sc): + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='userpass', + principal='fake principal', + domain=None) + mock_sc.assert_called_once_with( + host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token=None, + mechanism='userpass') + + def test_userpass_mech_domain_unused(self): + mock_sc = MagicMock() + with patch('salt.utils.vmware.SmartConnect', mock_sc): + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username@domain', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='userpass', + principal='fake principal', + domain='fake_domain') + mock_sc.assert_called_once_with( + host='fake_host.fqdn', + user='fake_username@domain', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token=None, + mechanism='userpass') + mock_sc.reset_mock() + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='domain\\fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='userpass', + principal='fake principal', + domain='fake_domain') + mock_sc.assert_called_once_with( + host='fake_host.fqdn', + user='domain\\fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token=None, + mechanism='userpass') + + def test_sspi_empty_principal(self): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal=None, + domain='fake_domain') + self.assertIn('mandatory parameters are missing', + excinfo.exception.strerror) + + def test_sspi_empty_domain(self): + with self.assertRaises(excs.CommandExecutionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain=None) + self.assertIn('mandatory parameters are missing', + excinfo.exception.strerror) + + def test_sspi_get_token_error(self): + mock_token = MagicMock(side_effect=Exception('Exception')) + + with patch('salt.utils.vmware.get_gssapi_token', mock_token): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + mock_token.assert_called_once_with('fake_principal', + 'fake_host.fqdn', + 'fake_domain') + self.assertEqual('Exception', excinfo.exception.strerror) + + def test_sspi_get_token_success_(self): + mock_token = MagicMock(return_value='fake_token') + mock_sc = MagicMock() + + with patch('salt.utils.vmware.get_gssapi_token', mock_token): + with patch('salt.utils.vmware.SmartConnect', mock_sc): + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + mock_token.assert_called_once_with('fake_principal', + 'fake_host.fqdn', + 'fake_domain') + mock_sc.assert_called_once_with( + host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token='fake_token', + mechanism='sspi') + + def test_first_attempt_successful_connection(self): + mock_sc = MagicMock() + with patch('salt.utils.vmware.SmartConnect', mock_sc): + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + mock_sc.assert_called_once_with( + host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token='fake_token', + mechanism='sspi') + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_second_attempt_successful_connection(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + mock_sc = MagicMock(side_effect=[exc, None]) + mock_ssl = MagicMock() + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch('ssl._create_unverified_context', + mock_ssl): + + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + mock_ssl.assert_called_once_with() + calls = [call(host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token='fake_token', + mechanism='sspi'), + call(host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + sslContext=mock_ssl.return_value, + b64token='fake_token', + mechanism='sspi')] + mock_sc.assert_has_calls(calls) + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_third_attempt_successful_connection(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc2 = Exception('certificate verify failed') + mock_sc = MagicMock(side_effect=[exc, exc2, None]) + mock_ssl_unverif = MagicMock() + mock_ssl_context = MagicMock() + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch('ssl._create_unverified_context', + mock_ssl_unverif): + + with patch('ssl.SSLContext', mock_ssl_context): + + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + mock_ssl_context.assert_called_once_with(ssl.PROTOCOL_TLSv1) + mock_ssl_unverif.assert_called_once_with() + calls = [call(host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + b64token='fake_token', + mechanism='sspi'), + call(host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + sslContext=mock_ssl_unverif.return_value, + b64token='fake_token', + mechanism='sspi'), + call(host='fake_host.fqdn', + user='fake_username', + pwd='fake_password', + protocol='fake_protocol', + port=1, + sslContext=mock_ssl_context.return_value, + b64token='fake_token', + mechanism='sspi'), + ] + mock_sc.assert_has_calls(calls) + + def test_first_attempt_unsuccessful_connection_default_error(self): + exc = Exception('Exception') + mock_sc = MagicMock(side_effect=exc) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 1) + self.assertIn('Could not connect to host \'fake_host.fqdn\'', + excinfo.Exception.message) + + def test_first_attempt_unsuccessful_connection_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault' + mock_sc = MagicMock(side_effect=exc) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 1) + self.assertEqual('VimFault', excinfo.Exception.message) + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_second_attempt_unsuccsessful_connection_default_error(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc2 = Exception('Exception') + mock_sc = MagicMock(side_effect=[exc, exc2]) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 2) + self.assertIn('Could not connect to host \'fake_host.fqdn\'', + excinfo.Exception.message) + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_second_attempt_unsuccsessful_connection_vim_fault(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc2 = vim.fault.VimFault() + exc2.msg = 'VimFault' + mock_sc = MagicMock(side_effect=[exc, exc2]) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 2) + self.assertIn('VimFault', excinfo.Exception.message) + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_third_attempt_unsuccessful_connection_detault_error(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc2 = Exception('certificate verify failed') + exc3 = Exception('Exception') + mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 3) + self.assertIn('Exception', excinfo.Exception.message) + + @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @patch('ssl.SSLContext', MagicMock()) + @patch('ssl._create_unverified_context', MagicMock()) + def test_third_attempt_unsuccessful_connection_vim_fault(self): + exc = vim.fault.HostConnectFault() + exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc2 = Exception('certificate verify failed') + exc3 = vim.fault.VimFault() + exc3.msg = 'VimFault' + mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) + + with patch('salt.utils.vmware.SmartConnect', mock_sc): + with self.assertRaises(excs.VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host='fake_host.fqdn', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='sspi', + principal='fake_principal', + domain='fake_domain') + + self.assertEqual(mock_sc.call_count, 3) + self.assertIn('VimFault', excinfo.Exception.message) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.GetSi', MagicMock(return_value=None)) +@patch('salt.utils.vmware._get_service_instance', + MagicMock(return_value=MagicMock())) +class GetServiceInstanceTestCase(TestCase): + '''Tests for salt.utils.vmware.get_service_instance''' + + def test_default_params(self): + mock_get_si = MagicMock() + with patch('salt.utils.vmware._get_service_instance', mock_get_si): + salt.utils.vmware.get_service_instance( + host='fake_host' + ) + mock_get_si.assert_called_once_with('fake_host', None, None, + 'https', 443, 'userpass', None, + None) + + @patch('salt.utils.is_proxy', MagicMock(return_value=True)) + def test_no_cached_service_instance_same_host_on_proxy(self): + # Service instance is uncached when using class default mock objs + mock_get_si = MagicMock() + with patch('salt.utils.vmware._get_service_instance', mock_get_si): + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain' + ) + mock_get_si.assert_called_once_with('fake_host', + 'fake_username', + 'fake_password', + 'fake_protocol', + 1, + 'fake_mechanism', + 'fake_principal', + 'fake_domain') + + def test_cached_service_instance_different_host(self): + mock_si = MagicMock() + mock_si_stub = MagicMock() + mock_disconnect = MagicMock() + mock_get_si = MagicMock(return_value=mock_si) + mock_getstub = MagicMock() + with patch('salt.utils.vmware.GetSi', mock_get_si): + with patch('salt.utils.vmware.GetStub', mock_getstub): + with patch('salt.utils.vmware.Disconnect', mock_disconnect): + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain' + ) + self.assertEqual(mock_get_si.call_count, 1) + self.assertEqual(mock_getstub.call_count, 1) + self.assertEqual(mock_disconnect.call_count, 1) + + def test_uncached_service_instance(self): + # Service instance is uncached when using class default mock objs + mock_get_si = MagicMock() + with patch('salt.utils.vmware._get_service_instance', mock_get_si): + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain' + ) + mock_get_si.assert_called_once_with('fake_host', + 'fake_username', + 'fake_password', + 'fake_protocol', + 1, + 'fake_mechanism', + 'fake_principal', + 'fake_domain') + + def test_unauthenticated_service_instance(self): + mock_si_current_time = MagicMock(side_effect=vim.fault.NotAuthenticated) + mock_si = MagicMock() + mock_get_si = MagicMock(return_value=mock_si) + mock_si.CurrentTime = mock_si_current_time + mock_disconnect = MagicMock() + with patch('salt.utils.vmware._get_service_instance', mock_get_si): + with patch('salt.utils.vmware.Disconnect', mock_disconnect): + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain' + ) + self.assertEqual(mock_si_current_time.call_count, 1) + self.assertEqual(mock_disconnect.call_count, 1) + self.assertEqual(mock_get_si.call_count, 2) + + def test_current_time_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + with patch('salt.utils.vmware._get_service_instance', + MagicMock(return_value=MagicMock( + CurrentTime=MagicMock(side_effect=exc)))): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain') + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_current_time_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + with patch('salt.utils.vmware._get_service_instance', + MagicMock(return_value=MagicMock( + CurrentTime=MagicMock(side_effect=exc)))): + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.get_service_instance( + host='fake_host', + username='fake_username', + password='fake_password', + protocol='fake_protocol', + port=1, + mechanism='fake_mechanism', + principal='fake_principal', + domain='fake_domain') + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +class DisconnectTestCase(TestCase): + '''Tests for salt.utils.vmware.disconnect''' + + def setUp(self): + self.mock_si = MagicMock() + + def test_disconnect(self): + mock_disconnect = MagicMock() + with patch('salt.utils.vmware.Disconnect', mock_disconnect): + salt.utils.vmware.disconnect( + service_instance=self.mock_si) + mock_disconnect.assert_called_once_with(self.mock_si) + + def test_disconnect_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + with patch('salt.utils.vmware.Disconnect', MagicMock(side_effect=exc)): + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.disconnect( + service_instance=self.mock_si) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_disconnect_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + with patch('salt.utils.vmware.Disconnect', MagicMock(side_effect=exc)): + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.disconnect( + service_instance=self.mock_si) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +class IsConnectionToAVCenterTestCase(TestCase): + '''Tests for salt.utils.vmware.is_connection_to_a_vcenter''' + + def test_api_type_raise_vim_fault(self): + exc = vim.fault.VimFault() + exc.msg = 'VimFault msg' + mock_si = MagicMock() + type(mock_si.content.about).apiType = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.is_connection_to_a_vcenter(mock_si) + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_api_type_raise_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + mock_si = MagicMock() + type(mock_si.content.about).apiType = PropertyMock(side_effect=exc) + with self.assertRaises(excs.VMwareRuntimeError) as excinfo: + salt.utils.vmware.is_connection_to_a_vcenter(mock_si) + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_connected_to_a_vcenter(self): + mock_si = MagicMock() + mock_si.content.about.apiType = 'VirtualCenter' + + ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) + self.assertTrue(ret) + + def test_connected_to_a_host(self): + mock_si = MagicMock() + mock_si.content.about.apiType = 'HostAgent' + + ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) + self.assertFalse(ret) + + def test_connected_to_invalid_entity(self): + mock_si = MagicMock() + mock_si.content.about.apiType = 'UnsupportedType' + + with self.assertRaises(excs.VMwareApiError) as excinfo: + salt.utils.vmware.is_connection_to_a_vcenter(mock_si) + self.assertIn('Unexpected api type \'UnsupportedType\'', + excinfo.exception.strerror) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.vim.ServiceInstance', MagicMock()) +class GetServiceInstanceFromManagedObjectTestCase(TestCase): + '''Tests for salt.utils.vmware.get_managed_instance_from_managed_object''' + + def setUp(self): + self.mock_si = MagicMock() + self.mock_stub = PropertyMock() + self.mock_mo_ref = MagicMock(_stub=self.mock_stub) + + def test_default_name_parameter(self): + mock_trace = MagicMock() + type(salt.utils.vmware.log).trace = mock_trace + salt.utils.vmware.get_service_instance_from_managed_object( + self.mock_mo_ref) + mock_trace.assert_called_once_with('[] Retrieving service ' + 'instance from managed object') + + def test_name_parameter_passed_in(self): + mock_trace = MagicMock() + type(salt.utils.vmware.log).trace = mock_trace + salt.utils.vmware.get_service_instance_from_managed_object( + self.mock_mo_ref, 'fake_mo_name') + mock_trace.assert_called_once_with('[fake_mo_name] Retrieving service ' + 'instance from managed object') + + def test_service_instance_instantiation(self): + mock_service_instance_ini = MagicMock() + with patch('salt.utils.vmware.vim.ServiceInstance', + mock_service_instance_ini): + salt.utils.vmware.get_service_instance_from_managed_object( + self.mock_mo_ref) + mock_service_instance_ini.assert_called_once_with('ServiceInstance') + + def test_si_return_and_stub_assignment(self): + with patch('salt.utils.vmware.vim.ServiceInstance', + MagicMock(return_value=self.mock_si)): + ret = salt.utils.vmware.get_service_instance_from_managed_object( + self.mock_mo_ref) + self.assertEqual(ret, self.mock_si) + self.assertEqual(ret._stub, self.mock_stub) + +if __name__ == '__main__': + from integration import run_tests + run_tests(GssapiTokenTest, needs_daemon=False) + run_tests(PrivateGetServiceInstanceTestCase, needs_daemon=False) + run_tests(GetServiceInstanceTestCase, needs_daemon=False) + run_tests(DisconnectTestCase, needs_daemon=False) + run_tests(IsConnectionToAVCenterTestCase, needs_daemon=False) + run_tests(GetServiceInstanceFromManagedObjectTestCase, needs_daemon=False) diff --git a/tests/unit/utils/vmware_test/test_datacenter.py b/tests/unit/utils/vmware_test/test_datacenter.py new file mode 100644 index 000000000000..76492dbe600d --- /dev/null +++ b/tests/unit/utils/vmware_test/test_datacenter.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +''' +:codeauthor: :email:`Alexandru Bleotu ` + +Tests for datacenter related functions in salt.utils.vmware +''' + +# Import python libraries +from __future__ import absolute_import +import logging +# Import Salt testing libraries +from salttesting import TestCase, skipIf +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock + +from salt.exceptions import VMwareObjectRetrievalError, VMwareApiError, \ + VMwareRuntimeError + +# Import Salt libraries +import salt.utils.vmware as vmware +# Import Third Party Libs +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +# Get Logging Started +log = logging.getLogger(__name__) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[{'name': 'fake_dc', 'object': MagicMock()}])) +class GetDatacentersTestCase(TestCase): + '''Tests for salt.utils.vmware.get_datacenters''' + + def setUp(self): + self.mock_si = MagicMock() + self.mock_dc1 = MagicMock() + self.mock_dc2 = MagicMock() + self.mock_entries = [{'name': 'fake_dc1', + 'object': self.mock_dc1}, + {'name': 'fake_dc2', + 'object': self.mock_dc2}] + + def test_get_mors_with_properties_call(self): + mock_get_mors_with_properties = MagicMock( + return_value=[{'name': 'fake_dc', 'object': MagicMock()}]) + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors_with_properties): + vmware.get_datacenters(self.mock_si, datacenter_names=['fake_dc1']) + mock_get_mors_with_properties.assert_called_once_with( + self.mock_si, vim.Datacenter, property_list=['name']) + + def test_get_mors_with_properties_returns_empty_array(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[])): + res = vmware.get_datacenters(self.mock_si, + datacenter_names=['fake_dc1']) + self.assertEqual(res, []) + + def test_no_parameters(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + res = vmware.get_datacenters(self.mock_si) + self.assertEqual(res, []) + + def test_datastore_not_found(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + res = vmware.get_datacenters(self.mock_si, + datacenter_names=['fake_dc']) + self.assertEqual(res, []) + + def test_datastore_found(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + res = vmware.get_datacenters( + self.mock_si, datacenter_names=['fake_dc2']) + self.assertEqual(res, [self.mock_dc2]) + + def test_get_all_datastores(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_entries)): + res = vmware.get_datacenters( + self.mock_si, get_all_datacenters=True) + self.assertEqual(res, [self.mock_dc1, self.mock_dc2]) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_datacenters', + MagicMock(return_value=[MagicMock()])) +class GetDatacenterTestCase(TestCase): + '''Tests for salt.utils.vmware.get_datacenter''' + + def setUp(self): + self.mock_si = MagicMock() + self.mock_dc = MagicMock() + + def test_get_datacenters_call(self): + mock_get_datacenters = MagicMock(return_value=[MagicMock()]) + with patch('salt.utils.vmware.get_datacenters', + mock_get_datacenters): + vmware.get_datacenter(self.mock_si, 'fake_dc1') + mock_get_datacenters.assert_called_once_with( + self.mock_si, datacenter_names=['fake_dc1']) + + def test_no_datacenters_returned(self): + with patch('salt.utils.vmware.get_datacenters', + MagicMock(return_value=[])): + with self.assertRaises(VMwareObjectRetrievalError) as excinfo: + vmware.get_datacenter(self.mock_si, 'fake_dc1') + self.assertEqual('Datacenter \'fake_dc1\' was not found', + excinfo.exception.strerror) + + def test_get_datacenter_return(self): + with patch('salt.utils.vmware.get_datacenters', + MagicMock(return_value=[self.mock_dc])): + res = vmware.get_datacenter(self.mock_si, 'fake_dc1') + self.assertEqual(res, self.mock_dc) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_root_folder', MagicMock()) +class CreateDatacenterTestCase(TestCase): + '''Tests for salt.utils.vmware.create_datacenter''' + + def setUp(self): + self.mock_si = MagicMock() + self.mock_dc = MagicMock() + self.mock_create_datacenter = MagicMock(return_value=self.mock_dc) + self.mock_root_folder = MagicMock( + CreateDatacenter=self.mock_create_datacenter) + + def test_get_root_folder(self): + mock_get_root_folder = MagicMock() + with patch('salt.utils.vmware.get_root_folder', mock_get_root_folder): + vmware.create_datacenter(self.mock_si, 'fake_dc') + mock_get_root_folder.assert_called_once_with(self.mock_si) + + def test_create_datacenter_call(self): + with patch('salt.utils.vmware.get_root_folder', + MagicMock(return_value=self.mock_root_folder)): + vmware.create_datacenter(self.mock_si, 'fake_dc') + self.mock_create_datacenter.assert_called_once_with('fake_dc') + + def test_create_datacenter_raise_vim_fault(self): + exc = vim.VimFault() + exc.msg = 'VimFault msg' + self.mock_root_folder = MagicMock( + CreateDatacenter=MagicMock(side_effect=exc)) + with patch('salt.utils.vmware.get_root_folder', + MagicMock(return_value=self.mock_root_folder)): + with self.assertRaises(VMwareApiError) as excinfo: + vmware.create_datacenter(self.mock_si, 'fake_dc') + self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + + def test_create_datacenter_runtime_fault(self): + exc = vmodl.RuntimeFault() + exc.msg = 'RuntimeFault msg' + self.mock_root_folder = MagicMock( + CreateDatacenter=MagicMock(side_effect=exc)) + with patch('salt.utils.vmware.get_root_folder', + MagicMock(return_value=self.mock_root_folder)): + with self.assertRaises(VMwareRuntimeError) as excinfo: + vmware.create_datacenter(self.mock_si, 'fake_dc') + self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + + def test_datastore_successfully_created(self): + with patch('salt.utils.vmware.get_root_folder', + MagicMock(return_value=self.mock_root_folder)): + res = vmware.create_datacenter(self.mock_si, 'fake_dc') + self.assertEqual(res, self.mock_dc) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(GetDatacentersTestCase, needs_daemon=False) + run_tests(GetDatacenterTestCase, needs_daemon=False) + run_tests(CreateDatacenterTestCase, needs_daemon=False) diff --git a/tests/unit/utils/vmware_test/test_host.py b/tests/unit/utils/vmware_test/test_host.py new file mode 100644 index 000000000000..751f3258b8ad --- /dev/null +++ b/tests/unit/utils/vmware_test/test_host.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Alexandru Bleotu ` + + Tests for host functions in salt.utils.vmware +''' + +# Import python libraries +from __future__ import absolute_import +import logging + +# Import Salt testing libraries +from salttesting import TestCase, skipIf +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock + +# Import Salt libraries +import salt.utils.vmware +# Import Third Party Libs +try: + from pyVmomi import vim + HAS_PYVMOMI = True +except ImportError: + HAS_PYVMOMI = False + +# Get Logging Started +log = logging.getLogger(__name__) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[])) +@patch('salt.utils.vmware.get_datacenter', + MagicMock(return_value=None)) +@patch('salt.utils.vmware.get_cluster', + MagicMock(return_value=None)) +class GetHostsTestCase(TestCase): + '''Tests for salt.utils.vmware.get_hosts''' + + def setUp(self): + self.mock_root_folder = MagicMock() + self.mock_si = MagicMock() + self.mock_host1, self.mock_host2, self.mock_host3 = MagicMock(), \ + MagicMock(), MagicMock() + self.mock_prop_host1 = {'name': 'fake_hostname1', + 'object': self.mock_host1} + self.mock_prop_host2 = {'name': 'fake_hostname2', + 'object': self.mock_host2} + self.mock_prop_host3 = {'name': 'fake_hostname3', + 'object': self.mock_host3} + self.mock_prop_hosts = [self.mock_prop_host1, self.mock_prop_host2, + self.mock_prop_host3] + + def test_get_si_no_datacenter_no_cluster(self): + mock_get_mors = MagicMock() + mock_get_root_folder = MagicMock(return_value=self.mock_root_folder) + with patch('salt.utils.vmware.get_root_folder', mock_get_root_folder): + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors): + salt.utils.vmware.get_hosts(self.mock_si) + mock_get_root_folder.assert_called_once_with(self.mock_si) + mock_get_mors.assert_called_once_with( + self.mock_si, vim.HostSystem, container_ref=self.mock_root_folder, + property_list=['name']) + + def test_get_si_datacenter_name_no_cluster_name(self): + mock_dc = MagicMock() + mock_get_dc = MagicMock(return_value=mock_dc) + mock_get_mors = MagicMock() + with patch('salt.utils.vmware.get_datacenter', mock_get_dc): + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors): + salt.utils.vmware.get_hosts(self.mock_si, + datacenter_name='fake_datacenter') + mock_get_dc.assert_called_once_with(self.mock_si, 'fake_datacenter') + mock_get_mors.assert_called_once_with(self.mock_si, + vim.HostSystem, + container_ref=mock_dc, + property_list=['name']) + + def test_get_si_datacenter_name_and_cluster_name(self): + mock_dc = MagicMock() + mock_get_dc = MagicMock(return_value=mock_dc) + mock_get_cl = MagicMock() + mock_get_mors = MagicMock() + with patch('salt.utils.vmware.get_datacenter', mock_get_dc): + with patch('salt.utils.vmware.get_cluster', mock_get_cl): + with patch('salt.utils.vmware.get_mors_with_properties', + mock_get_mors): + salt.utils.vmware.get_hosts( + self.mock_si, datacenter_name='fake_datacenter', + cluster_name='fake_cluster') + mock_get_dc.assert_called_once_with(self.mock_si, 'fake_datacenter') + mock_get_cl.assert_called_once_with(mock_dc, 'fake_cluster') + mock_get_mors.assert_called_once_with(self.mock_si, + vim.HostSystem, + container_ref=mock_dc, + property_list=['name', 'parent']) + + def test_host_get_all_hosts(self): + with patch('salt.utils.vmware.get_root_folder', + MagicMock(return_value=self.mock_root_folder)): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_prop_hosts)): + res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) + self.assertEqual(res, [self.mock_host1, self.mock_host2, + self.mock_host3]) + + def test_filter_hostname(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_prop_hosts)): + res = salt.utils.vmware.get_hosts(self.mock_si, + host_names=['fake_hostname1', + 'fake_hostname2']) + self.assertEqual(res, [self.mock_host1, self.mock_host2]) + + def test_get_all_host_flag_not_set_and_no_host_names(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_prop_hosts)): + res = salt.utils.vmware.get_hosts(self.mock_si) + self.assertEqual(res, []) + + def test_filter_cluster(self): + cluster1 = vim.ClusterComputeResource('fake_good_cluster') + cluster2 = vim.ClusterComputeResource('fake_bad_cluster') + # Mock cluster1.name and cluster2.name + cluster1._stub = MagicMock(InvokeAccessor=MagicMock( + return_value='fake_good_cluster')) + cluster2._stub = MagicMock(InvokeAccessor=MagicMock( + return_value='fake_bad_cluster')) + self.mock_prop_host1['parent'] = cluster2 + self.mock_prop_host2['parent'] = cluster1 + self.mock_prop_host3['parent'] = cluster1 + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=self.mock_prop_hosts)): + res = salt.utils.vmware.get_hosts(self.mock_si, + datacenter_name='fake_datacenter', + cluster_name='fake_good_cluster', + get_all_hosts=True) + self.assertEqual(res, [self.mock_host2, self.mock_host3]) + + def test_no_hosts(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[])): + res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) + self.assertEqual(res, []) + + def test_one_host_returned(self): + with patch('salt.utils.vmware.get_mors_with_properties', + MagicMock(return_value=[self.mock_prop_host1])): + res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) + self.assertEqual(res, [self.mock_host1]) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(GetHostsTestCase, needs_daemon=False) From 8c1adfafd52d96efaaede3aa7a52f0bc49e3b1e9 Mon Sep 17 00:00:00 2001 From: Dheeraj Date: Wed, 29 Mar 2017 17:21:47 +0530 Subject: [PATCH 09/17] More complete fix for 39692 The existing fix did not work if the profile name itself had a dash `-`. For example - `virtual-guest`. This commit fixes that by using `split('- ')` rather than `split('-')`. This commit also provides two simple tests for the `list_()` function to emulate behaviour of both old and new tuned-adm versions Fixes #39692 --- salt/modules/tuned.py | 2 +- tests/unit/modules/tuned_test.py | 67 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/unit/modules/tuned_test.py diff --git a/salt/modules/tuned.py b/salt/modules/tuned.py index 33a1485c9934..45d54b512b40 100644 --- a/salt/modules/tuned.py +++ b/salt/modules/tuned.py @@ -51,7 +51,7 @@ def list_(): result.pop() # Output can be : " - - " (v2.7.1) # or " - " (v2.4.1) - result = [i.split('-')[1].strip() for i in result] + result = [i.split('- ')[1].strip() for i in result] return result diff --git a/tests/unit/modules/tuned_test.py b/tests/unit/modules/tuned_test.py new file mode 100644 index 000000000000..bacead588d7e --- /dev/null +++ b/tests/unit/modules/tuned_test.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +from salt.modules import tuned + +from salttesting import skipIf, TestCase +from salttesting.helpers import ensure_in_syspath +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call + + +tuned.__salt__ = {} + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class TunedListTestCase(TestCase): + def test_v_241(self): + """ + Test the list_ function for older tuned-adm (v2.4.1) + as shipped with CentOS-6 + """ + tuned_list = '''Available profiles: +- throughput-performance +- virtual-guest +- latency-performance +- laptop-battery-powersave +- laptop-ac-powersave +- virtual-host +- desktop-powersave +- server-powersave +- spindown-disk +- sap +- enterprise-storage +- default +Current active profile: throughput-performance''' + mock_cmd = MagicMock(return_value=tuned_list) + with patch.dict(tuned.__salt__, {'cmd.run': mock_cmd}): + self.assertEqual( + tuned.list_(), + ['throughput-performance', 'virtual-guest', + 'latency-performance', 'laptop-battery-powersave', + 'laptop-ac-powersave', 'virtual-host', + 'desktop-powersave', 'server-powersave', + 'spindown-disk', 'sap', 'enterprise-storage', 'default']) + + def test_v_271(self): + """ + Test the list_ function for older tuned-adm (v2.7.1) + as shipped with CentOS-7 + """ + tuned_list = '''Available profiles: +- balanced - General non-specialized tuned profile +- desktop - Optmize for the desktop use-case +- latency-performance - Optimize for deterministic performance +- network-latency - Optimize for deterministic performance +- network-throughput - Optimize for streaming network throughput. +- powersave - Optimize for low power-consumption +- throughput-performance - Broadly applicable tuning that provides-- +- virtual-guest - Optimize for running inside a virtual-guest. +- virtual-host - Optimize for running KVM guests +Current active profile: virtual-guest +''' + mock_cmd = MagicMock(return_value=tuned_list) + with patch.dict(tuned.__salt__, {'cmd.run': mock_cmd}): + self.assertEqual( + tuned.list_(), + ['balanced', 'desktop', 'latency-performance', + 'network-latency', 'network-throughput', 'powersave', + 'throughput-performance', 'virtual-guest', + 'virtual-host']) From 77a40a0c4415773fb67f9f7ab4750dcd280608fa Mon Sep 17 00:00:00 2001 From: Dheeraj Date: Thu, 30 Mar 2017 10:30:21 +0530 Subject: [PATCH 10/17] Lint fixes --- tests/unit/modules/tuned_test.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unit/modules/tuned_test.py b/tests/unit/modules/tuned_test.py index bacead588d7e..a5b65ee54065 100644 --- a/tests/unit/modules/tuned_test.py +++ b/tests/unit/modules/tuned_test.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + from salt.modules import tuned from salttesting import skipIf, TestCase -from salttesting.helpers import ensure_in_syspath -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch tuned.__salt__ = {} @@ -11,6 +12,9 @@ @skipIf(NO_MOCK, NO_MOCK_REASON) class TunedListTestCase(TestCase): + """ + Test the tuned.list_() method for different versions of tuned-adm + """ def test_v_241(self): """ Test the list_ function for older tuned-adm (v2.4.1) @@ -42,7 +46,7 @@ def test_v_241(self): def test_v_271(self): """ - Test the list_ function for older tuned-adm (v2.7.1) + Test the list_ function for newer tuned-adm (v2.7.1) as shipped with CentOS-7 """ tuned_list = '''Available profiles: From 8edaf25e10e17e837b98b3f9abe9968cb02e0476 Mon Sep 17 00:00:00 2001 From: Loren Gordon Date: Thu, 30 Mar 2017 09:38:00 -0400 Subject: [PATCH 11/17] Adds some missing file functions on Windows --- salt/modules/win_file.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/salt/modules/win_file.py b/salt/modules/win_file.py index e73b2c578525..0170911c02fe 100644 --- a/salt/modules/win_file.py +++ b/salt/modules/win_file.py @@ -65,7 +65,9 @@ RE_FLAG_TABLE, blockreplace, prepend, seek_read, seek_write, rename, lstat, path_exists_glob, write, pardir, join, HASHES, HASHES_REVMAP, comment, uncomment, _add_flags, comment_line, _regex_to_static, - _get_line_indent, apply_template_on_contents) + _get_line_indent, apply_template_on_contents, dirname, basename, + list_backups_dir) +from salt.modules.file import normpath as normpath_ from salt.utils import namespaced_function as _namespaced_function @@ -93,7 +95,8 @@ def __virtual__(): global blockreplace, prepend, seek_read, seek_write, rename, lstat global write, pardir, join, _add_flags, apply_template_on_contents global path_exists_glob, comment, uncomment, _mkstemp_copy - global _regex_to_static, _get_line_indent + global _regex_to_static, _get_line_indent, dirname, basename + global list_backups_dir, normpath_ replace = _namespaced_function(replace, globals()) search = _namespaced_function(search, globals()) @@ -154,6 +157,10 @@ def __virtual__(): _mkstemp_copy = _namespaced_function(_mkstemp_copy, globals()) _add_flags = _namespaced_function(_add_flags, globals()) apply_template_on_contents = _namespaced_function(apply_template_on_contents, globals()) + dirname = _namespaced_function(dirname, globals()) + basename = _namespaced_function(basename, globals()) + list_backups_dir = _namespaced_function(list_backups_dir, globals()) + normpath_ = _namespaced_function(normpath_, globals()) return __virtualname__ return (False, "Module win_file: module only works on Windows systems") @@ -164,7 +171,8 @@ def __virtual__(): } __func_alias__ = { - 'makedirs_': 'makedirs' + 'makedirs_': 'makedirs', + 'normpath_': 'normpath', } From 7baf2809cfa4a7a6899c87316c584ad3a12de8c8 Mon Sep 17 00:00:00 2001 From: Loren Gordon Date: Thu, 30 Mar 2017 10:29:06 -0400 Subject: [PATCH 12/17] Closes handle to temporary file before returning the path Fixes #40417 --- salt/modules/temp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/salt/modules/temp.py b/salt/modules/temp.py index 30c08bdc14f9..3eb29172cb52 100644 --- a/salt/modules/temp.py +++ b/salt/modules/temp.py @@ -10,6 +10,7 @@ from __future__ import absolute_import import logging +import os import tempfile log = logging.getLogger(__name__) @@ -40,4 +41,6 @@ def file(suffix='', prefix='tmp', parent=None): salt '*' temp.file salt '*' temp.file prefix='mytemp-' parent='/var/run/' ''' - return tempfile.mkstemp(suffix, prefix, parent)[1] + fh_, tmp_ = tempfile.mkstemp(suffix, prefix, parent) + os.close(fh_) + return tmp_ From 66251263cfdf9ec174af68c25a071a0e834a7d41 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 30 Mar 2017 11:10:43 -0500 Subject: [PATCH 13/17] Fix open filehandles This fixes two cases of filehandles being left open by failing to use a with clause. --- salt/crypt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/salt/crypt.py b/salt/crypt.py index 8bfc875f1b48..e9e4f15dadf0 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -862,7 +862,8 @@ def verify_master(self, payload, master_pub=True): m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) m_pub_exists = os.path.isfile(m_pub_fn) if m_pub_exists and master_pub and not self.opts['open_mode']: - local_master_pub = salt.utils.fopen(m_pub_fn).read() + with salt.utils.fopen(m_pub_fn) as fp_: + local_master_pub = fp_.read() if payload['pub_key'].replace('\n', '').replace('\r', '') != \ local_master_pub.replace('\n', '').replace('\r', ''): @@ -912,7 +913,8 @@ def verify_master(self, payload, master_pub=True): if not m_pub_exists: # the minion has not received any masters pubkey yet, write # the newly received pubkey to minion_master.pub - salt.utils.fopen(m_pub_fn, 'wb+').write(payload['pub_key']) + with salt.utils.fopen(m_pub_fn, 'wb+') as fp_: + fp_.write(payload['pub_key']) return self.extract_aes(payload, master_pub=False) def _finger_fail(self, finger, master_key): From 43ecb1a597f1354cebb1ea808663182b1ba47257 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 30 Mar 2017 11:43:18 -0600 Subject: [PATCH 14/17] Fix logic for __virtual__ --- salt/modules/win_dsc.py | 11 +++++------ salt/modules/win_psget.py | 17 +++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/salt/modules/win_dsc.py b/salt/modules/win_dsc.py index 3b4ef67e0bd6..415d47f8c955 100644 --- a/salt/modules/win_dsc.py +++ b/salt/modules/win_dsc.py @@ -11,7 +11,6 @@ import logging import json import os -import distutils.version # Import salt libs import salt.utils @@ -32,14 +31,14 @@ def __virtual__(): if not salt.utils.is_windows(): return False, 'Module DSC: Only available on Windows systems' - # Verify PowerShell 5.0 + # Verify PowerShell powershell_info = __salt__['cmd.shell_info']('powershell') if not powershell_info['installed']: - return False, 'Module DSC: Powershell not available' + return False, 'Module DSC: Requires PowerShell' - if distutils.version.StrictVersion(powershell_info['version']) < \ - distutils.version.StrictVersion('5.0'): - return False, 'Module DSC: Requires Powershell 5 or later' + # Verify PowerShell 5.0 or greater + if salt.utils.compare_versions(powershell_info['version'], '<', '5.0'): + return False, 'Module DSC: Requires PowerShell 5 or later' return __virtualname__ diff --git a/salt/modules/win_psget.py b/salt/modules/win_psget.py index 51856b164d71..51813c4a4938 100644 --- a/salt/modules/win_psget.py +++ b/salt/modules/win_psget.py @@ -14,7 +14,6 @@ import copy import logging import json -import distutils.version # Import salt libs import salt.utils @@ -31,16 +30,18 @@ def __virtual__(): ''' Set the system module of the kernel is Windows ''' + # Verify Windows if not salt.utils.is_windows(): - return False, 'Module PSGet: Module only works on Windows systems' + return False, 'Module PSGet: Only available on Windows systems' - # Verify PowerShell 5.0 + # Verify PowerShell powershell_info = __salt__['cmd.shell_info']('powershell') - if not powershell_info['installed'] or \ - distutils.version.StrictVersion( - powershell_info['version']) >= distutils.version.StrictVersion('5.0'): - return False, 'Module DSC: Module only works with PowerShell 5 or ' \ - 'newer.' + if not powershell_info['installed']: + return False, 'Module PSGet: Requires PowerShell' + + # Verify PowerShell 5.0 or greater + if salt.utils.compare_versions(powershell_info['version'], '<', '5.0'): + return False, 'Module PSGet: Requires PowerShell 5 or newer.' return __virtualname__ From 08e95ce4f0c5c0036010fe05f4bb16193c3e6155 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 30 Mar 2017 12:01:55 -0600 Subject: [PATCH 15/17] Add logging on __virtual__ failures --- salt/modules/win_dsc.py | 3 +++ salt/modules/win_psget.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/salt/modules/win_dsc.py b/salt/modules/win_dsc.py index 415d47f8c955..00aae6372a61 100644 --- a/salt/modules/win_dsc.py +++ b/salt/modules/win_dsc.py @@ -29,15 +29,18 @@ def __virtual__(): ''' # Verify Windows if not salt.utils.is_windows(): + log.debug('Module DSC: Only available on Windows systems') return False, 'Module DSC: Only available on Windows systems' # Verify PowerShell powershell_info = __salt__['cmd.shell_info']('powershell') if not powershell_info['installed']: + log.debug('Module DSC: Requires PowerShell') return False, 'Module DSC: Requires PowerShell' # Verify PowerShell 5.0 or greater if salt.utils.compare_versions(powershell_info['version'], '<', '5.0'): + log.debug('Module DSC: Requires PowerShell 5 or later') return False, 'Module DSC: Requires PowerShell 5 or later' return __virtualname__ diff --git a/salt/modules/win_psget.py b/salt/modules/win_psget.py index 51813c4a4938..b941f5afbf68 100644 --- a/salt/modules/win_psget.py +++ b/salt/modules/win_psget.py @@ -32,15 +32,18 @@ def __virtual__(): ''' # Verify Windows if not salt.utils.is_windows(): + log.debug('Module PSGet: Only available on Windows systems') return False, 'Module PSGet: Only available on Windows systems' # Verify PowerShell powershell_info = __salt__['cmd.shell_info']('powershell') if not powershell_info['installed']: + log.debug('Module PSGet: Requires PowerShell') return False, 'Module PSGet: Requires PowerShell' # Verify PowerShell 5.0 or greater if salt.utils.compare_versions(powershell_info['version'], '<', '5.0'): + log.debug('Module PSGet: Requires PowerShell 5 or newer') return False, 'Module PSGet: Requires PowerShell 5 or newer.' return __virtualname__ From 92285cb04596d7936009ca9ed06f56dbb02c5bbd Mon Sep 17 00:00:00 2001 From: Roald Nefs Date: Wed, 29 Mar 2017 23:36:16 +0200 Subject: [PATCH 16/17] Fix for fixtures in the djangomod module Fixes #7287 --- salt/modules/djangomod.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/salt/modules/djangomod.py b/salt/modules/djangomod.py index f20405d8ca5d..76cb536d91e3 100644 --- a/salt/modules/djangomod.py +++ b/salt/modules/djangomod.py @@ -161,18 +161,19 @@ def loaddata(settings_module, salt '*' django.loaddata ''' - + args = [] kwargs = {} if database: kwargs['database'] = database + cmd = '{0} {1}'.format('loaddata', ' '.join(fixtures.split(','))) + return command(settings_module, - 'loaddata', + cmd, bin_env, pythonpath, env, - *fixtures.split(','), - **kwargs) + *args, **kwargs) def collectstatic(settings_module, From 7f6046deec2588b8c13632c350272022d88d29e2 Mon Sep 17 00:00:00 2001 From: Daniel Wallace Date: Thu, 30 Mar 2017 16:55:15 -0500 Subject: [PATCH 17/17] prepend ssh_log_file with root_dir --- salt/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/config/__init__.py b/salt/config/__init__.py index d408f92a0110..30efcee664e7 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -3318,7 +3318,7 @@ def apply_master_config(overrides=None, defaults=None): ] # These can be set to syslog, so, not actual paths on the system - for config_key in ('log_file', 'key_logfile'): + for config_key in ('log_file', 'key_logfile', 'ssh_log_file'): log_setting = opts.get(config_key, '') if log_setting is None: continue