mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
Added cluster_configured state to create/manage ESX clusters
This commit is contained in:
parent
359069904b
commit
08100d719d
250
salt/states/esxcluster.py
Normal file
250
salt/states/esxcluster.py
Normal file
@ -0,0 +1,250 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage VMware ESXi Clusters.
|
||||
|
||||
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.
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.exceptions
|
||||
from salt.utils.dictdiffer import recursive_diff
|
||||
from salt.utils.listdiffer import list_diff
|
||||
from salt.config.schemas.esxcluster import ESXClusterConfigSchema
|
||||
from salt.utils import dictupdate
|
||||
|
||||
# External libraries
|
||||
try:
|
||||
import jsonschema
|
||||
HAS_JSONSCHEMA = True
|
||||
except ImportError:
|
||||
HAS_JSONSCHEMA = False
|
||||
|
||||
# Get Logging Started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
return HAS_JSONSCHEMA
|
||||
|
||||
|
||||
def mod_init(low):
|
||||
'''
|
||||
Retrieves and adapt the login credentials from the proxy connection module
|
||||
'''
|
||||
return True
|
||||
|
||||
|
||||
def cluster_configured(name, cluster_config):
|
||||
'''
|
||||
Configures a cluster. Creates a new cluster, if it doesn't exist on the
|
||||
vCenter or reconfigures it if configured differently
|
||||
|
||||
Supported proxies: esxdatacenter, esxcluster
|
||||
|
||||
name
|
||||
Name of the state. If the state is run in by an ``esxdatacenter``
|
||||
proxy, it will be the name of the cluster.
|
||||
|
||||
cluster_config
|
||||
Configuration applied to the cluster.
|
||||
Complex datastructure following the ESXClusterConfigSchema.
|
||||
Valid example is:
|
||||
|
||||
.. code-block::yaml
|
||||
|
||||
drs:
|
||||
default_vm_behavior: fullyAutomated
|
||||
enabled: true
|
||||
vmotion_rate: 3
|
||||
ha:
|
||||
admission_control
|
||||
_enabled: false
|
||||
default_vm_settings:
|
||||
isolation_response: powerOff
|
||||
restart_priority: medium
|
||||
enabled: true
|
||||
hb_ds_candidate_policy: userSelectedDs
|
||||
host_monitoring: enabled
|
||||
options:
|
||||
- key: das.ignoreinsufficienthbdatastore
|
||||
value: 'true'
|
||||
vm_monitoring: vmMonitoringDisabled
|
||||
vm_swap_placement: vmDirectory
|
||||
vsan:
|
||||
auto_claim_storage: false
|
||||
compression_enabled: true
|
||||
dedup_enabled: true
|
||||
enabled: true
|
||||
|
||||
'''
|
||||
proxy_type = __salt__['vsphere.get_proxy_type']()
|
||||
if proxy_type == 'esxdatacenter':
|
||||
cluster_name, datacenter_name = \
|
||||
name, __salt__['esxdatacenter.get_details']()['datacenter']
|
||||
elif proxy_type == 'esxcluster':
|
||||
cluster_name, datacenter_name = \
|
||||
__salt__['esxcluster.get_details']()['cluster'], \
|
||||
__salt__['esxcluster.get_details']()['datacenter']
|
||||
else:
|
||||
raise salt.exceptions.CommandExecutionError('Unsupported proxy {0}'
|
||||
''.format(proxy_type))
|
||||
log.info('Running {0} for cluster \'{1}\' in datacenter '
|
||||
'\'{2}\''.format(name, cluster_name, datacenter_name))
|
||||
cluster_dict = cluster_config
|
||||
log.trace('cluster_dict = {0}'.format(cluster_dict))
|
||||
changes_required = False
|
||||
ret = {'name': name,
|
||||
'changes': {}, 'result': None, 'comment': 'Default'}
|
||||
comments = []
|
||||
changes = {}
|
||||
changes_required = False
|
||||
|
||||
try:
|
||||
log.debug('Validating cluster_configured state input')
|
||||
schema = ESXClusterConfigSchema.serialize()
|
||||
log.trace('schema = {0}'.format(schema))
|
||||
try:
|
||||
jsonschema.validate(cluster_dict, schema)
|
||||
except jsonschema.exceptions.ValidationError as exc:
|
||||
raise salt.exceptions.InvalidESXClusterPayloadError(exc)
|
||||
current = None
|
||||
si = __salt__['vsphere.get_service_instance_via_proxy']()
|
||||
try:
|
||||
current = __salt__['vsphere.list_cluster'](datacenter_name,
|
||||
cluster_name,
|
||||
service_instance=si)
|
||||
except salt.exceptions.VMwareObjectRetrievalError:
|
||||
changes_required = True
|
||||
if __opts__['test']:
|
||||
comments.append('State {0} will create cluster '
|
||||
'\'{1}\' in datacenter \'{2}\'.'
|
||||
''.format(name, cluster_name, datacenter_name))
|
||||
log.info(comments[-1])
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
ret.update({'result': None,
|
||||
'comment': '\n'.join(comments)})
|
||||
return ret
|
||||
log.debug ('Creating cluster \'{0}\' in datacenter \'{1}\'. '
|
||||
''.format(cluster_name, datacenter_name))
|
||||
__salt__['vsphere.create_cluster'](cluster_dict,
|
||||
datacenter_name,
|
||||
cluster_name,
|
||||
service_instance=si)
|
||||
comments.append('Created cluster \'{0}\' in datacenter \'{1}\''
|
||||
''.format(cluster_name, datacenter_name))
|
||||
log.info(comments[-1])
|
||||
changes.update({'new': cluster_dict})
|
||||
if current:
|
||||
# Cluster already exists
|
||||
# We need to handle lists sepparately
|
||||
ldiff = None
|
||||
if 'ha' in cluster_dict and 'options' in cluster_dict['ha']:
|
||||
ldiff = list_diff(current.get('ha', {}).get('options', []),
|
||||
cluster_dict.get('ha', {}).get('options', []),
|
||||
'key')
|
||||
log.trace('options diffs = {0}'.format(ldiff.diffs))
|
||||
# Remove options if exist
|
||||
del cluster_dict['ha']['options']
|
||||
if 'ha' in current and 'options' in current['ha']:
|
||||
del current['ha']['options']
|
||||
diff = recursive_diff(current, cluster_dict)
|
||||
log.trace('diffs = {0}'.format(diff.diffs))
|
||||
if not (diff.diffs or (ldiff and ldiff.diffs)):
|
||||
# No differences
|
||||
comments.append('Cluster \'{0}\' in datacenter \'{1}\' is up '
|
||||
'to date. Nothing to be done.'
|
||||
''.format(cluster_name, datacenter_name))
|
||||
log.info(comments[-1])
|
||||
else:
|
||||
changes_required = True
|
||||
changes_str = ''
|
||||
if diff.diffs:
|
||||
changes_str = '{0}{1}'.format(changes_str,
|
||||
diff.changes_str)
|
||||
if ldiff and ldiff.diffs:
|
||||
changes_str = '{0}\nha:\n options:\n{1}'.format(
|
||||
changes_str,
|
||||
'\n'.join([' {0}'.format(l) for l in
|
||||
ldiff.changes_str2.split('\n')]))
|
||||
# Apply the changes
|
||||
if __opts__['test']:
|
||||
comments.append(
|
||||
'State {0} will update cluster \'{1}\' '
|
||||
'in datacenter \'{2}\':\n{3}'
|
||||
''.format(name, cluster_name,
|
||||
datacenter_name, changes_str))
|
||||
else:
|
||||
new_values = diff.new_values
|
||||
old_values = diff.old_values
|
||||
if ldiff and ldiff.new_values:
|
||||
dictupdate.update(
|
||||
new_values, {'ha': {'options': ldiff.new_values}})
|
||||
if ldiff and ldiff.old_values:
|
||||
dictupdate.update(
|
||||
old_values, {'ha': {'options': ldiff.old_values}})
|
||||
log.trace('new_values = {0}'.format(new_values))
|
||||
__salt__['vsphere.update_cluster'](new_values,
|
||||
datacenter_name,
|
||||
cluster_name,
|
||||
service_instance=si)
|
||||
comments.append('Updated cluster \'{0}\' in datacenter '
|
||||
'\'{1}\''.format(cluster_name,
|
||||
datacenter_name))
|
||||
log.info(comments[-1])
|
||||
changes.update({'new': new_values,
|
||||
'old': old_values})
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
ret_status = True
|
||||
if __opts__['test'] and changes_required:
|
||||
ret_status = None
|
||||
ret.update({'result': ret_status,
|
||||
'comment': '\n'.join(comments),
|
||||
'changes': changes})
|
||||
return ret
|
||||
except salt.exceptions.CommandExecutionError as exc:
|
||||
log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc()))
|
||||
if si:
|
||||
__salt__['vsphere.disconnect'](si)
|
||||
ret.update({
|
||||
'result': False,
|
||||
'comment': str(exc)})
|
||||
return ret
|
Loading…
Reference in New Issue
Block a user