Merge pull request #43674 from alexbleotu/storage_policies-gh

VMware vCenter proxy + storage policies states & dependencies
This commit is contained in:
Nicole Thomas 2017-09-27 14:56:39 -04:00 committed by GitHub
commit e4be436ca1
10 changed files with 2412 additions and 18 deletions

View file

@ -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
View 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']()

View file

@ -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
View 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
View 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
View 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)

View file

@ -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.

View file

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

View 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')

View file

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