mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #43674 from alexbleotu/storage_policies-gh
VMware vCenter proxy + storage policies states & dependencies
This commit is contained in:
commit
e4be436ca1
10 changed files with 2412 additions and 18 deletions
|
@ -14,6 +14,8 @@ from __future__ import absolute_import
|
|||
|
||||
# Import Salt libs
|
||||
from salt.utils.schema import (Schema,
|
||||
ArrayItem,
|
||||
IntegerItem,
|
||||
StringItem)
|
||||
|
||||
|
||||
|
@ -31,3 +33,25 @@ class VCenterEntitySchema(Schema):
|
|||
vcenter = StringItem(title='vCenter',
|
||||
description='Specifies the vcenter hostname',
|
||||
required=True)
|
||||
|
||||
|
||||
class VCenterProxySchema(Schema):
|
||||
'''
|
||||
Schema for the configuration for the proxy to connect to a VCenter.
|
||||
'''
|
||||
title = 'VCenter Proxy Connection Schema'
|
||||
description = 'Schema that describes the connection to a VCenter'
|
||||
additional_properties = False
|
||||
proxytype = StringItem(required=True,
|
||||
enum=['vcenter'])
|
||||
vcenter = StringItem(required=True, pattern=r'[^\s]+')
|
||||
mechanism = StringItem(required=True, enum=['userpass', 'sspi'])
|
||||
username = StringItem()
|
||||
passwords = ArrayItem(min_items=1,
|
||||
items=StringItem(),
|
||||
unique_items=True)
|
||||
|
||||
domain = StringItem()
|
||||
principal = StringItem(default='host')
|
||||
protocol = StringItem(default='https')
|
||||
port = IntegerItem(minimum=1)
|
||||
|
|
29
salt/modules/vcenter.py
Normal file
29
salt/modules/vcenter.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Module used to access the vcenter proxy connection methods
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
import salt.utils
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__proxyenabled__ = ['vcenter']
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'vcenter'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on proxy
|
||||
'''
|
||||
if salt.utils.is_proxy():
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def get_details():
|
||||
return __proxy__['vcenter.get_details']()
|
|
@ -177,6 +177,7 @@ import salt.utils.http
|
|||
import salt.utils.path
|
||||
import salt.utils.vmware
|
||||
import salt.utils.vsan
|
||||
import salt.utils.pbm
|
||||
from salt.exceptions import CommandExecutionError, VMwareSaltError, \
|
||||
ArgumentValueError, InvalidConfigError, VMwareObjectRetrievalError, \
|
||||
VMwareApiError, InvalidEntityError
|
||||
|
@ -193,7 +194,7 @@ except ImportError:
|
|||
HAS_JSONSCHEMA = False
|
||||
|
||||
try:
|
||||
from pyVmomi import vim, vmodl, VmomiSupport
|
||||
from pyVmomi import vim, vmodl, pbm, VmomiSupport
|
||||
HAS_PYVMOMI = True
|
||||
except ImportError:
|
||||
HAS_PYVMOMI = False
|
||||
|
@ -207,7 +208,7 @@ else:
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'vsphere'
|
||||
__proxyenabled__ = ['esxi', 'esxcluster', 'esxdatacenter']
|
||||
__proxyenabled__ = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter']
|
||||
|
||||
|
||||
def __virtual__():
|
||||
|
@ -254,6 +255,8 @@ def _get_proxy_connection_details():
|
|||
details = __salt__['esxcluster.get_details']()
|
||||
elif proxytype == 'esxdatacenter':
|
||||
details = __salt__['esxdatacenter.get_details']()
|
||||
elif proxytype == 'vcenter':
|
||||
details = __salt__['vcenter.get_details']()
|
||||
else:
|
||||
raise CommandExecutionError('\'{0}\' proxy is not supported'
|
||||
''.format(proxytype))
|
||||
|
@ -379,7 +382,7 @@ def gets_service_instance_via_proxy(fn):
|
|||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter')
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter')
|
||||
def get_service_instance_via_proxy(service_instance=None):
|
||||
'''
|
||||
Returns a service instance to the proxied endpoint (vCenter/ESXi host).
|
||||
|
@ -399,7 +402,7 @@ def get_service_instance_via_proxy(service_instance=None):
|
|||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter')
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter')
|
||||
def disconnect(service_instance):
|
||||
'''
|
||||
Disconnects from a vCenter or ESXi host
|
||||
|
@ -1934,7 +1937,7 @@ def get_vsan_eligible_disks(host, username, password, protocol=None, port=None,
|
|||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter')
|
||||
@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def test_vcenter_connection(service_instance=None):
|
||||
'''
|
||||
|
@ -4608,8 +4611,344 @@ def remove_dvportgroup(portgroup, dvs, service_instance=None):
|
|||
return True
|
||||
|
||||
|
||||
def _get_policy_dict(policy):
|
||||
'''Returns a dictionary representation of a policy'''
|
||||
profile_dict = {'name': policy.name,
|
||||
'description': policy.description,
|
||||
'resource_type': policy.resourceType.resourceType}
|
||||
subprofile_dicts = []
|
||||
if isinstance(policy, pbm.profile.CapabilityBasedProfile) and \
|
||||
isinstance(policy.constraints,
|
||||
pbm.profile.SubProfileCapabilityConstraints):
|
||||
|
||||
for subprofile in policy.constraints.subProfiles:
|
||||
subprofile_dict = {'name': subprofile.name,
|
||||
'force_provision': subprofile.forceProvision}
|
||||
cap_dicts = []
|
||||
for cap in subprofile.capability:
|
||||
cap_dict = {'namespace': cap.id.namespace,
|
||||
'id': cap.id.id}
|
||||
# We assume there is one constraint with one value set
|
||||
val = cap.constraint[0].propertyInstance[0].value
|
||||
if isinstance(val, pbm.capability.types.Range):
|
||||
val_dict = {'type': 'range',
|
||||
'min': val.min,
|
||||
'max': val.max}
|
||||
elif isinstance(val, pbm.capability.types.DiscreteSet):
|
||||
val_dict = {'type': 'set',
|
||||
'values': val.values}
|
||||
else:
|
||||
val_dict = {'type': 'scalar',
|
||||
'value': val}
|
||||
cap_dict['setting'] = val_dict
|
||||
cap_dicts.append(cap_dict)
|
||||
subprofile_dict['capabilities'] = cap_dicts
|
||||
subprofile_dicts.append(subprofile_dict)
|
||||
profile_dict['subprofiles'] = subprofile_dicts
|
||||
return profile_dict
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'esxcluster')
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def list_storage_policies(policy_names=None, service_instance=None):
|
||||
'''
|
||||
Returns a list of storage policies.
|
||||
|
||||
policy_names
|
||||
Names of policies to list. If None, all policies are listed.
|
||||
Default is None.
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.list_storage_policies
|
||||
|
||||
salt '*' vsphere.list_storage_policy policy_names=[policy_name]
|
||||
'''
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
if not policy_names:
|
||||
policies = salt.utils.pbm.get_storage_policies(profile_manager,
|
||||
get_all_policies=True)
|
||||
else:
|
||||
policies = salt.utils.pbm.get_storage_policies(profile_manager,
|
||||
policy_names)
|
||||
return [_get_policy_dict(p) for p in policies]
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def list_default_vsan_policy(service_instance=None):
|
||||
'''
|
||||
Returns the default vsan storage policy.
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.list_storage_policies
|
||||
|
||||
salt '*' vsphere.list_storage_policy policy_names=[policy_name]
|
||||
'''
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
policies = salt.utils.pbm.get_storage_policies(profile_manager,
|
||||
get_all_policies=True)
|
||||
def_policies = [p for p in policies
|
||||
if p.systemCreatedProfileType == 'VsanDefaultProfile']
|
||||
if not def_policies:
|
||||
raise VMwareObjectRetrievalError('Default VSAN policy was not '
|
||||
'retrieved')
|
||||
return _get_policy_dict(def_policies[0])
|
||||
|
||||
|
||||
def _get_capability_definition_dict(cap_metadata):
|
||||
# We assume each capability definition has one property with the same id
|
||||
# as the capability so we display its type as belonging to the capability
|
||||
# The object model permits multiple properties
|
||||
return {'namespace': cap_metadata.id.namespace,
|
||||
'id': cap_metadata.id.id,
|
||||
'mandatory': cap_metadata.mandatory,
|
||||
'description': cap_metadata.summary.summary,
|
||||
'type': cap_metadata.propertyMetadata[0].type.typeName}
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def list_capability_definitions(service_instance=None):
|
||||
'''
|
||||
Returns a list of the metadata of all capabilities in the vCenter.
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.list_capabilities
|
||||
'''
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
ret_list = [_get_capability_definition_dict(c) for c in
|
||||
salt.utils.pbm.get_capability_definitions(profile_manager)]
|
||||
return ret_list
|
||||
|
||||
|
||||
def _apply_policy_config(policy_spec, policy_dict):
|
||||
'''Applies a policy dictionary to a policy spec'''
|
||||
log.trace('policy_dict = {0}'.format(policy_dict))
|
||||
if policy_dict.get('name'):
|
||||
policy_spec.name = policy_dict['name']
|
||||
if policy_dict.get('description'):
|
||||
policy_spec.description = policy_dict['description']
|
||||
if policy_dict.get('subprofiles'):
|
||||
# Incremental changes to subprofiles and capabilities are not
|
||||
# supported because they would complicate updates too much
|
||||
# The whole configuration of all sub-profiles is expected and applied
|
||||
policy_spec.constraints = pbm.profile.SubProfileCapabilityConstraints()
|
||||
subprofiles = []
|
||||
for subprofile_dict in policy_dict['subprofiles']:
|
||||
subprofile_spec = \
|
||||
pbm.profile.SubProfileCapabilityConstraints.SubProfile(
|
||||
name=subprofile_dict['name'])
|
||||
cap_specs = []
|
||||
if subprofile_dict.get('force_provision'):
|
||||
subprofile_spec.forceProvision = \
|
||||
subprofile_dict['force_provision']
|
||||
for cap_dict in subprofile_dict['capabilities']:
|
||||
prop_inst_spec = pbm.capability.PropertyInstance(
|
||||
id=cap_dict['id']
|
||||
)
|
||||
setting_type = cap_dict['setting']['type']
|
||||
if setting_type == 'set':
|
||||
prop_inst_spec.value = pbm.capability.types.DiscreteSet()
|
||||
prop_inst_spec.value.values = cap_dict['setting']['values']
|
||||
elif setting_type == 'range':
|
||||
prop_inst_spec.value = pbm.capability.types.Range()
|
||||
prop_inst_spec.value.max = cap_dict['setting']['max']
|
||||
prop_inst_spec.value.min = cap_dict['setting']['min']
|
||||
elif setting_type == 'scalar':
|
||||
prop_inst_spec.value = cap_dict['setting']['value']
|
||||
cap_spec = pbm.capability.CapabilityInstance(
|
||||
id=pbm.capability.CapabilityMetadata.UniqueId(
|
||||
id=cap_dict['id'],
|
||||
namespace=cap_dict['namespace']),
|
||||
constraint=[pbm.capability.ConstraintInstance(
|
||||
propertyInstance=[prop_inst_spec])])
|
||||
cap_specs.append(cap_spec)
|
||||
subprofile_spec.capability = cap_specs
|
||||
subprofiles.append(subprofile_spec)
|
||||
policy_spec.constraints.subProfiles = subprofiles
|
||||
log.trace('updated policy_spec = {0}'.format(policy_spec))
|
||||
return policy_spec
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def create_storage_policy(policy_name, policy_dict, service_instance=None):
|
||||
'''
|
||||
Creates a storage policy.
|
||||
|
||||
Supported capability types: scalar, set, range.
|
||||
|
||||
policy_name
|
||||
Name of the policy to create.
|
||||
The value of the argument will override any existing name in
|
||||
``policy_dict``.
|
||||
|
||||
policy_dict
|
||||
Dictionary containing the changes to apply to the policy.
|
||||
(exmaple in salt.states.pbm)
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.create_storage_policy policy_name='policy name'
|
||||
policy_dict="$policy_dict"
|
||||
'''
|
||||
log.trace('create storage policy \'{0}\', dict = {1}'
|
||||
''.format(policy_name, policy_dict))
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
policy_create_spec = pbm.profile.CapabilityBasedProfileCreateSpec()
|
||||
# Hardcode the storage profile resource type
|
||||
policy_create_spec.resourceType = pbm.profile.ResourceType(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE)
|
||||
# Set name argument
|
||||
policy_dict['name'] = policy_name
|
||||
log.trace('Setting policy values in policy_update_spec')
|
||||
_apply_policy_config(policy_create_spec, policy_dict)
|
||||
salt.utils.pbm.create_storage_policy(profile_manager, policy_create_spec)
|
||||
return {'create_storage_policy': True}
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def update_storage_policy(policy, policy_dict, service_instance=None):
|
||||
'''
|
||||
Updates a storage policy.
|
||||
|
||||
Supported capability types: scalar, set, range.
|
||||
|
||||
policy
|
||||
Name of the policy to update.
|
||||
|
||||
policy_dict
|
||||
Dictionary containing the changes to apply to the policy.
|
||||
(exmaple in salt.states.pbm)
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.update_storage_policy policy='policy name'
|
||||
policy_dict="$policy_dict"
|
||||
'''
|
||||
log.trace('updating storage policy, dict = {0}'.format(policy_dict))
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy])
|
||||
if not policies:
|
||||
raise VMwareObjectRetrievalError('Policy \'{0}\' was not found'
|
||||
''.format(policy))
|
||||
policy_ref = policies[0]
|
||||
policy_update_spec = pbm.profile.CapabilityBasedProfileUpdateSpec()
|
||||
log.trace('Setting policy values in policy_update_spec')
|
||||
for prop in ['description', 'constraints']:
|
||||
setattr(policy_update_spec, prop, getattr(policy_ref, prop))
|
||||
_apply_policy_config(policy_update_spec, policy_dict)
|
||||
salt.utils.pbm.update_storage_policy(profile_manager, policy_ref,
|
||||
policy_update_spec)
|
||||
return {'update_storage_policy': True}
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxcluster', 'esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def list_default_storage_policy_of_datastore(datastore, service_instance=None):
|
||||
'''
|
||||
Returns a list of datastores assign the the storage policies.
|
||||
|
||||
datastore
|
||||
Name of the datastore to assign.
|
||||
The datastore needs to be visible to the VMware entity the proxy
|
||||
points to.
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.list_default_storage_policy_of_datastore datastore=ds1
|
||||
'''
|
||||
log.trace('Listing the default storage policy of datastore \'{0}\''
|
||||
''.format(datastore))
|
||||
# Find datastore
|
||||
target_ref = _get_proxy_target(service_instance)
|
||||
ds_refs = salt.utils.vmware.get_datastores(service_instance, target_ref,
|
||||
datastore_names=[datastore])
|
||||
if not ds_refs:
|
||||
raise VMwareObjectRetrievalError('Datastore \'{0}\' was not '
|
||||
'found'.format(datastore))
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
policy = salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
profile_manager, ds_refs[0])
|
||||
return _get_policy_dict(policy)
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxcluster', 'esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def assign_default_storage_policy_to_datastore(policy, datastore,
|
||||
service_instance=None):
|
||||
'''
|
||||
Assigns a storage policy as the default policy to a datastore.
|
||||
|
||||
policy
|
||||
Name of the policy to assign.
|
||||
|
||||
datastore
|
||||
Name of the datastore to assign.
|
||||
The datastore needs to be visible to the VMware entity the proxy
|
||||
points to.
|
||||
|
||||
service_instance
|
||||
Service instance (vim.ServiceInstance) of the vCenter.
|
||||
Default is None.
|
||||
|
||||
.. code-block:: bash
|
||||
salt '*' vsphere.assign_storage_policy_to_datastore
|
||||
policy='policy name' datastore=ds1
|
||||
'''
|
||||
log.trace('Assigning policy {0} to datastore {1}'
|
||||
''.format(policy, datastore))
|
||||
profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
|
||||
# Find policy
|
||||
policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy])
|
||||
if not policies:
|
||||
raise VMwareObjectRetrievalError('Policy \'{0}\' was not found'
|
||||
''.format(policy))
|
||||
policy_ref = policies[0]
|
||||
# Find datastore
|
||||
target_ref = _get_proxy_target(service_instance)
|
||||
ds_refs = salt.utils.vmware.get_datastores(service_instance, target_ref,
|
||||
datastore_names=[datastore])
|
||||
if not ds_refs:
|
||||
raise VMwareObjectRetrievalError('Datastore \'{0}\' was not '
|
||||
'found'.format(datastore))
|
||||
ds_ref = ds_refs[0]
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
profile_manager, policy_ref, ds_ref)
|
||||
return True
|
||||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter', 'esxcluster', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def list_datacenters_via_proxy(datacenter_names=None, service_instance=None):
|
||||
'''
|
||||
|
@ -4647,7 +4986,7 @@ def list_datacenters_via_proxy(datacenter_names=None, service_instance=None):
|
|||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxdatacenter')
|
||||
@supports_proxies('esxdatacenter', 'vcenter')
|
||||
@gets_service_instance_via_proxy
|
||||
def create_datacenter(datacenter_name, service_instance=None):
|
||||
'''
|
||||
|
@ -6102,7 +6441,7 @@ def add_host_to_dvs(host, username, password, vmknic_name, vmnic_name,
|
|||
|
||||
|
||||
@depends(HAS_PYVMOMI)
|
||||
@supports_proxies('esxcluster', 'esxdatacenter')
|
||||
@supports_proxies('esxcluster', 'esxdatacenter', 'vcenter')
|
||||
def _get_proxy_target(service_instance):
|
||||
'''
|
||||
Returns the target object of a proxy.
|
||||
|
@ -6130,6 +6469,9 @@ def _get_proxy_target(service_instance):
|
|||
|
||||
reference = salt.utils.vmware.get_datacenter(service_instance,
|
||||
datacenter)
|
||||
elif proxy_type == 'vcenter':
|
||||
# vcenter proxy - the target is the root folder
|
||||
reference = salt.utils.vmware.get_root_folder(service_instance)
|
||||
log.trace('reference = {0}'.format(reference))
|
||||
return reference
|
||||
|
||||
|
|
338
salt/proxy/vcenter.py
Normal file
338
salt/proxy/vcenter.py
Normal file
|
@ -0,0 +1,338 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Proxy Minion interface module for managing VMWare vCenters.
|
||||
|
||||
:codeauthor: :email:`Rod McKenzie (roderick.mckenzie@morganstanley.com)`
|
||||
:codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)`
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
- pyVmomi Python Module
|
||||
|
||||
pyVmomi
|
||||
-------
|
||||
|
||||
PyVmomi can be installed via pip:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pyVmomi
|
||||
|
||||
.. note::
|
||||
|
||||
Version 6.0 of pyVmomi has some problems with SSL error handling on certain
|
||||
versions of Python. If using version 6.0 of pyVmomi, Python 2.6,
|
||||
Python 2.7.9, or newer must be present. This is due to an upstream dependency
|
||||
in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the
|
||||
version of Python is not in the supported range, you will need to install an
|
||||
earlier version of pyVmomi. See `Issue #29537`_ for more information.
|
||||
|
||||
.. _Issue #29537: https://github.com/saltstack/salt/issues/29537
|
||||
|
||||
Based on the note above, to install an earlier version of pyVmomi than the
|
||||
version currently listed in PyPi, run the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pyVmomi==5.5.0.2014.1.1
|
||||
|
||||
The 5.5.0.2014.1.1 is a known stable version that this original ESXi State
|
||||
Module was developed against.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
To use this proxy module, please use on of the following configurations:
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
proxy:
|
||||
proxytype: vcenter
|
||||
vcenter: <ip or dns name of parent vcenter>
|
||||
username: <vCenter username>
|
||||
mechanism: userpass
|
||||
passwords:
|
||||
- first_password
|
||||
- second_password
|
||||
- third_password
|
||||
|
||||
proxy:
|
||||
proxytype: vcenter
|
||||
vcenter: <ip or dns name of parent vcenter>
|
||||
username: <vCenter username>
|
||||
domain: <user domain>
|
||||
mechanism: sspi
|
||||
principal: <host kerberos principal>
|
||||
|
||||
proxytype
|
||||
^^^^^^^^^
|
||||
The ``proxytype`` key and value pair is critical, as it tells Salt which
|
||||
interface to load from the ``proxy`` directory in Salt's install hierarchy,
|
||||
or from ``/srv/salt/_proxy`` on the Salt Master (if you have created your
|
||||
own proxy module, for example). To use this Proxy Module, set this to
|
||||
``vcenter``.
|
||||
|
||||
vcenter
|
||||
^^^^^^^
|
||||
The location of the VMware vCenter server (host of ip). Required
|
||||
|
||||
username
|
||||
^^^^^^^^
|
||||
The username used to login to the vcenter, such as ``root``.
|
||||
Required only for userpass.
|
||||
|
||||
mechanism
|
||||
^^^^^^^^
|
||||
The mechanism used to connect to the vCenter server. Supported values are
|
||||
``userpass`` and ``sspi``. Required.
|
||||
|
||||
passwords
|
||||
^^^^^^^^^
|
||||
A list of passwords to be used to try and login to the vCenter server. At least
|
||||
one password in this list is required if mechanism is ``userpass``
|
||||
|
||||
The proxy integration will try the passwords listed in order.
|
||||
|
||||
domain
|
||||
^^^^^^
|
||||
User domain. Required if mechanism is ``sspi``
|
||||
|
||||
principal
|
||||
^^^^^^^^
|
||||
Kerberos principal. Rquired if mechanism is ``sspi``
|
||||
|
||||
protocol
|
||||
^^^^^^^^
|
||||
If the vCenter is not using the default protocol, set this value to an
|
||||
alternate protocol. Default is ``https``.
|
||||
|
||||
port
|
||||
^^^^
|
||||
If the ESXi host is not using the default port, set this value to an
|
||||
alternate port. Default is ``443``.
|
||||
|
||||
|
||||
Salt Proxy
|
||||
----------
|
||||
|
||||
After your pillar is in place, you can test the proxy. The proxy can run on
|
||||
any machine that has network connectivity to your Salt Master and to the
|
||||
vCenter server in the pillar. SaltStack recommends that the machine running the
|
||||
salt-proxy process also run a regular minion, though it is not strictly
|
||||
necessary.
|
||||
|
||||
On the machine that will run the proxy, make sure there is an ``/etc/salt/proxy``
|
||||
file with at least the following in it:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
master: <ip or hostname of salt-master>
|
||||
|
||||
You can then start the salt-proxy process with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-proxy --proxyid <id of the cluster>
|
||||
|
||||
You may want to add ``-l debug`` to run the above in the foreground in
|
||||
debug mode just to make sure everything is OK.
|
||||
|
||||
Next, accept the key for the proxy on your salt-master, just like you
|
||||
would for a regular minion:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-key -a <id you gave the vcenter host>
|
||||
|
||||
You can confirm that the pillar data is in place for the proxy:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> pillar.items
|
||||
|
||||
And now you should be able to ping the ESXi host to make sure it is
|
||||
responding:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> test.ping
|
||||
|
||||
At this point you can execute one-off commands against the vcenter. For
|
||||
example, you can get if the proxy can actually connect to the vCenter:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <id> vsphere.test_vcenter_connection
|
||||
|
||||
Note that you don't need to provide credentials or an ip/hostname. Salt
|
||||
knows to use the credentials you stored in Pillar.
|
||||
|
||||
It's important to understand how this particular proxy works.
|
||||
:mod:`Salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` is a
|
||||
standard Salt execution module.
|
||||
|
||||
If you pull up the docs for it you'll see
|
||||
that almost every function in the module takes credentials and a targets either
|
||||
a vcenter or a host. When credentials and a host aren't passed, Salt runs commands
|
||||
through ``pyVmomi`` against the local machine. If you wanted, you could run
|
||||
functions from this module on any host where an appropriate version of
|
||||
``pyVmomi`` is installed, and that host would reach out over the network
|
||||
and communicate with the ESXi host.
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.exceptions
|
||||
from salt.config.schemas.vcenter import VCenterProxySchema
|
||||
from salt.utils.dictupdate import merge
|
||||
|
||||
# This must be present or the Salt loader won't load this module.
|
||||
__proxyenabled__ = ['vcenter']
|
||||
|
||||
# External libraries
|
||||
try:
|
||||
import jsonschema
|
||||
HAS_JSONSCHEMA = True
|
||||
except ImportError:
|
||||
HAS_JSONSCHEMA = False
|
||||
|
||||
# Variables are scoped to this module so we can have persistent data
|
||||
# across calls to fns in here.
|
||||
DETAILS = {}
|
||||
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'vcenter'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if the vsphere execution module is available.
|
||||
'''
|
||||
if HAS_JSONSCHEMA:
|
||||
return __virtualname__
|
||||
|
||||
return False, 'The vcenter proxy module did not load.'
|
||||
|
||||
|
||||
def init(opts):
|
||||
'''
|
||||
This function gets called when the proxy starts up.
|
||||
For login the protocol and port are cached.
|
||||
'''
|
||||
log.info('Initting vcenter proxy module in process {0}'
|
||||
''.format(os.getpid()))
|
||||
log.trace('VCenter Proxy Validating vcenter proxy input')
|
||||
schema = VCenterProxySchema.serialize()
|
||||
log.trace('schema = {}'.format(schema))
|
||||
proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {}))
|
||||
log.trace('proxy_conf = {0}'.format(proxy_conf))
|
||||
try:
|
||||
jsonschema.validate(proxy_conf, schema)
|
||||
except jsonschema.exceptions.ValidationError as exc:
|
||||
raise salt.exceptions.InvalidConfigError(exc)
|
||||
|
||||
# Save mandatory fields in cache
|
||||
for key in ('vcenter', 'mechanism'):
|
||||
DETAILS[key] = proxy_conf[key]
|
||||
|
||||
# Additional validation
|
||||
if DETAILS['mechanism'] == 'userpass':
|
||||
if 'username' not in proxy_conf:
|
||||
raise salt.exceptions.InvalidConfigError(
|
||||
'Mechanism is set to \'userpass\' , but no '
|
||||
'\'username\' key found in proxy config')
|
||||
if 'passwords' not in proxy_conf:
|
||||
raise salt.exceptions.InvalidConfigError(
|
||||
'Mechanism is set to \'userpass\' , but no '
|
||||
'\'passwords\' key found in proxy config')
|
||||
for key in ('username', 'passwords'):
|
||||
DETAILS[key] = proxy_conf[key]
|
||||
else:
|
||||
if 'domain' not in proxy_conf:
|
||||
raise salt.exceptions.InvalidConfigError(
|
||||
'Mechanism is set to \'sspi\' , but no '
|
||||
'\'domain\' key found in proxy config')
|
||||
if 'principal' not in proxy_conf:
|
||||
raise salt.exceptions.InvalidConfigError(
|
||||
'Mechanism is set to \'sspi\' , but no '
|
||||
'\'principal\' key found in proxy config')
|
||||
for key in ('domain', 'principal'):
|
||||
DETAILS[key] = proxy_conf[key]
|
||||
|
||||
# Save optional
|
||||
DETAILS['protocol'] = proxy_conf.get('protocol')
|
||||
DETAILS['port'] = proxy_conf.get('port')
|
||||
|
||||
# Test connection
|
||||
if DETAILS['mechanism'] == 'userpass':
|
||||
# Get the correct login details
|
||||
log.info('Retrieving credentials and testing vCenter connection for '
|
||||
'mehchanism \'userpass\'')
|
||||
try:
|
||||
username, password = find_credentials()
|
||||
DETAILS['password'] = password
|
||||
except salt.exceptions.SaltSystemExit as err:
|
||||
log.critical('Error: {0}'.format(err))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def ping():
|
||||
'''
|
||||
Returns True.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt vcenter test.ping
|
||||
'''
|
||||
return True
|
||||
|
||||
|
||||
def shutdown():
|
||||
'''
|
||||
Shutdown the connection to the proxy device. For this proxy,
|
||||
shutdown is a no-op.
|
||||
'''
|
||||
log.debug('VCenter proxy shutdown() called...')
|
||||
|
||||
|
||||
def find_credentials():
|
||||
'''
|
||||
Cycle through all the possible credentials and return the first one that
|
||||
works.
|
||||
'''
|
||||
|
||||
# if the username and password were already found don't fo though the
|
||||
# connection process again
|
||||
if 'username' in DETAILS and 'password' in DETAILS:
|
||||
return DETAILS['username'], DETAILS['password']
|
||||
|
||||
passwords = __pillar__['proxy']['passwords']
|
||||
for password in passwords:
|
||||
DETAILS['password'] = password
|
||||
if not __salt__['vsphere.test_vcenter_connection']():
|
||||
# We are unable to authenticate
|
||||
continue
|
||||
# If we have data returned from above, we've successfully authenticated.
|
||||
return DETAILS['username'], password
|
||||
# We've reached the end of the list without successfully authenticating.
|
||||
raise salt.exceptions.VMwareConnectionError('Cannot complete login due to '
|
||||
'incorrect credentials.')
|
||||
|
||||
|
||||
def get_details():
|
||||
'''
|
||||
Function that returns the cached details
|
||||
'''
|
||||
return DETAILS
|
501
salt/states/pbm.py
Normal file
501
salt/states/pbm.py
Normal file
|
@ -0,0 +1,501 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manages VMware storage policies
|
||||
(called pbm because the vCenter endpoint is /pbm)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Storage policy
|
||||
--------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
"name": "salt_storage_policy"
|
||||
"description": "Managed by Salt. Random capability values.",
|
||||
"resource_type": "STORAGE",
|
||||
"subprofiles": [
|
||||
{
|
||||
"capabilities": [
|
||||
{
|
||||
"setting": {
|
||||
"type": "scalar",
|
||||
"value": 2
|
||||
},
|
||||
"namespace": "VSAN",
|
||||
"id": "hostFailuresToTolerate"
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "scalar",
|
||||
"value": 2
|
||||
},
|
||||
"namespace": "VSAN",
|
||||
"id": "stripeWidth"
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "scalar",
|
||||
"value": true
|
||||
},
|
||||
"namespace": "VSAN",
|
||||
"id": "forceProvisioning"
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "scalar",
|
||||
"value": 50
|
||||
},
|
||||
"namespace": "VSAN",
|
||||
"id": "proportionalCapacity"
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "scalar",
|
||||
"value": 0
|
||||
},
|
||||
"namespace": "VSAN",
|
||||
"id": "cacheReservation"
|
||||
}
|
||||
],
|
||||
"name": "Rule-Set 1: VSAN",
|
||||
"force_provision": null
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
|
||||
- pyVmomi Python Module
|
||||
|
||||
|
||||
pyVmomi
|
||||
-------
|
||||
|
||||
PyVmomi can be installed via pip:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pyVmomi
|
||||
|
||||
.. note::
|
||||
|
||||
Version 6.0 of pyVmomi has some problems with SSL error handling on certain
|
||||
versions of Python. If using version 6.0 of pyVmomi, Python 2.6,
|
||||
Python 2.7.9, or newer must be present. This is due to an upstream dependency
|
||||
in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the
|
||||
version of Python is not in the supported range, you will need to install an
|
||||
earlier version of pyVmomi. See `Issue #29537`_ for more information.
|
||||
|
||||
.. _Issue #29537: https://github.com/saltstack/salt/issues/29537
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import copy
|
||||
import sys
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.exceptions import CommandExecutionError, ArgumentValueError
|
||||
from salt.utils.dictdiffer import recursive_diff
|
||||
from salt.utils.listdiffer import list_diff
|
||||
|
||||
# External libraries
|
||||
try:
|
||||
from pyVmomi import VmomiSupport
|
||||
HAS_PYVMOMI = True
|
||||
except ImportError:
|
||||
HAS_PYVMOMI = False
|
||||
|
||||
# Get Logging Started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if not HAS_PYVMOMI:
|
||||
return False, 'State module did not load: pyVmomi not found'
|
||||
|
||||
# We check the supported vim versions to infer the pyVmomi version
|
||||
if 'vim25/6.0' in VmomiSupport.versionMap and \
|
||||
sys.version_info > (2, 7) and sys.version_info < (2, 7, 9):
|
||||
|
||||
return False, ('State module did not load: Incompatible versions '
|
||||
'of Python and pyVmomi present. See Issue #29537.')
|
||||
return True
|
||||
|
||||
|
||||
def mod_init(low):
|
||||
'''
|
||||
Init function
|
||||
'''
|
||||
return True
|
||||
|
||||
|
||||
def default_vsan_policy_configured(name, policy):
|
||||
'''
|
||||
Configures the default VSAN policy on a vCenter.
|
||||
The state assumes there is only one default VSAN policy on a vCenter.
|
||||
|
||||
policy
|
||||
Dict representation of a policy
|
||||
'''
|
||||
# TODO Refactor when recurse_differ supports list_differ
|
||||
# It's going to make the whole thing much easier
|
||||
policy_copy = copy.deepcopy(policy)
|
||||
proxy_type = __salt__['vsphere.get_proxy_type']()
|
||||
log.trace('proxy_type = {0}'.format(proxy_type))
|
||||
# All allowed proxies have a shim execution module with the same
|
||||
# name which implementes a get_details function
|
||||
# All allowed proxies have a vcenter detail
|
||||
vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter']
|
||||
log.info('Running {0} on vCenter '
|
||||
'\'{1}\''.format(name, vcenter))
|
||||
log.trace('policy = {0}'.format(policy))
|
||||
changes_required = False
|
||||
ret = {'name': name, 'changes': {}, 'result': None, 'comment': None,
|
||||
'pchanges': {}}
|
||||
comments = []
|
||||
changes = {}
|
||||
changes_required = False
|
||||
si = None
|
||||
|
||||
try:
|
||||
#TODO policy schema validation
|
||||
si = __salt__['vsphere.get_service_instance_via_proxy']()
|
||||
current_policy = __salt__['vsphere.list_default_vsan_policy'](si)
|
||||
log.trace('current_policy = {0}'.format(current_policy))
|
||||
# Building all diffs between the current and expected policy
|
||||
# XXX We simplify the comparison by assuming we have at most 1
|
||||
# sub_profile
|
||||
if policy.get('subprofiles'):
|
||||
if len(policy['subprofiles']) > 1:
|
||||
raise ArgumentValueError('Multiple sub_profiles ({0}) are not '
|
||||
'supported in the input policy')
|
||||
subprofile = policy['subprofiles'][0]
|
||||
current_subprofile = current_policy['subprofiles'][0]
|
||||
capabilities_differ = list_diff(current_subprofile['capabilities'],
|
||||
subprofile.get('capabilities', []),
|
||||
key='id')
|
||||
del policy['subprofiles']
|
||||
if subprofile.get('capabilities'):
|
||||
del subprofile['capabilities']
|
||||
del current_subprofile['capabilities']
|
||||
# Get the subprofile diffs without the capability keys
|
||||
subprofile_differ = recursive_diff(current_subprofile,
|
||||
dict(subprofile))
|
||||
|
||||
del current_policy['subprofiles']
|
||||
policy_differ = recursive_diff(current_policy, policy)
|
||||
if policy_differ.diffs or capabilities_differ.diffs or \
|
||||
subprofile_differ.diffs:
|
||||
|
||||
if 'name' in policy_differ.new_values or \
|
||||
'description' in policy_differ.new_values:
|
||||
|
||||
raise ArgumentValueError(
|
||||
'\'name\' and \'description\' of the default VSAN policy '
|
||||
'cannot be updated')
|
||||
changes_required = True
|
||||
if __opts__['test']:
|
||||
str_changes = []
|
||||
if policy_differ.diffs:
|
||||
str_changes.extend([change for change in
|
||||
policy_differ.changes_str.split('\n')])
|
||||
if subprofile_differ.diffs or capabilities_differ.diffs:
|
||||
str_changes.append('subprofiles:')
|
||||
if subprofile_differ.diffs:
|
||||
str_changes.extend(
|
||||
[' {0}'.format(change) for change in
|
||||
subprofile_differ.changes_str.split('\n')])
|
||||
if capabilities_differ.diffs:
|
||||
str_changes.append(' capabilities:')
|
||||
str_changes.extend(
|
||||
[' {0}'.format(change) for change in
|
||||
capabilities_differ.changes_str2.split('\n')])
|
||||
comments.append(
|
||||
'State {0} will update the default VSAN policy on '
|
||||
'vCenter \'{1}\':\n{2}'
|
||||
''.format(name, vcenter, '\n'.join(str_changes)))
|
||||
else:
|
||||
__salt__['vsphere.update_storage_policy'](
|
||||
policy=current_policy['name'],
|
||||
policy_dict=policy_copy,
|
||||
service_instance=si)
|
||||
comments.append('Updated the default VSAN policy in vCenter '
|
||||
'\'{0}\''.format(vcenter))
|
||||
log.info(comments[-1])
|
||||
|
||||
new_values = policy_differ.new_values
|
||||
new_values['subprofiles'] = [subprofile_differ.new_values]
|
||||
new_values['subprofiles'][0]['capabilities'] = \
|
||||
capabilities_differ.new_values
|
||||
if not new_values['subprofiles'][0]['capabilities']:
|
||||
del new_values['subprofiles'][0]['capabilities']
|
||||
if not new_values['subprofiles'][0]:
|
||||
del new_values['subprofiles']
|
||||
old_values = policy_differ.old_values
|
||||
old_values['subprofiles'] = [subprofile_differ.old_values]
|
||||
old_values['subprofiles'][0]['capabilities'] = \
|
||||
capabilities_differ.old_values
|
||||
if not old_values['subprofiles'][0]['capabilities']:
|
||||
del old_values['subprofiles'][0]['capabilities']
|
||||
if not old_values['subprofiles'][0]:
|
||||
del old_values['subprofiles']
|
||||
changes.update({'default_vsan_policy':
|
||||
{'new': new_values,
|
||||
'old': old_values}})
|
||||
log.trace(changes)
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
except CommandExecutionError as exc:
|
||||
log.error('Error: {}'.format(exc))
|
||||
if si:
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
ret.update({'comment': exc.strerror,
|
||||
'result': False if not __opts__['test'] else None})
|
||||
return ret
|
||||
if not changes_required:
|
||||
# We have no changes
|
||||
ret.update({'comment': ('Default VSAN policy in vCenter '
|
||||
'\'{0}\' is correctly configured. '
|
||||
'Nothing to be done.'.format(vcenter)),
|
||||
'result': True})
|
||||
else:
|
||||
ret.update({'comment': '\n'.join(comments)})
|
||||
if __opts__['test']:
|
||||
ret.update({'pchanges': changes,
|
||||
'result': None})
|
||||
else:
|
||||
ret.update({'changes': changes,
|
||||
'result': True})
|
||||
return ret
|
||||
|
||||
|
||||
def storage_policies_configured(name, policies):
|
||||
'''
|
||||
Configures storage policies on a vCenter.
|
||||
|
||||
policies
|
||||
List of dict representation of the required storage policies
|
||||
'''
|
||||
comments = []
|
||||
changes = []
|
||||
changes_required = False
|
||||
ret = {'name': name, 'changes': {}, 'result': None, 'comment': None,
|
||||
'pchanges': {}}
|
||||
log.trace('policies = {0}'.format(policies))
|
||||
si = None
|
||||
try:
|
||||
proxy_type = __salt__['vsphere.get_proxy_type']()
|
||||
log.trace('proxy_type = {0}'.format(proxy_type))
|
||||
# All allowed proxies have a shim execution module with the same
|
||||
# name which implementes a get_details function
|
||||
# All allowed proxies have a vcenter detail
|
||||
vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter']
|
||||
log.info('Running state \'{0}\' on vCenter '
|
||||
'\'{1}\''.format(name, vcenter))
|
||||
si = __salt__['vsphere.get_service_instance_via_proxy']()
|
||||
current_policies = __salt__['vsphere.list_storage_policies'](
|
||||
policy_names=[policy['name'] for policy in policies],
|
||||
service_instance=si)
|
||||
log.trace('current_policies = {0}'.format(current_policies))
|
||||
# TODO Refactor when recurse_differ supports list_differ
|
||||
# It's going to make the whole thing much easier
|
||||
for policy in policies:
|
||||
policy_copy = copy.deepcopy(policy)
|
||||
filtered_policies = [p for p in current_policies
|
||||
if p['name'] == policy['name']]
|
||||
current_policy = filtered_policies[0] \
|
||||
if filtered_policies else None
|
||||
|
||||
if not current_policy:
|
||||
changes_required = True
|
||||
if __opts__['test']:
|
||||
comments.append('State {0} will create the storage policy '
|
||||
'\'{1}\' on vCenter \'{2}\''
|
||||
''.format(name, policy['name'], vcenter))
|
||||
else:
|
||||
__salt__['vsphere.create_storage_policy'](
|
||||
policy['name'], policy, service_instance=si)
|
||||
comments.append('Created storage policy \'{0}\' on '
|
||||
'vCenter \'{1}\''.format(policy['name'],
|
||||
vcenter))
|
||||
changes.append({'new': policy, 'old': None})
|
||||
log.trace(comments[-1])
|
||||
# Continue with next
|
||||
continue
|
||||
|
||||
# Building all diffs between the current and expected policy
|
||||
# XXX We simplify the comparison by assuming we have at most 1
|
||||
# sub_profile
|
||||
if policy.get('subprofiles'):
|
||||
if len(policy['subprofiles']) > 1:
|
||||
raise ArgumentValueError('Multiple sub_profiles ({0}) are not '
|
||||
'supported in the input policy')
|
||||
subprofile = policy['subprofiles'][0]
|
||||
current_subprofile = current_policy['subprofiles'][0]
|
||||
capabilities_differ = list_diff(current_subprofile['capabilities'],
|
||||
subprofile.get('capabilities', []),
|
||||
key='id')
|
||||
del policy['subprofiles']
|
||||
if subprofile.get('capabilities'):
|
||||
del subprofile['capabilities']
|
||||
del current_subprofile['capabilities']
|
||||
# Get the subprofile diffs without the capability keys
|
||||
subprofile_differ = recursive_diff(current_subprofile,
|
||||
dict(subprofile))
|
||||
|
||||
del current_policy['subprofiles']
|
||||
policy_differ = recursive_diff(current_policy, policy)
|
||||
if policy_differ.diffs or capabilities_differ.diffs or \
|
||||
subprofile_differ.diffs:
|
||||
|
||||
changes_required = True
|
||||
if __opts__['test']:
|
||||
str_changes = []
|
||||
if policy_differ.diffs:
|
||||
str_changes.extend(
|
||||
[change for change in
|
||||
policy_differ.changes_str.split('\n')])
|
||||
if subprofile_differ.diffs or \
|
||||
capabilities_differ.diffs:
|
||||
|
||||
str_changes.append('subprofiles:')
|
||||
if subprofile_differ.diffs:
|
||||
str_changes.extend(
|
||||
[' {0}'.format(change) for change in
|
||||
subprofile_differ.changes_str.split('\n')])
|
||||
if capabilities_differ.diffs:
|
||||
str_changes.append(' capabilities:')
|
||||
str_changes.extend(
|
||||
[' {0}'.format(change) for change in
|
||||
capabilities_differ.changes_str2.split('\n')])
|
||||
comments.append(
|
||||
'State {0} will update the storage policy \'{1}\''
|
||||
' on vCenter \'{2}\':\n{3}'
|
||||
''.format(name, policy['name'], vcenter,
|
||||
'\n'.join(str_changes)))
|
||||
else:
|
||||
__salt__['vsphere.update_storage_policy'](
|
||||
policy=current_policy['name'],
|
||||
policy_dict=policy_copy,
|
||||
service_instance=si)
|
||||
comments.append('Updated the storage policy \'{0}\''
|
||||
'in vCenter \'{1}\''
|
||||
''.format(policy['name'], vcenter))
|
||||
log.info(comments[-1])
|
||||
|
||||
# Build new/old values to report what was changed
|
||||
new_values = policy_differ.new_values
|
||||
new_values['subprofiles'] = [subprofile_differ.new_values]
|
||||
new_values['subprofiles'][0]['capabilities'] = \
|
||||
capabilities_differ.new_values
|
||||
if not new_values['subprofiles'][0]['capabilities']:
|
||||
del new_values['subprofiles'][0]['capabilities']
|
||||
if not new_values['subprofiles'][0]:
|
||||
del new_values['subprofiles']
|
||||
old_values = policy_differ.old_values
|
||||
old_values['subprofiles'] = [subprofile_differ.old_values]
|
||||
old_values['subprofiles'][0]['capabilities'] = \
|
||||
capabilities_differ.old_values
|
||||
if not old_values['subprofiles'][0]['capabilities']:
|
||||
del old_values['subprofiles'][0]['capabilities']
|
||||
if not old_values['subprofiles'][0]:
|
||||
del old_values['subprofiles']
|
||||
changes.append({'new': new_values,
|
||||
'old': old_values})
|
||||
else:
|
||||
# No diffs found - no updates required
|
||||
comments.append('Storage policy \'{0}\' is up to date. '
|
||||
'Nothing to be done.'.format(policy['name']))
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
except CommandExecutionError as exc:
|
||||
log.error('Error: {0}'.format(exc))
|
||||
if si:
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
ret.update({'comment': exc.strerror,
|
||||
'result': False if not __opts__['test'] else None})
|
||||
return ret
|
||||
if not changes_required:
|
||||
# We have no changes
|
||||
ret.update({'comment': ('All storage policy in vCenter '
|
||||
'\'{0}\' is correctly configured. '
|
||||
'Nothing to be done.'.format(vcenter)),
|
||||
'result': True})
|
||||
else:
|
||||
ret.update({'comment': '\n'.join(comments)})
|
||||
if __opts__['test']:
|
||||
ret.update({'pchanges': {'storage_policies': changes},
|
||||
'result': None})
|
||||
else:
|
||||
ret.update({'changes': {'storage_policies': changes},
|
||||
'result': True})
|
||||
return ret
|
||||
|
||||
|
||||
def default_storage_policy_assigned(name, policy, datastore):
|
||||
'''
|
||||
Assigns a default storage policy to a datastore
|
||||
|
||||
policy
|
||||
Name of storage policy
|
||||
|
||||
datastore
|
||||
Name of datastore
|
||||
'''
|
||||
log.info('Running state {0} for policy \'{1}\', datastore \'{2}\'.'
|
||||
''.format(name, policy, datastore))
|
||||
changes = {}
|
||||
changes_required = False
|
||||
ret = {'name': name, 'changes': {}, 'result': None, 'comment': None,
|
||||
'pchanges': {}}
|
||||
si = None
|
||||
try:
|
||||
si = __salt__['vsphere.get_service_instance_via_proxy']()
|
||||
existing_policy = \
|
||||
__salt__['vsphere.list_default_storage_policy_of_datastore'](
|
||||
datastore=datastore, service_instance=si)
|
||||
if existing_policy['name'] == policy:
|
||||
comment = ('Storage policy \'{0}\' is already assigned to '
|
||||
'datastore \'{1}\'. Nothing to be done.'
|
||||
''.format(policy, datastore))
|
||||
else:
|
||||
changes_required = True
|
||||
changes = {
|
||||
'default_storage_policy': {'old': existing_policy['name'],
|
||||
'new': policy}}
|
||||
if __opts__['test']:
|
||||
comment = ('State {0} will assign storage policy \'{1}\' to '
|
||||
'datastore \'{2}\'.').format(name, policy,
|
||||
datastore)
|
||||
else:
|
||||
__salt__['vsphere.assign_default_storage_policy_to_datastore'](
|
||||
policy=policy, datastore=datastore, service_instance=si)
|
||||
comment = ('Storage policy \'{0} was assigned to datastore '
|
||||
'\'{1}\'.').format(policy, name)
|
||||
log.info(comment)
|
||||
except CommandExecutionError as exc:
|
||||
log.error('Error: {}'.format(exc))
|
||||
if si:
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
ret.update({'comment': exc.strerror,
|
||||
'result': False if not __opts__['test'] else None})
|
||||
return ret
|
||||
ret['comment'] = comment
|
||||
if changes_required:
|
||||
if __opts__['test']:
|
||||
ret.update({'result': None,
|
||||
'pchanges': changes})
|
||||
else:
|
||||
ret.update({'result': True,
|
||||
'changes': changes})
|
||||
else:
|
||||
ret['result'] = True
|
||||
return ret
|
329
salt/utils/pbm.py
Normal file
329
salt/utils/pbm.py
Normal file
|
@ -0,0 +1,329 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Library for VMware Storage Policy management (via the pbm endpoint)
|
||||
|
||||
This library is used to manage the various policies available in VMware
|
||||
|
||||
:codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstaley.com>
|
||||
|
||||
Dependencies
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- pyVmomi Python Module
|
||||
|
||||
pyVmomi
|
||||
-------
|
||||
|
||||
PyVmomi can be installed via pip:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pyVmomi
|
||||
|
||||
.. note::
|
||||
|
||||
versions of Python. If using version 6.0 of pyVmomi, Python 2.6,
|
||||
Python 2.7.9, or newer must be present. This is due to an upstream dependency
|
||||
in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the
|
||||
version of Python is not in the supported range, you will need to install an
|
||||
earlier version of pyVmomi. See `Issue #29537`_ for more information.
|
||||
|
||||
.. _Issue #29537: https://github.com/saltstack/salt/issues/29537
|
||||
|
||||
Based on the note above, to install an earlier version of pyVmomi than the
|
||||
version currently listed in PyPi, run the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pyVmomi==5.5.0.2014.1.1
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.utils.vmware
|
||||
from salt.exceptions import VMwareApiError, VMwareRuntimeError, \
|
||||
VMwareObjectRetrievalError
|
||||
|
||||
|
||||
try:
|
||||
from pyVmomi import pbm, vim, vmodl
|
||||
HAS_PYVMOMI = True
|
||||
except ImportError:
|
||||
HAS_PYVMOMI = False
|
||||
|
||||
|
||||
# Get Logging Started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if PyVmomi is installed.
|
||||
'''
|
||||
if HAS_PYVMOMI:
|
||||
return True
|
||||
else:
|
||||
return False, 'Missing dependency: The salt.utils.pbm module ' \
|
||||
'requires the pyvmomi library'
|
||||
|
||||
|
||||
def get_profile_manager(service_instance):
|
||||
'''
|
||||
Returns a profile manager
|
||||
|
||||
service_instance
|
||||
Service instance to the host or vCenter
|
||||
'''
|
||||
stub = salt.utils.vmware.get_new_service_instance_stub(
|
||||
service_instance, ns='pbm/2.0', path='/pbm/sdk')
|
||||
pbm_si = pbm.ServiceInstance('ServiceInstance', stub)
|
||||
try:
|
||||
profile_manager = pbm_si.RetrieveContent().profileManager
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
return profile_manager
|
||||
|
||||
|
||||
def get_placement_solver(service_instance):
|
||||
'''
|
||||
Returns a placement solver
|
||||
|
||||
service_instance
|
||||
Service instance to the host or vCenter
|
||||
'''
|
||||
stub = salt.utils.vmware.get_new_service_instance_stub(
|
||||
service_instance, ns='pbm/2.0', path='/pbm/sdk')
|
||||
pbm_si = pbm.ServiceInstance('ServiceInstance', stub)
|
||||
try:
|
||||
profile_manager = pbm_si.RetrieveContent().placementSolver
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
return profile_manager
|
||||
|
||||
|
||||
def get_capability_definitions(profile_manager):
|
||||
'''
|
||||
Returns a list of all capability definitions.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
'''
|
||||
res_type = pbm.profile.ResourceType(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE)
|
||||
try:
|
||||
cap_categories = profile_manager.FetchCapabilityMetadata(res_type)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
cap_definitions = []
|
||||
for cat in cap_categories:
|
||||
cap_definitions.extend(cat.capabilityMetadata)
|
||||
return cap_definitions
|
||||
|
||||
|
||||
def get_policies_by_id(profile_manager, policy_ids):
|
||||
'''
|
||||
Returns a list of policies with the specified ids.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
policy_ids
|
||||
List of policy ids to retrieve.
|
||||
'''
|
||||
try:
|
||||
return profile_manager.RetrieveContent(policy_ids)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
|
||||
|
||||
def get_storage_policies(profile_manager, policy_names=None,
|
||||
get_all_policies=False):
|
||||
'''
|
||||
Returns a list of the storage policies, filtered by name.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
policy_names
|
||||
List of policy names to filter by.
|
||||
Default is None.
|
||||
|
||||
get_all_policies
|
||||
Flag specifying to return all policies, regardless of the specified
|
||||
filter.
|
||||
'''
|
||||
res_type = pbm.profile.ResourceType(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE)
|
||||
try:
|
||||
policy_ids = profile_manager.QueryProfile(res_type)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
log.trace('policy_ids = {0}'.format(policy_ids))
|
||||
# More policies are returned so we need to filter again
|
||||
policies = [p for p in get_policies_by_id(profile_manager, policy_ids)
|
||||
if p.resourceType.resourceType ==
|
||||
pbm.profile.ResourceTypeEnum.STORAGE]
|
||||
if get_all_policies:
|
||||
return policies
|
||||
if not policy_names:
|
||||
policy_names = []
|
||||
return [p for p in policies if p.name in policy_names]
|
||||
|
||||
|
||||
def create_storage_policy(profile_manager, policy_spec):
|
||||
'''
|
||||
Creates a storage policy.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
policy_spec
|
||||
Policy update spec.
|
||||
'''
|
||||
try:
|
||||
profile_manager.Create(policy_spec)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
|
||||
|
||||
def update_storage_policy(profile_manager, policy, policy_spec):
|
||||
'''
|
||||
Updates a storage policy.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
policy
|
||||
Reference to the policy to be updated.
|
||||
|
||||
policy_spec
|
||||
Policy update spec.
|
||||
'''
|
||||
try:
|
||||
profile_manager.Update(policy.profileId, policy_spec)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
|
||||
|
||||
def get_default_storage_policy_of_datastore(profile_manager, datastore):
|
||||
'''
|
||||
Returns the default storage policy reference assigned to a datastore.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
datastore
|
||||
Reference to the datastore.
|
||||
'''
|
||||
# Retrieve all datastores visible
|
||||
hub = pbm.placement.PlacementHub(
|
||||
hubId=datastore._moId, hubType='Datastore')
|
||||
log.trace('placement_hub = {0}'.format(hub))
|
||||
try:
|
||||
policy_id = profile_manager.QueryDefaultRequirementProfile(hub)
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
||||
policy_refs = get_policies_by_id(profile_manager, [policy_id])
|
||||
if not policy_refs:
|
||||
raise VMwareObjectRetrievalError('Storage policy with id \'{0}\' was '
|
||||
'not found'.format(policy_id))
|
||||
return policy_refs[0]
|
||||
|
||||
|
||||
def assign_default_storage_policy_to_datastore(profile_manager, policy,
|
||||
datastore):
|
||||
'''
|
||||
Assigns a storage policy as the default policy to a datastore.
|
||||
|
||||
profile_manager
|
||||
Reference to the profile manager.
|
||||
|
||||
policy
|
||||
Reference to the policy to assigned.
|
||||
|
||||
datastore
|
||||
Reference to the datastore.
|
||||
'''
|
||||
placement_hub = pbm.placement.PlacementHub(
|
||||
hubId=datastore._moId, hubType='Datastore')
|
||||
log.trace('placement_hub = {0}'.format(placement_hub))
|
||||
try:
|
||||
profile_manager.AssignDefaultRequirementProfile(policy.profileId,
|
||||
[placement_hub])
|
||||
except vim.fault.NoPermission as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError('Not enough permissions. Required privilege: '
|
||||
'{0}'.format(exc.privilegeId))
|
||||
except vim.fault.VimFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareApiError(exc.msg)
|
||||
except vmodl.RuntimeFault as exc:
|
||||
log.exception(exc)
|
||||
raise VMwareRuntimeError(exc.msg)
|
|
@ -79,6 +79,8 @@ import atexit
|
|||
import errno
|
||||
import logging
|
||||
import time
|
||||
import sys
|
||||
import ssl
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.exceptions
|
||||
|
@ -92,8 +94,9 @@ import salt.utils.stringutils
|
|||
from salt.ext import six
|
||||
from salt.ext.six.moves.http_client import BadStatusLine # pylint: disable=E0611
|
||||
try:
|
||||
from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub
|
||||
from pyVmomi import vim, vmodl
|
||||
from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub, \
|
||||
SoapStubAdapter
|
||||
from pyVmomi import vim, vmodl, VmomiSupport
|
||||
HAS_PYVMOMI = True
|
||||
except ImportError:
|
||||
HAS_PYVMOMI = False
|
||||
|
@ -405,6 +408,49 @@ def get_service_instance(host, username=None, password=None, protocol=None,
|
|||
return service_instance
|
||||
|
||||
|
||||
def get_new_service_instance_stub(service_instance, path, ns=None,
|
||||
version=None):
|
||||
'''
|
||||
Returns a stub that points to a different path,
|
||||
created from an existing connection.
|
||||
|
||||
service_instance
|
||||
The Service Instance.
|
||||
|
||||
path
|
||||
Path of the new stub.
|
||||
|
||||
ns
|
||||
Namespace of the new stub.
|
||||
Default value is None
|
||||
|
||||
version
|
||||
Version of the new stub.
|
||||
Default value is None.
|
||||
'''
|
||||
#For python 2.7.9 and later, the defaul SSL conext has more strict
|
||||
#connection handshaking rule. We may need turn of the hostname checking
|
||||
#and client side cert verification
|
||||
context = None
|
||||
if sys.version_info[:3] > (2, 7, 8):
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
stub = service_instance._stub
|
||||
hostname = stub.host.split(':')[0]
|
||||
session_cookie = stub.cookie.split('"')[1]
|
||||
VmomiSupport.GetRequestContext()['vcSessionCookie'] = session_cookie
|
||||
new_stub = SoapStubAdapter(host=hostname,
|
||||
ns=ns,
|
||||
path=path,
|
||||
version=version,
|
||||
poolSize=0,
|
||||
sslContext=context)
|
||||
new_stub.cookie = stub.cookie
|
||||
return new_stub
|
||||
|
||||
|
||||
def get_service_instance_from_managed_object(mo_ref, name='<unnamed>'):
|
||||
'''
|
||||
Retrieves the service instance from a managed object.
|
||||
|
|
|
@ -639,6 +639,14 @@ class _GetProxyConnectionDetailsTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'mechanism': 'fake_mechanism',
|
||||
'principal': 'fake_principal',
|
||||
'domain': 'fake_domain'}
|
||||
self.vcenter_details = {'vcenter': 'fake_vcenter',
|
||||
'username': 'fake_username',
|
||||
'password': 'fake_password',
|
||||
'protocol': 'fake_protocol',
|
||||
'port': 'fake_port',
|
||||
'mechanism': 'fake_mechanism',
|
||||
'principal': 'fake_principal',
|
||||
'domain': 'fake_domain'}
|
||||
|
||||
def tearDown(self):
|
||||
for attrname in ('esxi_host_details', 'esxi_vcenter_details',
|
||||
|
@ -693,6 +701,17 @@ class _GetProxyConnectionDetailsTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'fake_protocol', 'fake_port', 'fake_mechanism',
|
||||
'fake_principal', 'fake_domain'), ret)
|
||||
|
||||
def test_vcenter_proxy_details(self):
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value='vcenter')):
|
||||
with patch.dict(vsphere.__salt__,
|
||||
{'vcenter.get_details': MagicMock(
|
||||
return_value=self.vcenter_details)}):
|
||||
ret = vsphere._get_proxy_connection_details()
|
||||
self.assertEqual(('fake_vcenter', 'fake_username', 'fake_password',
|
||||
'fake_protocol', 'fake_port', 'fake_mechanism',
|
||||
'fake_principal', 'fake_domain'), ret)
|
||||
|
||||
def test_unsupported_proxy_details(self):
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value='unsupported')):
|
||||
|
@ -890,7 +909,7 @@ class GetServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}
|
||||
|
||||
def test_supported_proxies(self):
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter']
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter']
|
||||
for proxy_type in supported_proxies:
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value=proxy_type)):
|
||||
|
@ -933,7 +952,7 @@ class DisconnectTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}
|
||||
|
||||
def test_supported_proxies(self):
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter']
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter']
|
||||
for proxy_type in supported_proxies:
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value=proxy_type)):
|
||||
|
@ -974,7 +993,7 @@ class TestVcenterConnectionTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}
|
||||
|
||||
def test_supported_proxies(self):
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter']
|
||||
supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter']
|
||||
for proxy_type in supported_proxies:
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value=proxy_type)):
|
||||
|
@ -1049,7 +1068,7 @@ class ListDatacentersViaProxyTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}
|
||||
|
||||
def test_supported_proxies(self):
|
||||
supported_proxies = ['esxcluster', 'esxdatacenter']
|
||||
supported_proxies = ['esxcluster', 'esxdatacenter', 'vcenter']
|
||||
for proxy_type in supported_proxies:
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value=proxy_type)):
|
||||
|
@ -1127,7 +1146,7 @@ class CreateDatacenterTestCase(TestCase, LoaderModuleMockMixin):
|
|||
}
|
||||
|
||||
def test_supported_proxies(self):
|
||||
supported_proxies = ['esxdatacenter']
|
||||
supported_proxies = ['esxdatacenter', 'vcenter']
|
||||
for proxy_type in supported_proxies:
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value=proxy_type)):
|
||||
|
@ -1339,12 +1358,15 @@ class _GetProxyTargetTestCase(TestCase, LoaderModuleMockMixin):
|
|||
def setUp(self):
|
||||
attrs = (('mock_si', MagicMock()),
|
||||
('mock_dc', MagicMock()),
|
||||
('mock_cl', MagicMock()))
|
||||
('mock_cl', MagicMock()),
|
||||
('mock_root', MagicMock()))
|
||||
for attr, mock_obj in attrs:
|
||||
setattr(self, attr, mock_obj)
|
||||
self.addCleanup(delattr, self, attr)
|
||||
attrs = (('mock_get_datacenter', MagicMock(return_value=self.mock_dc)),
|
||||
('mock_get_cluster', MagicMock(return_value=self.mock_cl)))
|
||||
('mock_get_cluster', MagicMock(return_value=self.mock_cl)),
|
||||
('mock_get_root_folder',
|
||||
MagicMock(return_value=self.mock_root)))
|
||||
for attr, mock_obj in attrs:
|
||||
setattr(self, attr, mock_obj)
|
||||
self.addCleanup(delattr, self, attr)
|
||||
|
@ -1360,7 +1382,8 @@ class _GetProxyTargetTestCase(TestCase, LoaderModuleMockMixin):
|
|||
MagicMock(return_value=(None, None, None, None, None, None, None,
|
||||
None, 'datacenter'))),
|
||||
('salt.utils.vmware.get_datacenter', self.mock_get_datacenter),
|
||||
('salt.utils.vmware.get_cluster', self.mock_get_cluster))
|
||||
('salt.utils.vmware.get_cluster', self.mock_get_cluster),
|
||||
('salt.utils.vmware.get_root_folder', self.mock_get_root_folder))
|
||||
for module, mock_obj in patches:
|
||||
patcher = patch(module, mock_obj)
|
||||
patcher.start()
|
||||
|
@ -1409,3 +1432,10 @@ class _GetProxyTargetTestCase(TestCase, LoaderModuleMockMixin):
|
|||
MagicMock(return_value='esxdatacenter')):
|
||||
ret = vsphere._get_proxy_target(self.mock_si)
|
||||
self.assertEqual(ret, self.mock_dc)
|
||||
|
||||
def test_vcenter_proxy_return(self):
|
||||
with patch('salt.modules.vsphere.get_proxy_type',
|
||||
MagicMock(return_value='vcenter')):
|
||||
ret = vsphere._get_proxy_target(self.mock_si)
|
||||
self.mock_get_root_folder.assert_called_once_with(self.mock_si)
|
||||
self.assertEqual(ret, self.mock_root)
|
||||
|
|
664
tests/unit/utils/test_pbm.py
Normal file
664
tests/unit/utils/test_pbm.py
Normal file
|
@ -0,0 +1,664 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>`
|
||||
|
||||
Tests functions in salt.utils.vsan
|
||||
'''
|
||||
|
||||
# Import python libraries
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
# Import Salt testing libraries
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, \
|
||||
PropertyMock
|
||||
|
||||
# Import Salt libraries
|
||||
from salt.exceptions import VMwareApiError, VMwareRuntimeError, \
|
||||
VMwareObjectRetrievalError
|
||||
from salt.ext.six.moves import range
|
||||
import salt.utils.pbm
|
||||
|
||||
try:
|
||||
from pyVmomi import vim, vmodl, pbm
|
||||
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')
|
||||
class GetProfileManagerTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_profile_manager'''
|
||||
def setUp(self):
|
||||
self.mock_si = MagicMock()
|
||||
self.mock_stub = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock()
|
||||
self.mock_content = MagicMock()
|
||||
self.mock_pbm_si = MagicMock(
|
||||
RetrieveContent=MagicMock(return_value=self.mock_content))
|
||||
type(self.mock_content).profileManager = \
|
||||
PropertyMock(return_value=self.mock_prof_mgr)
|
||||
patches = (
|
||||
('salt.utils.vmware.get_new_service_instance_stub',
|
||||
MagicMock(return_value=self.mock_stub)),
|
||||
('salt.utils.pbm.pbm.ServiceInstance',
|
||||
MagicMock(return_value=self.mock_pbm_si)))
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_si', 'mock_stub', 'mock_content',
|
||||
'mock_pbm_si', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_new_service_stub(self):
|
||||
mock_get_new_service_stub = MagicMock()
|
||||
with patch('salt.utils.vmware.get_new_service_instance_stub',
|
||||
mock_get_new_service_stub):
|
||||
salt.utils.pbm.get_profile_manager(self.mock_si)
|
||||
mock_get_new_service_stub.assert_called_once_with(
|
||||
self.mock_si, ns='pbm/2.0', path='/pbm/sdk')
|
||||
|
||||
def test_pbm_si(self):
|
||||
mock_get_pbm_si = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.ServiceInstance',
|
||||
mock_get_pbm_si):
|
||||
salt.utils.pbm.get_profile_manager(self.mock_si)
|
||||
mock_get_pbm_si.assert_called_once_with('ServiceInstance',
|
||||
self.mock_stub)
|
||||
|
||||
def test_return_profile_manager(self):
|
||||
ret = salt.utils.pbm.get_profile_manager(self.mock_si)
|
||||
self.assertEqual(ret, self.mock_prof_mgr)
|
||||
|
||||
def test_profile_manager_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
type(self.mock_content).profileManager = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_profile_manager(self.mock_si)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_profile_manager_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
type(self.mock_content).profileManager = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_profile_manager(self.mock_si)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_profile_manager_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
type(self.mock_content).profileManager = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_profile_manager(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 GetPlacementSolverTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_placement_solver'''
|
||||
def setUp(self):
|
||||
self.mock_si = MagicMock()
|
||||
self.mock_stub = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock()
|
||||
self.mock_content = MagicMock()
|
||||
self.mock_pbm_si = MagicMock(
|
||||
RetrieveContent=MagicMock(return_value=self.mock_content))
|
||||
type(self.mock_content).placementSolver = \
|
||||
PropertyMock(return_value=self.mock_prof_mgr)
|
||||
patches = (
|
||||
('salt.utils.vmware.get_new_service_instance_stub',
|
||||
MagicMock(return_value=self.mock_stub)),
|
||||
('salt.utils.pbm.pbm.ServiceInstance',
|
||||
MagicMock(return_value=self.mock_pbm_si)))
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_si', 'mock_stub', 'mock_content',
|
||||
'mock_pbm_si', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_new_service_stub(self):
|
||||
mock_get_new_service_stub = MagicMock()
|
||||
with patch('salt.utils.vmware.get_new_service_instance_stub',
|
||||
mock_get_new_service_stub):
|
||||
salt.utils.pbm.get_placement_solver(self.mock_si)
|
||||
mock_get_new_service_stub.assert_called_once_with(
|
||||
self.mock_si, ns='pbm/2.0', path='/pbm/sdk')
|
||||
|
||||
def test_pbm_si(self):
|
||||
mock_get_pbm_si = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.ServiceInstance',
|
||||
mock_get_pbm_si):
|
||||
salt.utils.pbm.get_placement_solver(self.mock_si)
|
||||
mock_get_pbm_si.assert_called_once_with('ServiceInstance',
|
||||
self.mock_stub)
|
||||
|
||||
def test_return_profile_manager(self):
|
||||
ret = salt.utils.pbm.get_placement_solver(self.mock_si)
|
||||
self.assertEqual(ret, self.mock_prof_mgr)
|
||||
|
||||
def test_placement_solver_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
type(self.mock_content).placementSolver = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_placement_solver(self.mock_si)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_placement_solver_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
type(self.mock_content).placementSolver = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_placement_solver(self.mock_si)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_placement_solver_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
type(self.mock_content).placementSolver = PropertyMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_placement_solver(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 GetCapabilityDefinitionsTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_capability_definitions'''
|
||||
def setUp(self):
|
||||
self.mock_res_type = MagicMock()
|
||||
self.mock_cap_cats = [MagicMock(capabilityMetadata=['fake_cap_meta1',
|
||||
'fake_cap_meta2']),
|
||||
MagicMock(capabilityMetadata=['fake_cap_meta3'])]
|
||||
self.mock_prof_mgr = MagicMock(
|
||||
FetchCapabilityMetadata=MagicMock(return_value=self.mock_cap_cats))
|
||||
patches = (
|
||||
('salt.utils.pbm.pbm.profile.ResourceType',
|
||||
MagicMock(return_value=self.mock_res_type)),)
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_res_type', 'mock_cap_cats', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_res_type(self):
|
||||
mock_get_res_type = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.profile.ResourceType',
|
||||
mock_get_res_type):
|
||||
salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
mock_get_res_type.assert_called_once_with(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE)
|
||||
|
||||
def test_fetch_capabilities(self):
|
||||
salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
self.mock_prof_mgr.FetchCapabilityMetadata.assert_called_once_with(
|
||||
self.mock_res_type)
|
||||
|
||||
def test_fetch_capabilities_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.FetchCapabilityMetadata = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_fetch_capabilities_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.FetchCapabilityMetadata = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_fetch_capabilities_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.FetchCapabilityMetadata = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
def test_return_cap_definitions(self):
|
||||
ret = salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr)
|
||||
self.assertEqual(ret, ['fake_cap_meta1', 'fake_cap_meta2',
|
||||
'fake_cap_meta3'])
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class GetPoliciesByIdTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_policies_by_id'''
|
||||
def setUp(self):
|
||||
self.policy_ids = MagicMock()
|
||||
self.mock_policies = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock(
|
||||
RetrieveContent=MagicMock(return_value=self.mock_policies))
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('policy_ids', 'mock_policies', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_retrieve_policies(self):
|
||||
salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids)
|
||||
self.mock_prof_mgr.RetrieveContent.assert_called_once_with(
|
||||
self.policy_ids)
|
||||
|
||||
def test_retrieve_policies_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_retrieve_policies_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_retrieve_policies_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
def test_return_policies(self):
|
||||
ret = salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids)
|
||||
self.assertEqual(ret, self.mock_policies)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class GetStoragePoliciesTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_storage_policies'''
|
||||
def setUp(self):
|
||||
self.mock_res_type = MagicMock()
|
||||
self.mock_policy_ids = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock(
|
||||
QueryProfile=MagicMock(return_value=self.mock_policy_ids))
|
||||
# Policies
|
||||
self.mock_policies = []
|
||||
for i in range(4):
|
||||
mock_obj = MagicMock(resourceType=MagicMock(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE))
|
||||
mock_obj.name = 'fake_policy{0}'.format(i)
|
||||
self.mock_policies.append(mock_obj)
|
||||
patches = (
|
||||
('salt.utils.pbm.pbm.profile.ResourceType',
|
||||
MagicMock(return_value=self.mock_res_type)),
|
||||
('salt.utils.pbm.get_policies_by_id',
|
||||
MagicMock(return_value=self.mock_policies)))
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_res_type', 'mock_policy_ids', 'mock_policies',
|
||||
'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_res_type(self):
|
||||
mock_get_res_type = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.profile.ResourceType',
|
||||
mock_get_res_type):
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
mock_get_res_type.assert_called_once_with(
|
||||
resourceType=pbm.profile.ResourceTypeEnum.STORAGE)
|
||||
|
||||
def test_retrieve_policy_ids(self):
|
||||
mock_retrieve_policy_ids = MagicMock(return_value=self.mock_policy_ids)
|
||||
self.mock_prof_mgr.QueryProfile = mock_retrieve_policy_ids
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
mock_retrieve_policy_ids.assert_called_once_with(self.mock_res_type)
|
||||
|
||||
def test_retrieve_policy_ids_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_retrieve_policy_ids_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_retrieve_policy_ids_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
def test_get_policies_by_id(self):
|
||||
mock_get_policies_by_id = MagicMock(return_value=self.mock_policies)
|
||||
with patch('salt.utils.pbm.get_policies_by_id',
|
||||
mock_get_policies_by_id):
|
||||
salt.utils.pbm.get_storage_policies(self.mock_prof_mgr)
|
||||
mock_get_policies_by_id.assert_called_once_with(
|
||||
self.mock_prof_mgr, self.mock_policy_ids)
|
||||
|
||||
def test_return_all_policies(self):
|
||||
ret = salt.utils.pbm.get_storage_policies(self.mock_prof_mgr,
|
||||
get_all_policies=True)
|
||||
self.assertEqual(ret, self.mock_policies)
|
||||
|
||||
def test_return_filtered_policies(self):
|
||||
ret = salt.utils.pbm.get_storage_policies(
|
||||
self.mock_prof_mgr, policy_names=['fake_policy1', 'fake_policy3'])
|
||||
self.assertEqual(ret, [self.mock_policies[1], self.mock_policies[3]])
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class CreateStoragePolicyTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.create_storage_policy'''
|
||||
def setUp(self):
|
||||
self.mock_policy_spec = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock()
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_policy_spec', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_create_policy(self):
|
||||
salt.utils.pbm.create_storage_policy(self.mock_prof_mgr,
|
||||
self.mock_policy_spec)
|
||||
self.mock_prof_mgr.Create.assert_called_once_with(
|
||||
self.mock_policy_spec)
|
||||
|
||||
def test_create_policy_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.Create = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.create_storage_policy(self.mock_prof_mgr,
|
||||
self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_create_policy_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.Create = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.create_storage_policy(self.mock_prof_mgr,
|
||||
self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_create_policy_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.Create = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.create_storage_policy(self.mock_prof_mgr,
|
||||
self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class UpdateStoragePolicyTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.update_storage_policy'''
|
||||
def setUp(self):
|
||||
self.mock_policy_spec = MagicMock()
|
||||
self.mock_policy = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock()
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_policy_spec', 'mock_policy', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_create_policy(self):
|
||||
salt.utils.pbm.update_storage_policy(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec)
|
||||
self.mock_prof_mgr.Update.assert_called_once_with(
|
||||
self.mock_policy.profileId, self.mock_policy_spec)
|
||||
|
||||
def test_create_policy_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.Update = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.update_storage_policy(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_create_policy_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.Update = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.update_storage_policy(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_create_policy_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.Update = MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.update_storage_policy(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class GetDefaultStoragePolicyOfDatastoreTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.get_default_storage_policy_of_datastore'''
|
||||
def setUp(self):
|
||||
self.mock_ds = MagicMock(_moId='fake_ds_moid')
|
||||
self.mock_hub = MagicMock()
|
||||
self.mock_policy_id = 'fake_policy_id'
|
||||
self.mock_prof_mgr = MagicMock(
|
||||
QueryDefaultRequirementProfile=MagicMock(
|
||||
return_value=self.mock_policy_id))
|
||||
self.mock_policy_refs = [MagicMock()]
|
||||
patches = (
|
||||
('salt.utils.pbm.pbm.placement.PlacementHub',
|
||||
MagicMock(return_value=self.mock_hub)),
|
||||
('salt.utils.pbm.get_policies_by_id',
|
||||
MagicMock(return_value=self.mock_policy_refs)))
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_ds', 'mock_hub', 'mock_policy_id', 'mock_prof_mgr',
|
||||
'mock_policy_refs'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_placement_hub(self):
|
||||
mock_get_placement_hub = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.placement.PlacementHub',
|
||||
mock_get_placement_hub):
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
mock_get_placement_hub.assert_called_once_with(
|
||||
hubId='fake_ds_moid', hubType='Datastore')
|
||||
|
||||
def test_query_default_requirement_profile(self):
|
||||
mock_query_prof = MagicMock(return_value=self.mock_policy_id)
|
||||
self.mock_prof_mgr.QueryDefaultRequirementProfile = \
|
||||
mock_query_prof
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
mock_query_prof.assert_called_once_with(self.mock_hub)
|
||||
|
||||
def test_query_default_requirement_profile_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.QueryDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_query_default_requirement_profile_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.QueryDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_query_default_requirement_profile_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.QueryDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
||||
|
||||
def test_get_policies_by_id(self):
|
||||
mock_get_policies_by_id = MagicMock()
|
||||
with patch('salt.utils.pbm.get_policies_by_id',
|
||||
mock_get_policies_by_id):
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
mock_get_policies_by_id.assert_called_once_with(
|
||||
self.mock_prof_mgr, [self.mock_policy_id])
|
||||
|
||||
def test_no_policy_refs(self):
|
||||
mock_get_policies_by_id = MagicMock()
|
||||
with patch('salt.utils.pbm.get_policies_by_id',
|
||||
MagicMock(return_value=None)):
|
||||
with self.assertRaises(VMwareObjectRetrievalError) as excinfo:
|
||||
salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Storage policy with id \'fake_policy_id\' was not '
|
||||
'found')
|
||||
|
||||
def test_return_policy_ref(self):
|
||||
mock_get_policies_by_id = MagicMock()
|
||||
ret = salt.utils.pbm.get_default_storage_policy_of_datastore(
|
||||
self.mock_prof_mgr, self.mock_ds)
|
||||
self.assertEqual(ret, self.mock_policy_refs[0])
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class AssignDefaultStoragePolicyToDatastoreTestCase(TestCase):
|
||||
'''Tests for salt.utils.pbm.assign_default_storage_policy_to_datastore'''
|
||||
def setUp(self):
|
||||
self.mock_ds = MagicMock(_moId='fake_ds_moid')
|
||||
self.mock_policy = MagicMock()
|
||||
self.mock_hub = MagicMock()
|
||||
self.mock_prof_mgr = MagicMock()
|
||||
patches = (
|
||||
('salt.utils.pbm.pbm.placement.PlacementHub',
|
||||
MagicMock(return_value=self.mock_hub)),)
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_ds', 'mock_hub', 'mock_policy', 'mock_prof_mgr'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_get_placement_hub(self):
|
||||
mock_get_placement_hub = MagicMock()
|
||||
with patch('salt.utils.pbm.pbm.placement.PlacementHub',
|
||||
mock_get_placement_hub):
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_ds)
|
||||
mock_get_placement_hub.assert_called_once_with(
|
||||
hubId='fake_ds_moid', hubType='Datastore')
|
||||
|
||||
def test_assign_default_requirement_profile(self):
|
||||
mock_assign_prof = MagicMock()
|
||||
self.mock_prof_mgr.AssignDefaultRequirementProfile = \
|
||||
mock_assign_prof
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_ds)
|
||||
mock_assign_prof.assert_called_once_with(
|
||||
self.mock_policy.profileId, [self.mock_hub])
|
||||
|
||||
def test_assign_default_requirement_profile_raises_no_permissions(self):
|
||||
exc = vim.fault.NoPermission()
|
||||
exc.privilegeId = 'Fake privilege'
|
||||
self.mock_prof_mgr.AssignDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror,
|
||||
'Not enough permissions. Required privilege: '
|
||||
'Fake privilege')
|
||||
|
||||
def test_assign_default_requirement_profile_raises_vim_fault(self):
|
||||
exc = vim.fault.VimFault()
|
||||
exc.msg = 'VimFault msg'
|
||||
self.mock_prof_mgr.AssignDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareApiError) as excinfo:
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror, 'VimFault msg')
|
||||
|
||||
def test_assign_default_requirement_profile_raises_runtime_fault(self):
|
||||
exc = vmodl.RuntimeFault()
|
||||
exc.msg = 'RuntimeFault msg'
|
||||
self.mock_prof_mgr.AssignDefaultRequirementProfile = \
|
||||
MagicMock(side_effect=exc)
|
||||
with self.assertRaises(VMwareRuntimeError) as excinfo:
|
||||
salt.utils.pbm.assign_default_storage_policy_to_datastore(
|
||||
self.mock_prof_mgr, self.mock_policy, self.mock_ds)
|
||||
self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg')
|
|
@ -13,6 +13,7 @@ import ssl
|
|||
import sys
|
||||
|
||||
# Import Salt testing libraries
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, call, \
|
||||
PropertyMock
|
||||
|
@ -852,6 +853,96 @@ class IsConnectionToAVCenterTestCase(TestCase):
|
|||
excinfo.exception.strerror)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class GetNewServiceInstanceStub(TestCase, LoaderModuleMockMixin):
|
||||
'''Tests for salt.utils.vmware.get_new_service_instance_stub'''
|
||||
def setup_loader_modules(self):
|
||||
return {salt.utils.vmware: {
|
||||
'__virtual__': MagicMock(return_value='vmware'),
|
||||
'sys': MagicMock(),
|
||||
'ssl': MagicMock()}}
|
||||
|
||||
def setUp(self):
|
||||
self.mock_stub = MagicMock(
|
||||
host='fake_host:1000',
|
||||
cookie='ignore"fake_cookie')
|
||||
self.mock_si = MagicMock(
|
||||
_stub=self.mock_stub)
|
||||
self.mock_ret = MagicMock()
|
||||
self.mock_new_stub = MagicMock()
|
||||
self.context_dict = {}
|
||||
patches = (('salt.utils.vmware.VmomiSupport.GetRequestContext',
|
||||
MagicMock(
|
||||
return_value=self.context_dict)),
|
||||
('salt.utils.vmware.SoapStubAdapter',
|
||||
MagicMock(return_value=self.mock_new_stub)))
|
||||
for mod, mock in patches:
|
||||
patcher = patch(mod, mock)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
type(salt.utils.vmware.sys).version_info = \
|
||||
PropertyMock(return_value=(2, 7, 9))
|
||||
self.mock_context = MagicMock()
|
||||
self.mock_create_default_context = \
|
||||
MagicMock(return_value=self.mock_context)
|
||||
salt.utils.vmware.ssl.create_default_context = \
|
||||
self.mock_create_default_context
|
||||
|
||||
def tearDown(self):
|
||||
for attr in ('mock_stub', 'mock_si', 'mock_ret', 'mock_new_stub',
|
||||
'context_dict', 'mock_context',
|
||||
'mock_create_default_context'):
|
||||
delattr(self, attr)
|
||||
|
||||
def test_ssl_default_context_loaded(self):
|
||||
salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path')
|
||||
self.mock_create_default_context.assert_called_once_with()
|
||||
self.assertFalse(self.mock_context.check_hostname)
|
||||
self.assertEqual(self.mock_context.verify_mode,
|
||||
salt.utils.vmware.ssl.CERT_NONE)
|
||||
|
||||
def test_ssl_default_context_not_loaded(self):
|
||||
type(salt.utils.vmware.sys).version_info = \
|
||||
PropertyMock(return_value=(2, 7, 8))
|
||||
salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path')
|
||||
self.assertEqual(self.mock_create_default_context.call_count, 0)
|
||||
|
||||
def test_session_cookie_in_context(self):
|
||||
salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path')
|
||||
self.assertEqual(self.context_dict['vcSessionCookie'], 'fake_cookie')
|
||||
|
||||
def test_get_new_stub(self):
|
||||
mock_get_new_stub = MagicMock()
|
||||
with patch('salt.utils.vmware.SoapStubAdapter', mock_get_new_stub):
|
||||
salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path', 'fake_ns', 'fake_version')
|
||||
mock_get_new_stub.assert_called_once_with(
|
||||
host='fake_host', ns='fake_ns', path='fake_path',
|
||||
version='fake_version', poolSize=0, sslContext=self.mock_context)
|
||||
|
||||
def test_get_new_stub_2_7_8_python(self):
|
||||
type(salt.utils.vmware.sys).version_info = \
|
||||
PropertyMock(return_value=(2, 7, 8))
|
||||
mock_get_new_stub = MagicMock()
|
||||
with patch('salt.utils.vmware.SoapStubAdapter', mock_get_new_stub):
|
||||
salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path', 'fake_ns', 'fake_version')
|
||||
mock_get_new_stub.assert_called_once_with(
|
||||
host='fake_host', ns='fake_ns', path='fake_path',
|
||||
version='fake_version', poolSize=0, sslContext=None)
|
||||
|
||||
def test_new_stub_returned(self):
|
||||
ret = salt.utils.vmware.get_new_service_instance_stub(
|
||||
self.mock_si, 'fake_path')
|
||||
self.assertEqual(self.mock_new_stub.cookie, 'ignore"fake_cookie')
|
||||
self.assertEqual(ret, self.mock_new_stub)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing')
|
||||
class GetServiceInstanceFromManagedObjectTestCase(TestCase):
|
||||
|
|
Loading…
Add table
Reference in a new issue