salt/states/glassfish.py
# -*- coding: utf-8 -*-
'''
Manage Glassfish/Payara server
.. versionadded:: Carbon
Management of glassfish using it's RESTful API
You can setup connection parameters like this
.. code-block:: yaml
- server:
- ssl: true
- url: localhost
- port: 4848
- user: admin
- password: changeit
'''
from __future__ import absolute_import, print_function, unicode_literals
try:
import salt.utils.json
from salt.ext import six
from salt.exceptions import CommandExecutionError
import requests
HAS_LIBS = True
except ImportError:
HAS_LIBS = False
def __virtual__():
'''
Only load if glassfish module is available
'''
return 'glassfish.enum_connector_c_pool' in __salt__ and HAS_LIBS
def _json_to_unicode(data):
'''
Encode json values in unicode to match that of the API
'''
ret = {}
for key, value in data.items():
if not isinstance(value, six.text_type):
if isinstance(value, dict):
ret[key] = _json_to_unicode(value)
else:
ret[key] = six.text_type(value).lower()
else:
ret[key] = value
return ret
def _is_updated(old_conf, new_conf):
'''
Compare the API results to the current statefile data
'''
changed = {}
# Dirty json hacking to get parameters in the same format
new_conf = _json_to_unicode(salt.utils.json.loads(
salt.utils.json.dumps(new_conf, ensure_ascii=False)))
old_conf = salt.utils.json.loads(salt.utils.json.dumps(old_conf, ensure_ascii=False))
for key, value in old_conf.items():
oldval = six.text_type(value).lower()
if key in new_conf:
newval = six.text_type(new_conf[key]).lower()
if oldval == 'null' or oldval == 'none':
oldval = ''
if key in new_conf and newval != oldval:
changed[key] = {'old': oldval, 'new': newval}
return changed
def _do_element_present(name, elem_type, data, server=None):
'''
Generic function to create or update an element
'''
ret = {'changes': {}, 'update': False, 'create': False, 'error': None}
try:
elements = __salt__['glassfish.enum_{0}'.format(elem_type)]()
except requests.ConnectionError as error:
if __opts__['test']:
ret['changes'] = {'Name': name, 'Params': data}
ret['create'] = True
return ret
else:
ret['error'] = "Can't connect to the server"
return ret
if not elements or name not in elements:
ret['changes'] = {'Name': name, 'Params': data}
ret['create'] = True
if not __opts__['test']:
try:
__salt__['glassfish.create_{0}'.format(elem_type)](name, server=server, **data)
except CommandExecutionError as error:
ret['error'] = error
return ret
elif elements and any(data):
current_data = __salt__['glassfish.get_{0}'.format(elem_type)](name, server=server)
data_diff = _is_updated(current_data, data)
if data_diff:
ret['update'] = True
ret['changes'] = data_diff
if not __opts__['test']:
try:
__salt__['glassfish.update_{0}'.format(elem_type)](name, server=server, **data)
except CommandExecutionError as error:
ret['error'] = error
return ret
def _do_element_absent(name, elem_type, data, server=None):
'''
Generic function to delete an element
'''
ret = {'delete': False, 'error': None}
try:
elements = __salt__['glassfish.enum_{0}'.format(elem_type)]()
except requests.ConnectionError as error:
if __opts__['test']:
ret['create'] = True
return ret
else:
ret['error'] = "Can't connect to the server"
return ret
if elements and name in elements:
ret['delete'] = True
if not __opts__['test']:
try:
__salt__['glassfish.delete_{0}'.format(elem_type)](name, server=server, **data)
except CommandExecutionError as error:
ret['error'] = error
return ret
def connection_factory_present(name,
restype='connection_factory',
description='',
enabled=True,
min_size=1,
max_size=250,
resize_quantity=2,
idle_timeout=300,
wait_timeout=60,
reconnect_on_failure=False,
transaction_support='',
connection_validation=False,
server=None):
'''
Ensures that the Connection Factory is present
name
Name of the connection factory
restype
Type of the connection factory, can be either ``connection_factory``,
``queue_connection_factory` or ``topic_connection_factory``,
defaults to ``connection_factory``
description
Description of the connection factory
enabled
Is the connection factory enabled? defaults to ``true``
min_size
Minimum and initial number of connections in the pool, defaults to ``1``
max_size
Maximum number of connections that can be created in the pool, defaults to ``250``
resize_quantity
Number of connections to be removed when idle_timeout expires, defaults to ``2``
idle_timeout
Maximum time a connection can remain idle in the pool, in seconds, defaults to ``300``
wait_timeout
Maximum time a caller can wait before timeout, in seconds, defaults to ``60``
reconnect_on_failure
Close all connections and reconnect on failure (or reconnect only when used), defaults to ``false``
transaction_support
Level of transaction support, can be either ``XATransaction``, ``LocalTransaction`` or ``NoTransaction``
connection_validation
Connection validation is required, defaults to ``false``
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
# Manage parameters
pool_data = {}
res_data = {}
pool_name = '{0}-Connection-Pool'.format(name)
if restype == 'topic_connection_factory':
pool_data['connectionDefinitionName'] = 'javax.jms.TopicConnectionFactory'
elif restype == 'queue_connection_factory':
pool_data['connectionDefinitionName'] = 'javax.jms.QueueConnectionFactory'
elif restype == 'connection_factory':
pool_data['connectionDefinitionName'] = 'javax.jms.ConnectionFactory'
else:
ret['result'] = False
ret['comment'] = 'Invalid restype'
return ret
pool_data['description'] = description
res_data['description'] = description
res_data['enabled'] = enabled
res_data['poolName'] = pool_name
pool_data['steadyPoolSize'] = min_size
pool_data['maxPoolSize'] = max_size
pool_data['poolResizeQuantity'] = resize_quantity
pool_data['idleTimeoutInSeconds'] = idle_timeout
pool_data['maxWaitTimeInMillis'] = wait_timeout*1000
pool_data['failAllConnections'] = reconnect_on_failure
if transaction_support:
if transaction_support == 'xa_transaction':
pool_data['transactionSupport'] = 'XATransaction'
elif transaction_support == 'local_transaction':
pool_data['transactionSupport'] = 'LocalTransaction'
elif transaction_support == 'no_transaction':
pool_data['transactionSupport'] = 'NoTransaction'
else:
ret['result'] = False
ret['comment'] = 'Invalid transaction_support'
return ret
pool_data['isConnectionValidationRequired'] = connection_validation
pool_ret = _do_element_present(pool_name, 'connector_c_pool', pool_data, server)
res_ret = _do_element_present(name, 'connector_resource', res_data, server)
if not pool_ret['error'] and not res_ret['error']:
if not __opts__['test']:
ret['result'] = True
if pool_ret['create'] or res_ret['create']:
ret['changes']['pool'] = pool_ret['changes']
ret['changes']['resource'] = res_ret['changes']
if __opts__['test']:
ret['comment'] = 'Connection factory set to be created'
else:
ret['comment'] = 'Connection factory created'
elif pool_ret['update'] or res_ret['update']:
ret['changes']['pool'] = pool_ret['changes']
ret['changes']['resource'] = res_ret['changes']
if __opts__['test']:
ret['comment'] = 'Connection factory set to be updated'
else:
ret['comment'] = 'Connection factory updated'
else:
ret['result'] = True
ret['changes'] = {}
ret['comment'] = 'Connection factory is already up-to-date'
else:
ret['result'] = False
ret['comment'] = 'ERROR: {0} // {1}'.format(pool_ret['error'], res_ret['error'])
return ret
def connection_factory_absent(name, both=True, server=None):
'''
Ensures the transaction factory is absent.
name
Name of the connection factory
both
Delete both the pool and the resource, defaults to ``true``
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
pool_name = '{0}-Connection-Pool'.format(name)
pool_ret = _do_element_absent(pool_name, 'connector_c_pool', {'cascade': both}, server)
if not pool_ret['error']:
if __opts__['test'] and pool_ret['delete']:
ret['comment'] = 'Connection Factory set to be deleted'
elif pool_ret['delete']:
ret['result'] = True
ret['comment'] = 'Connection Factory deleted'
else:
ret['result'] = True
ret['comment'] = 'Connection Factory doesn\'t exist'
else:
ret['result'] = False
ret['comment'] = 'Error: {0}'.format(pool_ret['error'])
return ret
def destination_present(name,
physical,
restype='queue',
description='',
enabled=True,
server=None):
'''
Ensures that the JMS Destination Resource (queue or topic) is present
name
The JMS Queue/Topic name
physical
The Physical destination name
restype
The JMS Destination resource type, either ``queue`` or ``topic``, defaults is ``queue``
description
A description of the resource
enabled
Defaults to ``True``
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
params = {}
# Set parameters dict
if restype == 'queue':
params['resType'] = 'javax.jms.Queue'
params['className'] = 'com.sun.messaging.Queue'
elif restype == 'topic':
params['resType'] = 'javax.jms.Topic'
params['className'] = 'com.sun.messaging.Topic'
else:
ret['result'] = False
ret['comment'] = 'Invalid restype'
return ret
params['properties'] = {'Name': physical}
params['description'] = description
params['enabled'] = enabled
jms_ret = _do_element_present(name, 'admin_object_resource', params, server)
if not jms_ret['error']:
if not __opts__['test']:
ret['result'] = True
if jms_ret['create'] and __opts__['test']:
ret['comment'] = 'JMS Queue set to be created'
elif jms_ret['create']:
ret['changes'] = jms_ret['changes']
ret['comment'] = 'JMS queue created'
elif jms_ret['update'] and __opts__['test']:
ret['comment'] = 'JMS Queue set to be updated'
elif jms_ret['update']:
ret['changes'] = jms_ret['changes']
ret['comment'] = 'JMS Queue updated'
else:
ret['result'] = True
ret['comment'] = 'JMS Queue already up-to-date'
else:
ret['result'] = False
ret['comment'] = 'Error from API: {0}'.format(jms_ret['error'])
return ret
def destination_absent(name, server=None):
'''
Ensures that the JMS Destination doesn't exists
name
Name of the JMS Destination
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
jms_ret = _do_element_absent(name, 'admin_object_resource', {}, server)
if not jms_ret['error']:
if __opts__['test'] and jms_ret['delete']:
ret['comment'] = 'JMS Queue set to be deleted'
elif jms_ret['delete']:
ret['result'] = True
ret['comment'] = 'JMS Queue deleted'
else:
ret['result'] = True
ret['comment'] = 'JMS Queue doesn\'t exist'
else:
ret['result'] = False
ret['comment'] = 'Error: {0}'.format(jms_ret['error'])
return ret
def jdbc_datasource_present(name,
description='',
enabled=True,
restype='datasource',
vendor='mysql',
sql_url='',
sql_user='',
sql_password='',
min_size=8,
max_size=32,
resize_quantity=2,
idle_timeout=300,
wait_timeout=60,
non_transactional=False,
transaction_isolation='',
isolation_guaranteed=True,
server=None):
'''
Ensures that the JDBC Datasource exists
name
Name of the datasource
description
Description of the datasource
enabled
Is the datasource enabled? defaults to ``true``
restype
Resource type, can be ``datasource``, ``xa_datasource``,
``connection_pool_datasource`` or ``driver``, defaults to ``datasource``
vendor
SQL Server type, currently supports ``mysql``,
``postgresql`` and ``mssql``, defaults to ``mysql``
sql_url
URL of the server in jdbc form
sql_user
Username for the server
sql_password
Password for that username
min_size
Minimum and initial number of connections in the pool, defaults to ``8``
max_size
Maximum number of connections that can be created in the pool, defaults to ``32``
resize_quantity
Number of connections to be removed when idle_timeout expires, defaults to ``2``
idle_timeout
Maximum time a connection can remain idle in the pool, in seconds, defaults to ``300``
wait_timeout
Maximum time a caller can wait before timeout, in seconds, defaults to ``60``
non_transactional
Return non-transactional connections
transaction_isolation
Defaults to the JDBC driver default
isolation_guaranteed
All connections use the same isolation level
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
# Manage parameters
res_name = 'jdbc/{0}'.format(name)
pool_data = {}
pool_data_properties = {}
res_data = {}
if restype == 'datasource':
pool_data['resType'] = 'javax.sql.DataSource'
elif restype == 'xa_datasource':
pool_data['resType'] = 'javax.sql.XADataSource'
elif restype == 'connection_pool_datasource':
pool_data['resType'] = 'javax.sql.ConnectionPoolDataSource'
elif restype == 'driver':
pool_data['resType'] = 'javax.sql.Driver'
datasources = {}
datasources['mysql'] = {
'driver': 'com.mysql.jdbc.Driver',
'datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlDataSource',
'xa_datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlXADataSource',
'connection_pool_datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource'
}
datasources['postgresql'] = {
'driver': 'org.postgresql.Driver',
'datasource': 'org.postgresql.ds.PGSimpleDataSource',
'xa_datasource': 'org.postgresql.xa.PGXADataSource',
'connection_pool_datasource': 'org.postgresql.ds.PGConnectionPoolDataSource'
}
datasources['mssql'] = {
'driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
'datasource': 'com.microsoft.sqlserver.jdbc.SQLServerDataSource',
'xa_datasource': 'com.microsoft.sqlserver.jdbc.SQLServerXADataSource',
'connection_pool_datasource': 'com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource'
}
if restype == 'driver':
pool_data['driverClassname'] = datasources[vendor]['driver']
else:
pool_data['datasourceClassname'] = datasources[vendor][restype]
pool_data_properties['url'] = sql_url
pool_data_properties['user'] = sql_user
pool_data_properties['password'] = sql_password
pool_data['properties'] = pool_data_properties
pool_data['description'] = description
res_data['description'] = description
res_data['poolName'] = name
res_data['enabled'] = enabled
pool_data['steadyPoolSize'] = min_size
pool_data['maxPoolSize'] = max_size
pool_data['poolResizeQuantity'] = resize_quantity
pool_data['idleTimeoutInSeconds'] = idle_timeout
pool_data['maxWaitTimeInMillis'] = wait_timeout*1000
pool_data['nonTransactionalConnections'] = non_transactional
pool_data['transactionIsolationLevel'] = transaction_isolation
pool_data['isIsolationLevelGuaranteed'] = isolation_guaranteed
pool_ret = _do_element_present(name, 'jdbc_connection_pool', pool_data, server)
res_ret = _do_element_present(res_name, 'jdbc_resource', res_data, server)
if not pool_ret['error'] and not res_ret['error']:
if not __opts__['test']:
ret['result'] = True
if pool_ret['create'] or res_ret['create']:
ret['changes']['pool'] = pool_ret['changes']
ret['changes']['resource'] = res_ret['changes']
if __opts__['test']:
ret['comment'] = 'JDBC Datasource set to be created'
else:
ret['comment'] = 'JDBC Datasource created'
elif pool_ret['update'] or res_ret['update']:
ret['changes']['pool'] = pool_ret['changes']
ret['changes']['resource'] = res_ret['changes']
if __opts__['test']:
ret['comment'] = 'JDBC Datasource set to be updated'
else:
ret['comment'] = 'JDBC Datasource updated'
else:
ret['result'] = True
ret['changes'] = {}
ret['comment'] = 'JDBC Datasource is already up-to-date'
else:
ret['result'] = False
ret['comment'] = 'ERROR: {0} // {1}'.format(pool_ret['error'], res_ret['error'])
return ret
def jdbc_datasource_absent(name, both=True, server=None):
'''
Ensures the JDBC Datasource doesn't exists
name
Name of the datasource
both
Delete both the pool and the resource, defaults to ``true``
'''
ret = {'name': name, 'result': None, 'comment': None, 'changes': {}}
pool_ret = _do_element_absent(name, 'jdbc_connection_pool', {'cascade': both}, server)
if not pool_ret['error']:
if __opts__['test'] and pool_ret['delete']:
ret['comment'] = 'JDBC Datasource set to be deleted'
elif pool_ret['delete']:
ret['result'] = True
ret['comment'] = 'JDBC Datasource deleted'
else:
ret['result'] = True
ret['comment'] = 'JDBC Datasource doesn\'t exist'
else:
ret['result'] = False
ret['comment'] = 'Error: {0}'.format(pool_ret['error'])
return ret
def system_properties_present(server=None, **kwargs):
'''
Ensures that the system properties are present
properties
The system properties
'''
ret = {'name': '', 'result': None, 'comment': None, 'changes': {}}
del kwargs['name']
try:
data = __salt__['glassfish.get_system_properties'](server=server)
except requests.ConnectionError as error:
if __opts__['test']:
ret['changes'] = kwargs
ret['result'] = None
return ret
else:
ret['error'] = "Can't connect to the server"
return ret
ret['changes'] = {'data': data, 'kwargs': kwargs}
if not data == kwargs:
data.update(kwargs)
if not __opts__['test']:
try:
__salt__['glassfish.update_system_properties'](data, server=server)
ret['changes'] = kwargs
ret['result'] = True
ret['comment'] = 'System properties updated'
except CommandExecutionError as error:
ret['comment'] = error
ret['result'] = False
else:
ret['result'] = None
ret['changes'] = kwargs
ret['coment'] = 'System properties would have been updated'
else:
ret['changes'] = {}
ret['result'] = True
ret['comment'] = 'System properties are already up-to-date'
return ret
def system_properties_absent(name, server=None):
'''
Ensures that the system property doesn't exists
name
Name of the system property
'''
ret = {'name': '', 'result': None, 'comment': None, 'changes': {}}
try:
data = __salt__['glassfish.get_system_properties'](server=server)
except requests.ConnectionError as error:
if __opts__['test']:
ret['changes'] = {'Name': name}
ret['result'] = None
return ret
else:
ret['error'] = "Can't connect to the server"
return ret
if name in data:
if not __opts__['test']:
try:
__salt__['glassfish.delete_system_properties'](name, server=server)
ret['result'] = True
ret['comment'] = 'System properties deleted'
except CommandExecutionError as error:
ret['comment'] = error
ret['result'] = False
else:
ret['result'] = None
ret['comment'] = 'System properties would have been deleted'
ret['changes'] = {'Name': name}
else:
ret['result'] = True
ret['comment'] = 'System properties are already absent'
return ret