mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge branch '2018.3.0rc1' into '2018.3'
No conflicts.
This commit is contained in:
commit
b03cda3cea
@ -786,6 +786,35 @@ A value of 10 minutes is a reasonable default.
|
||||
|
||||
grains_refresh_every: 0
|
||||
|
||||
.. conf_minion:: fibre_channel_grains
|
||||
|
||||
``fibre_channel_grains``
|
||||
------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
The ``fibre_channel_grains`` setting will enable the ``fc_wwn`` grain for
|
||||
Fibre Channel WWN's on the minion. Since this grain is expensive, it is
|
||||
disabled by default.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
fibre_channel_grains: True
|
||||
|
||||
.. conf_minion:: iscsi_grains
|
||||
|
||||
``iscsi_grains``
|
||||
------------------------
|
||||
|
||||
Default: ``False``
|
||||
|
||||
The ``iscsi_grains`` setting will enable the ``iscsi_iqn`` grain on the
|
||||
minion. Since this grain is expensive, it is disabled by default.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
iscsi_grains: True
|
||||
|
||||
.. conf_minion:: mine_enabled
|
||||
|
||||
``mine_enabled``
|
||||
|
@ -263,7 +263,7 @@ The use of ``require_any`` demands that one of the required states executes befo
|
||||
dependent state. The state containing the ``require_any`` requisite is defined as the
|
||||
dependent state. The states specified in the ``require_any`` statement are defined as the
|
||||
required states. If at least one of the required state's execution succeeds, the dependent state
|
||||
will then execute. If at least one of the required state's execution fails, the dependent state
|
||||
will then execute. If all of the executions by the required states fail, the dependent state
|
||||
will not execute.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
@ -521,6 +521,8 @@ In addition to the ``mapping`` and ``port`` options, the following additional op
|
||||
match a given Master. If set to ``any`` (the default), then any match to a
|
||||
key/value mapping will constitute a match.
|
||||
- ``pause`` - The interval in seconds between attempts (default: 5).
|
||||
- ``fibre_channel_grains`` - Enables the ``fc_wwn`` grain. (Default: False)
|
||||
- ``iscsi_grains`` - Enables the ``iscsi_iqn`` grain. (Default: False)
|
||||
|
||||
Connection to a type instead of DNS
|
||||
===================================
|
||||
@ -1522,7 +1524,7 @@ The use of ``require_any`` demands that one of the required states executes befo
|
||||
dependent state. The state containing the ``require_any`` requisite is defined as the
|
||||
dependent state. The states specified in the ``require_any`` statement are defined as the
|
||||
required states. If at least one of the required state's execution succeeds, the dependent state
|
||||
will then execute. If at least one of the required state's execution fails, the dependent state
|
||||
will then execute. If all of the executions by the required states fail, the dependent state
|
||||
will not execute.
|
||||
|
||||
- ``watch_any``
|
||||
|
@ -15,7 +15,6 @@ from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import glob
|
||||
import re
|
||||
import platform
|
||||
import logging
|
||||
@ -64,7 +63,6 @@ __salt__ = {
|
||||
'cmd.run_all': salt.modules.cmdmod._run_all_quiet,
|
||||
'smbios.records': salt.modules.smbios.records,
|
||||
'smbios.get': salt.modules.smbios.get,
|
||||
'cmd.run_ps': salt.modules.cmdmod.powershell,
|
||||
}
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -2456,123 +2454,3 @@ def default_gateway():
|
||||
except Exception:
|
||||
continue
|
||||
return grains
|
||||
|
||||
|
||||
def fc_wwn():
|
||||
'''
|
||||
Return list of fiber channel HBA WWNs
|
||||
'''
|
||||
grains = {}
|
||||
grains['fc_wwn'] = False
|
||||
if salt.utils.platform.is_linux():
|
||||
grains['fc_wwn'] = _linux_wwns()
|
||||
elif salt.utils.platform.is_windows():
|
||||
grains['fc_wwn'] = _windows_wwns()
|
||||
return grains
|
||||
|
||||
|
||||
def iscsi_iqn():
|
||||
'''
|
||||
Return iSCSI IQN
|
||||
'''
|
||||
grains = {}
|
||||
grains['iscsi_iqn'] = False
|
||||
if salt.utils.platform.is_linux():
|
||||
grains['iscsi_iqn'] = _linux_iqn()
|
||||
elif salt.utils.platform.is_windows():
|
||||
grains['iscsi_iqn'] = _windows_iqn()
|
||||
elif salt.utils.platform.is_aix():
|
||||
grains['iscsi_iqn'] = _aix_iqn()
|
||||
return grains
|
||||
|
||||
|
||||
def _linux_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from a Linux host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
initiator = '/etc/iscsi/initiatorname.iscsi'
|
||||
try:
|
||||
with salt.utils.files.fopen(initiator, 'r') as _iscsi:
|
||||
for line in _iscsi:
|
||||
line = line.strip()
|
||||
if line.startswith('InitiatorName='):
|
||||
ret.append(line.split('=', 1)[1])
|
||||
except IOError as ex:
|
||||
if ex.errno != os.errno.ENOENT:
|
||||
log.debug("Error while accessing '%s': %s", initiator, ex)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _aix_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from an AIX host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
aixcmd = 'lsattr -E -l iscsi0 | grep initiator_name'
|
||||
|
||||
aixret = __salt__['cmd.run'](aixcmd)
|
||||
if aixret[0].isalpha():
|
||||
try:
|
||||
ret.append(aixret.split()[1].rstrip())
|
||||
except IndexError:
|
||||
pass
|
||||
return ret
|
||||
|
||||
|
||||
def _linux_wwns():
|
||||
'''
|
||||
Return Fibre Channel port WWNs from a Linux host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
for fcfile in glob.glob('/sys/class/fc_host/*/port_name'):
|
||||
with salt.utils.files.fopen(fcfile, 'r') as _wwn:
|
||||
for line in _wwn:
|
||||
ret.append(line.rstrip()[2:])
|
||||
return ret
|
||||
|
||||
|
||||
def _windows_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from a Windows host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
wmic = salt.utils.path.which('wmic')
|
||||
|
||||
if not wmic:
|
||||
return ret
|
||||
|
||||
namespace = r'\\root\WMI'
|
||||
mspath = 'MSiSCSIInitiator_MethodClass'
|
||||
get = 'iSCSINodeName'
|
||||
|
||||
cmdret = __salt__['cmd.run_all'](
|
||||
'{0} /namespace:{1} path {2} get {3} /format:table'.format(
|
||||
wmic, namespace, mspath, get))
|
||||
|
||||
for line in cmdret['stdout'].splitlines():
|
||||
if line.startswith('iqn.'):
|
||||
line = line.rstrip()
|
||||
ret.append(line.rstrip())
|
||||
return ret
|
||||
|
||||
|
||||
def _windows_wwns():
|
||||
'''
|
||||
Return Fibre Channel port WWNs from a Windows host.
|
||||
'''
|
||||
ps_cmd = r'Get-WmiObject -ErrorAction Stop -class MSFC_FibrePortHBAAttributes -namespace "root\WMI" | Select -Expandproperty Attributes | %{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}'
|
||||
|
||||
ret = []
|
||||
|
||||
cmdret = __salt__['cmd.run_ps'](ps_cmd)
|
||||
|
||||
for line in cmdret:
|
||||
ret.append(line.rstrip())
|
||||
|
||||
return ret
|
||||
|
76
salt/grains/fibre_channel.py
Normal file
76
salt/grains/fibre_channel.py
Normal file
@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Grains for Fibre Channel WWN's. On Windows this runs a PowerShell command that
|
||||
queries WMI to get the Fibre Channel WWN's available.
|
||||
|
||||
.. versionadded:: 2018.3.0
|
||||
|
||||
To enable these grains set ``fibre_channel_grains: True``.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
fibre_channel_grains: True
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import glob
|
||||
import logging
|
||||
|
||||
# Import Salt libs
|
||||
import salt.modules.cmdmod
|
||||
import salt.utils.platform
|
||||
import salt.utils.files
|
||||
|
||||
__virtualname__ = 'fibre_channel'
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if __opts__.get('fibre_channel_grains', False) is False:
|
||||
return False
|
||||
else:
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def _linux_wwns():
|
||||
'''
|
||||
Return Fibre Channel port WWNs from a Linux host.
|
||||
'''
|
||||
ret = []
|
||||
for fc_file in glob.glob('/sys/class/fc_host/*/port_name'):
|
||||
with salt.utils.files.fopen(fc_file, 'r') as _wwn:
|
||||
content = _wwn.read()
|
||||
for line in content.splitlines():
|
||||
ret.append(line.rstrip()[2:])
|
||||
return ret
|
||||
|
||||
|
||||
def _windows_wwns():
|
||||
'''
|
||||
Return Fibre Channel port WWNs from a Windows host.
|
||||
'''
|
||||
ps_cmd = r'Get-WmiObject -ErrorAction Stop ' \
|
||||
r'-class MSFC_FibrePortHBAAttributes ' \
|
||||
r'-namespace "root\WMI" | ' \
|
||||
r'Select -Expandproperty Attributes | ' \
|
||||
r'%{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}'
|
||||
ret = []
|
||||
cmd_ret = salt.modules.cmdmod.powershell(ps_cmd)
|
||||
for line in cmd_ret:
|
||||
ret.append(line.rstrip())
|
||||
return ret
|
||||
|
||||
|
||||
def fibre_channel_wwns():
|
||||
'''
|
||||
Return list of fiber channel HBA WWNs
|
||||
'''
|
||||
grains = {'fc_wwn': False}
|
||||
if salt.utils.platform.is_linux():
|
||||
grains['fc_wwn'] = _linux_wwns()
|
||||
elif salt.utils.platform.is_windows():
|
||||
grains['fc_wwn'] = _windows_wwns()
|
||||
return grains
|
114
salt/grains/iscsi.py
Normal file
114
salt/grains/iscsi.py
Normal file
@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Grains for iSCSI Qualified Names (IQN).
|
||||
|
||||
.. versionadded:: 2018.3.0
|
||||
|
||||
To enable these grains set `iscsi_grains: True`.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
iscsi_grains: True
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Import Salt libs
|
||||
import salt.modules.cmdmod
|
||||
import salt.utils.files
|
||||
import salt.utils.path
|
||||
import salt.utils.platform
|
||||
|
||||
__virtualname__ = 'iscsi'
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if __opts__.get('iscsi_grains', False) is False:
|
||||
return False
|
||||
else:
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def iscsi_iqn():
|
||||
'''
|
||||
Return iSCSI IQN
|
||||
'''
|
||||
grains = {}
|
||||
grains['iscsi_iqn'] = False
|
||||
if salt.utils.platform.is_linux():
|
||||
grains['iscsi_iqn'] = _linux_iqn()
|
||||
elif salt.utils.platform.is_windows():
|
||||
grains['iscsi_iqn'] = _windows_iqn()
|
||||
elif salt.utils.platform.is_aix():
|
||||
grains['iscsi_iqn'] = _aix_iqn()
|
||||
return grains
|
||||
|
||||
|
||||
def _linux_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from a Linux host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
initiator = '/etc/iscsi/initiatorname.iscsi'
|
||||
try:
|
||||
with salt.utils.files.fopen(initiator, 'r') as _iscsi:
|
||||
for line in _iscsi:
|
||||
line = line.strip()
|
||||
if line.startswith('InitiatorName='):
|
||||
ret.append(line.split('=', 1)[1])
|
||||
except IOError as ex:
|
||||
if ex.errno != os.errno.ENOENT:
|
||||
log.debug("Error while accessing '%s': %s", initiator, ex)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _aix_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from an AIX host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
aix_cmd = 'lsattr -E -l iscsi0 | grep initiator_name'
|
||||
|
||||
aix_ret = salt.modules.cmdmod.run(aix_cmd)
|
||||
if aix_ret[0].isalpha():
|
||||
try:
|
||||
ret.append(aix_ret.split()[1].rstrip())
|
||||
except IndexError:
|
||||
pass
|
||||
return ret
|
||||
|
||||
|
||||
def _windows_iqn():
|
||||
'''
|
||||
Return iSCSI IQN from a Windows host.
|
||||
'''
|
||||
ret = []
|
||||
|
||||
wmic = salt.utils.path.which('wmic')
|
||||
|
||||
if not wmic:
|
||||
return ret
|
||||
|
||||
namespace = r'\\root\WMI'
|
||||
path = 'MSiSCSIInitiator_MethodClass'
|
||||
get = 'iSCSINodeName'
|
||||
|
||||
cmd_ret = salt.modules.cmdmod.run_all(
|
||||
'{0} /namespace:{1} path {2} get {3} /format:table'
|
||||
''.format(wmic, namespace, path, get))
|
||||
|
||||
for line in cmd_ret['stdout'].splitlines():
|
||||
if line.startswith('iqn.'):
|
||||
line = line.rstrip()
|
||||
ret.append(line.rstrip())
|
||||
|
||||
return ret
|
@ -652,7 +652,7 @@ def _load_cached_grains(opts, cfn):
|
||||
try:
|
||||
serial = salt.payload.Serial(opts)
|
||||
with salt.utils.files.fopen(cfn, 'rb') as fp_:
|
||||
cached_grains = salt.utils.data.decode(serial.load(fp_))
|
||||
cached_grains = salt.utils.data.decode(serial.load(fp_), preserve_tuples=True)
|
||||
if not cached_grains:
|
||||
log.debug('Cached grains are empty, cache might be corrupted. Refreshing.')
|
||||
return None
|
||||
@ -819,7 +819,7 @@ def grains(opts, force_refresh=False, proxy=None):
|
||||
salt.utils.dictupdate.update(grains_data, opts['grains'])
|
||||
else:
|
||||
grains_data.update(opts['grains'])
|
||||
return salt.utils.data.decode(grains_data)
|
||||
return salt.utils.data.decode(grains_data, preserve_tuples=True)
|
||||
|
||||
|
||||
# TODO: get rid of? Does anyone use this? You should use raw() instead
|
||||
|
@ -90,7 +90,7 @@ def _to_dict(objects):
|
||||
|
||||
def db_list(user=None, password=None, host=None, port=None, authdb=None):
|
||||
'''
|
||||
List all Mongodb databases
|
||||
List all MongoDB databases
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -112,7 +112,7 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None):
|
||||
|
||||
def db_exists(name, user=None, password=None, host=None, port=None, authdb=None):
|
||||
'''
|
||||
Checks if a database exists in Mongodb
|
||||
Checks if a database exists in MongoDB
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -130,7 +130,7 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None)
|
||||
|
||||
def db_remove(name, user=None, password=None, host=None, port=None, authdb=None):
|
||||
'''
|
||||
Remove a Mongodb database
|
||||
Remove a MongoDB database
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -207,7 +207,7 @@ def user_find(name, user=None, password=None, host=None, port=None,
|
||||
|
||||
def user_list(user=None, password=None, host=None, port=None, database='admin', authdb=None):
|
||||
'''
|
||||
List users of a Mongodb database
|
||||
List users of a MongoDB database
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -248,7 +248,7 @@ def user_list(user=None, password=None, host=None, port=None, database='admin',
|
||||
def user_exists(name, user=None, password=None, host=None, port=None,
|
||||
database='admin', authdb=None):
|
||||
'''
|
||||
Checks if a user exists in Mongodb
|
||||
Checks if a user exists in MongoDB
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -271,7 +271,7 @@ def user_exists(name, user=None, password=None, host=None, port=None,
|
||||
def user_create(name, passwd, user=None, password=None, host=None, port=None,
|
||||
database='admin', authdb=None, roles=None):
|
||||
'''
|
||||
Create a Mongodb user
|
||||
Create a MongoDB user
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -299,7 +299,7 @@ def user_create(name, passwd, user=None, password=None, host=None, port=None,
|
||||
def user_remove(name, user=None, password=None, host=None, port=None,
|
||||
database='admin', authdb=None):
|
||||
'''
|
||||
Remove a Mongodb user
|
||||
Remove a MongoDB user
|
||||
|
||||
CLI Example:
|
||||
|
||||
@ -325,7 +325,7 @@ def user_remove(name, user=None, password=None, host=None, port=None,
|
||||
def user_roles_exists(name, roles, database, user=None, password=None, host=None,
|
||||
port=None, authdb=None):
|
||||
'''
|
||||
Checks if a user of a Mongodb database has specified roles
|
||||
Checks if a user of a MongoDB database has specified roles
|
||||
|
||||
CLI Examples:
|
||||
|
||||
@ -363,7 +363,7 @@ def user_roles_exists(name, roles, database, user=None, password=None, host=None
|
||||
def user_grant_roles(name, roles, database, user=None, password=None, host=None,
|
||||
port=None, authdb=None):
|
||||
'''
|
||||
Grant one or many roles to a Mongodb user
|
||||
Grant one or many roles to a MongoDB user
|
||||
|
||||
CLI Examples:
|
||||
|
||||
@ -398,7 +398,7 @@ def user_grant_roles(name, roles, database, user=None, password=None, host=None,
|
||||
def user_revoke_roles(name, roles, database, user=None, password=None, host=None,
|
||||
port=None, authdb=None):
|
||||
'''
|
||||
Revoke one or many roles to a Mongodb user
|
||||
Revoke one or many roles to a MongoDB user
|
||||
|
||||
CLI Examples:
|
||||
|
||||
|
@ -58,7 +58,7 @@ SCHEDULE_CONF = [
|
||||
'after',
|
||||
'return_config',
|
||||
'return_kwargs',
|
||||
'run_on_start'
|
||||
'run_on_start',
|
||||
'skip_during_range',
|
||||
'run_after_skip_range',
|
||||
]
|
||||
|
@ -131,6 +131,10 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1):
|
||||
raise salt.exceptions.SaltInvocationError(
|
||||
'value must be precomputed hash')
|
||||
|
||||
# ensure name and value are bytes
|
||||
name = salt.utils.stringutils.to_bytes(name)
|
||||
value = salt.utils.stringutils.to_bytes(value)
|
||||
|
||||
try:
|
||||
ctx = M2Crypto.m2.x509v3_set_nconf()
|
||||
_fix_ctx(ctx, issuer)
|
||||
@ -316,7 +320,7 @@ def _text_or_file(input_):
|
||||
'''
|
||||
if os.path.isfile(input_):
|
||||
with salt.utils.files.fopen(input_) as fp_:
|
||||
return salt.utils.stringutils.to_unicode(fp_.read())
|
||||
return salt.utils.stringutils.to_bytes(fp_.read())
|
||||
else:
|
||||
return input_
|
||||
|
||||
@ -493,7 +497,7 @@ def get_pem_entry(text, pem_type=None):
|
||||
ret += pem_body[i:i + 64] + '\n'
|
||||
ret += pem_footer + '\n'
|
||||
|
||||
return ret
|
||||
return ret.encode('ascii')
|
||||
|
||||
|
||||
def get_pem_entries(glob_path):
|
||||
|
@ -1,435 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Management of Mongodb users and databases
|
||||
=========================================
|
||||
|
||||
.. note::
|
||||
This module requires PyMongo to be installed.
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'mongodb'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if 'mongodb.user_exists' not in __salt__:
|
||||
return False
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def database_absent(name,
|
||||
user=None,
|
||||
password=None,
|
||||
host=None,
|
||||
port=None,
|
||||
authdb=None):
|
||||
'''
|
||||
Ensure that the named database is absent. Note that creation doesn't make sense in MongoDB.
|
||||
|
||||
name
|
||||
The name of the database to remove
|
||||
|
||||
user
|
||||
The user to connect as (must be able to create the user)
|
||||
|
||||
password
|
||||
The password of the user
|
||||
|
||||
host
|
||||
The host to connect to
|
||||
|
||||
port
|
||||
The port to connect to
|
||||
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': ''}
|
||||
|
||||
#check if database exists and remove it
|
||||
if __salt__['mongodb.db_exists'](name, user, password, host, port, authdb=authdb):
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = ('Database {0} is present and needs to be removed'
|
||||
).format(name)
|
||||
return ret
|
||||
if __salt__['mongodb.db_remove'](name, user, password, host, port, authdb=authdb):
|
||||
ret['comment'] = 'Database {0} has been removed'.format(name)
|
||||
ret['changes'][name] = 'Absent'
|
||||
return ret
|
||||
|
||||
# fallback
|
||||
ret['comment'] = ('User {0} is not present, so it cannot be removed'
|
||||
).format(name)
|
||||
return ret
|
||||
|
||||
|
||||
def user_present(name,
|
||||
passwd,
|
||||
database="admin",
|
||||
user=None,
|
||||
password=None,
|
||||
host="localhost",
|
||||
port=27017,
|
||||
authdb=None):
|
||||
'''
|
||||
Ensure that the user is present with the specified properties
|
||||
|
||||
name
|
||||
The name of the user to manage
|
||||
|
||||
passwd
|
||||
The password of the user to manage
|
||||
|
||||
user
|
||||
MongoDB user with sufficient privilege to create the user
|
||||
|
||||
password
|
||||
Password for the admin user specified with the ``user`` parameter
|
||||
|
||||
host
|
||||
The hostname/IP address of the MongoDB server
|
||||
|
||||
port
|
||||
The port on which MongoDB is listening
|
||||
|
||||
database
|
||||
The database in which to create the user
|
||||
|
||||
.. note::
|
||||
If the database doesn't exist, it will be created.
|
||||
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
mongouser-myapp:
|
||||
mongodb.user_present:
|
||||
- name: myapp
|
||||
- passwd: password-of-myapp
|
||||
# Connect as admin:sekrit
|
||||
- user: admin
|
||||
- password: sekrit
|
||||
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': 'User {0} is already present'.format(name)}
|
||||
|
||||
# Check for valid port
|
||||
try:
|
||||
port = int(port)
|
||||
except TypeError:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Port ({0}) is not an integer.'.format(port)
|
||||
return ret
|
||||
|
||||
# check if user exists
|
||||
user_exists = __salt__['mongodb.user_exists'](name, user, password, host, port, database, authdb)
|
||||
if user_exists is True:
|
||||
return ret
|
||||
|
||||
# if the check does not return a boolean, return an error
|
||||
# this may be the case if there is a database connection error
|
||||
if not isinstance(user_exists, bool):
|
||||
ret['comment'] = user_exists
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = ('User {0} is not present and needs to be created'
|
||||
).format(name)
|
||||
return ret
|
||||
# The user is not present, make it!
|
||||
if __salt__['mongodb.user_create'](name, passwd, user, password, host, port, database=database, authdb=authdb):
|
||||
ret['comment'] = 'User {0} has been created'.format(name)
|
||||
ret['changes'][name] = 'Present'
|
||||
else:
|
||||
ret['comment'] = 'Failed to create database {0}'.format(name)
|
||||
ret['result'] = False
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def user_absent(name,
|
||||
user=None,
|
||||
password=None,
|
||||
host=None,
|
||||
port=None,
|
||||
database="admin",
|
||||
authdb=None):
|
||||
'''
|
||||
Ensure that the named user is absent
|
||||
|
||||
name
|
||||
The name of the user to remove
|
||||
|
||||
user
|
||||
MongoDB user with sufficient privilege to create the user
|
||||
|
||||
password
|
||||
Password for the admin user specified by the ``user`` parameter
|
||||
|
||||
host
|
||||
The hostname/IP address of the MongoDB server
|
||||
|
||||
port
|
||||
The port on which MongoDB is listening
|
||||
|
||||
database
|
||||
The database from which to remove the user specified by the ``name``
|
||||
parameter
|
||||
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': ''}
|
||||
|
||||
#check if user exists and remove it
|
||||
user_exists = __salt__['mongodb.user_exists'](name, user, password, host, port, database=database, authdb=authdb)
|
||||
if user_exists is True:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = ('User {0} is present and needs to be removed'
|
||||
).format(name)
|
||||
return ret
|
||||
if __salt__['mongodb.user_remove'](name, user, password, host, port, database=database, authdb=authdb):
|
||||
ret['comment'] = 'User {0} has been removed'.format(name)
|
||||
ret['changes'][name] = 'Absent'
|
||||
return ret
|
||||
|
||||
# if the check does not return a boolean, return an error
|
||||
# this may be the case if there is a database connection error
|
||||
if not isinstance(user_exists, bool):
|
||||
ret['comment'] = user_exists
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
# fallback
|
||||
ret['comment'] = ('User {0} is not present, so it cannot be removed'
|
||||
).format(name)
|
||||
return ret
|
||||
|
||||
|
||||
def _roles_to_set(roles, database):
|
||||
ret = set()
|
||||
for r in roles:
|
||||
if isinstance(r, dict):
|
||||
if r['db'] == database:
|
||||
ret.add(r['role'])
|
||||
else:
|
||||
ret.add(r)
|
||||
return ret
|
||||
|
||||
|
||||
def _user_roles_to_set(user_list, name, database):
|
||||
ret = set()
|
||||
|
||||
for item in user_list:
|
||||
if item['user'] == name:
|
||||
ret = ret.union(_roles_to_set(item['roles'], database))
|
||||
return ret
|
||||
|
||||
|
||||
def user_grant_roles(name, roles,
|
||||
database="admin",
|
||||
user=None,
|
||||
password=None,
|
||||
host="localhost",
|
||||
port=27017,
|
||||
authdb=None):
|
||||
|
||||
'''
|
||||
Ensure that the named user is granted certain roles
|
||||
|
||||
name
|
||||
The name of the user to remove
|
||||
|
||||
roles
|
||||
The roles to grant to the user
|
||||
|
||||
user
|
||||
MongoDB user with sufficient privilege to create the user
|
||||
|
||||
password
|
||||
Password for the admin user specified by the ``user`` parameter
|
||||
|
||||
host
|
||||
The hostname/IP address of the MongoDB server
|
||||
|
||||
port
|
||||
The port on which MongoDB is listening
|
||||
|
||||
database
|
||||
The database from which to remove the user specified by the ``name``
|
||||
parameter
|
||||
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': ''}
|
||||
|
||||
if not isinstance(roles, (list, tuple)):
|
||||
roles = [roles]
|
||||
|
||||
if not roles:
|
||||
ret['result'] = True
|
||||
ret['comment'] = "nothing to do (no roles given)"
|
||||
return ret
|
||||
|
||||
# Check for valid port
|
||||
try:
|
||||
port = int(port)
|
||||
except TypeError:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Port ({0}) is not an integer.'.format(port)
|
||||
return ret
|
||||
|
||||
# check if grant exists
|
||||
user_roles_exists = __salt__['mongodb.user_roles_exists'](name, roles, database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb)
|
||||
if user_roles_exists is True:
|
||||
ret['result'] = True
|
||||
ret['comment'] = "Roles already assigned"
|
||||
return ret
|
||||
|
||||
user_list = __salt__['mongodb.user_list'](database=database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb)
|
||||
|
||||
user_set = _user_roles_to_set(user_list, name, database)
|
||||
roles_set = _roles_to_set(roles, database)
|
||||
diff = roles_set - user_set
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = "Would have modified roles (missing: {0})".format(diff)
|
||||
return ret
|
||||
|
||||
# The user is not present, make it!
|
||||
if __salt__['mongodb.user_grant_roles'](name, roles, database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb):
|
||||
ret['comment'] = 'Granted roles to {0} on {1}'.format(name, database)
|
||||
ret['changes'][name] = ['{0} granted'.format(i) for i in diff]
|
||||
ret['result'] = True
|
||||
else:
|
||||
ret['comment'] = 'Failed to grant roles ({2}) to {0} on {1}'.format(name, database, diff)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def user_set_roles(name, roles,
|
||||
database="admin",
|
||||
user=None,
|
||||
password=None,
|
||||
host="localhost",
|
||||
port=27017,
|
||||
authdb=None):
|
||||
|
||||
'''
|
||||
Ensure that the named user has the given roles and no other roles
|
||||
|
||||
name
|
||||
The name of the user to remove
|
||||
|
||||
roles
|
||||
The roles the given user should have
|
||||
|
||||
user
|
||||
MongoDB user with sufficient privilege to create the user
|
||||
|
||||
password
|
||||
Password for the admin user specified by the ``user`` parameter
|
||||
|
||||
host
|
||||
The hostname/IP address of the MongoDB server
|
||||
|
||||
port
|
||||
The port on which MongoDB is listening
|
||||
|
||||
database
|
||||
The database from which to remove the user specified by the ``name``
|
||||
parameter
|
||||
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': ''}
|
||||
|
||||
if not isinstance(roles, (list, tuple)):
|
||||
roles = [roles]
|
||||
|
||||
if not roles:
|
||||
ret['result'] = True
|
||||
ret['comment'] = "nothing to do (no roles given)"
|
||||
return ret
|
||||
|
||||
# Check for valid port
|
||||
try:
|
||||
port = int(port)
|
||||
except TypeError:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Port ({0}) is not an integer.'.format(port)
|
||||
return ret
|
||||
|
||||
user_list = __salt__['mongodb.user_list'](database=database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb)
|
||||
|
||||
user_set = _user_roles_to_set(user_list, name, database)
|
||||
roles_set = _roles_to_set(roles, database)
|
||||
to_grant = list(roles_set - user_set)
|
||||
to_revoke = list(user_set - roles_set)
|
||||
|
||||
if not to_grant and not to_revoke:
|
||||
ret['result'] = True
|
||||
ret['comment'] = "User {0} has the appropriate roles on {1}".format(name, database)
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
lsg = ', '.join(to_grant)
|
||||
lsr = ', '.join(to_revoke)
|
||||
ret['result'] = None
|
||||
ret['comment'] = "Would have modified roles (grant: {0}; revoke: {1})".format(lsg, lsr)
|
||||
return ret
|
||||
|
||||
ret['changes'][name] = changes = {}
|
||||
|
||||
if to_grant:
|
||||
if not __salt__['mongodb.user_grant_roles'](name, to_grant, database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb):
|
||||
ret['comment'] = "failed to grant some or all of {0} to {1} on {2}".format(to_grant, name, database)
|
||||
return ret
|
||||
else:
|
||||
changes['granted'] = list(to_grant)
|
||||
|
||||
if to_revoke:
|
||||
if not __salt__['mongodb.user_revoke_roles'](name, to_revoke, database,
|
||||
user=user, password=password, host=host, port=port, authdb=authdb):
|
||||
ret['comment'] = "failed to revoke some or all of {0} to {1} on {2}".format(to_revoke, name, database)
|
||||
return ret
|
||||
else:
|
||||
changes['revoked'] = list(to_revoke)
|
||||
|
||||
ret['result'] = True
|
||||
return ret
|
@ -1,14 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Management of Mongodb databases
|
||||
Management of MongoDB Databases
|
||||
===============================
|
||||
|
||||
Only deletion is supported, creation doesn't make sense
|
||||
and can be done using mongodb_user.present
|
||||
:depends: - pymongo Python module
|
||||
|
||||
Only deletion is supported, creation doesn't make sense and can be done using
|
||||
:py:func:`mongodb_user.present <salt.states.mongodb_user.present>`.
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import salt.utils.versions
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'mongodb_database'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if 'mongodb.db_exists' in __salt__:
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def absent(name,
|
||||
@ -18,10 +27,8 @@ def absent(name,
|
||||
port=None,
|
||||
authdb=None):
|
||||
'''
|
||||
.. deprecated:: Fluorine
|
||||
Use ``mongodb.database_absent`` instead
|
||||
|
||||
Ensure that the named database is absent
|
||||
Ensure that the named database is absent. Note that creation doesn't make
|
||||
sense in MongoDB.
|
||||
|
||||
name
|
||||
The name of the database to remove
|
||||
@ -41,19 +48,11 @@ def absent(name,
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': ''}
|
||||
|
||||
salt.utils.versions.warn_until(
|
||||
'Fluorine',
|
||||
'The \'mongodb_database.absent\' function has been deprecated and will be removed in Salt '
|
||||
'{version}. Please use \'mongodb.database_absent\' instead.'
|
||||
)
|
||||
|
||||
#check if database exists and remove it
|
||||
if __salt__['mongodb.db_exists'](name, user, password, host, port, authdb=authdb):
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
@ -65,7 +64,5 @@ def absent(name,
|
||||
ret['changes'][name] = 'Absent'
|
||||
return ret
|
||||
|
||||
# fallback
|
||||
ret['comment'] = ('User {0} is not present, so it cannot be removed'
|
||||
).format(name)
|
||||
ret['comment'] = 'Database {0} is not present'.format(name)
|
||||
return ret
|
||||
|
@ -1,16 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Management of Mongodb users
|
||||
Management of MongoDB Users
|
||||
===========================
|
||||
|
||||
.. note::
|
||||
This module requires PyMongo to be installed.
|
||||
:depends: - pymongo Python module
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import salt.utils.versions
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'mongodb_user'
|
||||
|
||||
@ -31,9 +27,6 @@ def present(name,
|
||||
authdb=None,
|
||||
roles=None):
|
||||
'''
|
||||
.. deprecated:: Fluorine
|
||||
Use ``mongodb.user_present`` instead
|
||||
|
||||
Ensure that the user is present with the specified properties
|
||||
|
||||
name
|
||||
@ -84,13 +77,6 @@ def present(name,
|
||||
- dbOwner
|
||||
|
||||
'''
|
||||
|
||||
salt.utils.versions.warn_until(
|
||||
'Fluorine',
|
||||
'The \'mongodb_user.present\' function has been deprecated and will be removed in Salt '
|
||||
'{version}. Please use \'mongodb.user_present\' instead.'
|
||||
)
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
@ -167,9 +153,6 @@ def absent(name,
|
||||
database="admin",
|
||||
authdb=None):
|
||||
'''
|
||||
.. deprecated:: Fluorine
|
||||
Use ``mongodb.user_absent`` instead
|
||||
|
||||
Ensure that the named user is absent
|
||||
|
||||
name
|
||||
@ -194,13 +177,6 @@ def absent(name,
|
||||
authdb
|
||||
The database in which to authenticate
|
||||
'''
|
||||
|
||||
salt.utils.versions.warn_until(
|
||||
'Fluorine',
|
||||
'The \'mongodb_user.absent\' function has been deprecated and will be removed in Salt '
|
||||
'{version}. Please use \'mongodb.user_absent\' instead.'
|
||||
)
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': True,
|
||||
@ -227,6 +203,5 @@ def absent(name,
|
||||
return ret
|
||||
|
||||
# fallback
|
||||
ret['comment'] = ('User {0} is not present, so it cannot be removed'
|
||||
).format(name)
|
||||
ret['comment'] = 'User {0} is not present'.format(name)
|
||||
return ret
|
||||
|
@ -310,7 +310,7 @@ def private_key_managed(name,
|
||||
|
||||
ret = __states__['file.managed'](**file_args)
|
||||
if ret['changes'] and new_key:
|
||||
ret['changes'] = 'New private key generated'
|
||||
ret['changes'] = {'new': 'New private key generated'}
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -19,14 +19,28 @@ from salt.ext import six
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __split(raw):
|
||||
'''
|
||||
Performs a splitlines on the string. This function exists to make mocking
|
||||
possible in unit tests, since the member functions of the str/unicode
|
||||
builtins cannot be mocked.
|
||||
'''
|
||||
return raw.splitlines()
|
||||
|
||||
|
||||
def find_json(raw):
|
||||
'''
|
||||
Pass in a raw string and load the json when it starts. This allows for a
|
||||
string to start with garbage and end with json but be cleanly loaded
|
||||
'''
|
||||
ret = {}
|
||||
for ind, _ in enumerate(raw):
|
||||
working = '\n'.join(raw.splitlines()[ind:])
|
||||
lines = __split(raw)
|
||||
for ind, _ in enumerate(lines):
|
||||
try:
|
||||
working = '\n'.join(lines[ind:])
|
||||
except UnicodeDecodeError:
|
||||
working = '\n'.join(salt.utils.data.decode(lines[ind:]))
|
||||
|
||||
try:
|
||||
ret = json.loads(working) # future lint: blacklisted-function
|
||||
except ValueError:
|
||||
|
@ -1241,7 +1241,7 @@ class Schedule(object):
|
||||
|
||||
# If there is no job specific skip_during_range available,
|
||||
# grab the global which defaults to None.
|
||||
if 'skip_during_range' not in data:
|
||||
if 'skip_during_range' not in data and self.skip_during_range:
|
||||
data['skip_during_range'] = self.skip_during_range
|
||||
|
||||
if 'skip_during_range' in data and data['skip_during_range']:
|
||||
|
@ -368,3 +368,32 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
self.schedule.eval(now=run_time)
|
||||
ret = self.schedule.job_status('job1')
|
||||
self.assertEqual(ret['_last_run'], run_time)
|
||||
|
||||
def test_eval_run_on_start(self):
|
||||
'''
|
||||
verify that scheduled job is run when minion starts
|
||||
'''
|
||||
job = {
|
||||
'schedule': {
|
||||
'job1': {
|
||||
'function': 'test.ping',
|
||||
'hours': '1',
|
||||
'run_on_start': True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add job to schedule
|
||||
self.schedule.opts.update(job)
|
||||
|
||||
# eval at 2:00pm, will run.
|
||||
run_time = dateutil_parser.parse('11/29/2017 2:00pm')
|
||||
self.schedule.eval(now=run_time)
|
||||
ret = self.schedule.job_status('job1')
|
||||
self.assertEqual(ret['_last_run'], run_time)
|
||||
|
||||
# eval at 3:00pm, will run.
|
||||
run_time = dateutil_parser.parse('11/29/2017 3:00pm')
|
||||
self.schedule.eval(now=run_time)
|
||||
ret = self.schedule.job_status('job1')
|
||||
self.assertEqual(ret['_last_run'], run_time)
|
||||
|
@ -558,58 +558,6 @@ PATCHLEVEL = 3
|
||||
}
|
||||
self._run_os_grains_tests("ubuntu-17.10", _os_release_map, expectation)
|
||||
|
||||
def test_windows_iscsi_iqn_grains(self):
|
||||
cmd_run_mock = MagicMock(
|
||||
return_value={'stdout': 'iSCSINodeName\niqn.1991-05.com.microsoft:simon-x1\n'}
|
||||
)
|
||||
|
||||
with patch.object(salt.utils.platform, 'is_linux',
|
||||
MagicMock(return_value=False)):
|
||||
with patch.object(salt.utils.platform, 'is_windows',
|
||||
MagicMock(return_value=True)):
|
||||
with patch.dict(core.__salt__, {'run_all': cmd_run_mock}):
|
||||
with patch.object(salt.utils.path, 'which',
|
||||
MagicMock(return_value=True)):
|
||||
with patch.dict(core.__salt__, {'cmd.run_all': cmd_run_mock}):
|
||||
_grains = core.iscsi_iqn()
|
||||
|
||||
self.assertEqual(_grains.get('iscsi_iqn'),
|
||||
['iqn.1991-05.com.microsoft:simon-x1'])
|
||||
|
||||
@skipIf(salt.utils.platform.is_windows(), 'System is Windows')
|
||||
def test_aix_iscsi_iqn_grains(self):
|
||||
cmd_run_mock = MagicMock(
|
||||
return_value='initiator_name iqn.localhost.hostid.7f000001'
|
||||
)
|
||||
|
||||
with patch.object(salt.utils.platform, 'is_linux',
|
||||
MagicMock(return_value=False)):
|
||||
with patch.object(salt.utils.platform, 'is_aix',
|
||||
MagicMock(return_value=True)):
|
||||
with patch.dict(core.__salt__, {'cmd.run': cmd_run_mock}):
|
||||
_grains = core.iscsi_iqn()
|
||||
|
||||
self.assertEqual(_grains.get('iscsi_iqn'),
|
||||
['iqn.localhost.hostid.7f000001'])
|
||||
|
||||
@patch('salt.grains.core.os.path.isfile', MagicMock(return_value=True))
|
||||
@patch('salt.grains.core.os.access', MagicMock(return_value=True))
|
||||
def test_linux_iscsi_iqn_grains(self):
|
||||
_iscsi_file = '## DO NOT EDIT OR REMOVE THIS FILE!\n' \
|
||||
'## If you remove this file, the iSCSI daemon will not start.\n' \
|
||||
'## If you change the InitiatorName, existing access control lists\n' \
|
||||
'## may reject this initiator. The InitiatorName must be unique\n' \
|
||||
'## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames.\n' \
|
||||
'InitiatorName=iqn.1993-08.org.debian:01:d12f7aba36\n'
|
||||
|
||||
with patch('salt.utils.files.fopen', mock_open()) as iscsi_initiator_file:
|
||||
iscsi_initiator_file.return_value.__iter__.return_value = _iscsi_file.splitlines()
|
||||
iqn = core._linux_iqn()
|
||||
|
||||
assert isinstance(iqn, list)
|
||||
assert len(iqn) == 1
|
||||
assert iqn == ['iqn.1993-08.org.debian:01:d12f7aba36']
|
||||
|
||||
@skipIf(not salt.utils.platform.is_linux(), 'System is not Linux')
|
||||
def test_linux_memdata(self):
|
||||
'''
|
||||
@ -906,30 +854,3 @@ SwapTotal: 4789244 kB'''
|
||||
[]}}
|
||||
with patch.object(salt.utils.dns, 'parse_resolv', MagicMock(return_value=resolv_mock)):
|
||||
assert core.dns() == ret
|
||||
|
||||
@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.EPERM,
|
||||
'The cables are not the same length.')))
|
||||
@patch('salt.grains.core.log', MagicMock())
|
||||
def test_linux_iqn_non_root(self):
|
||||
'''
|
||||
Test if linux_iqn is running on salt-master as non-root
|
||||
and handling access denial properly.
|
||||
:return:
|
||||
'''
|
||||
assert core._linux_iqn() == []
|
||||
core.log.debug.assert_called()
|
||||
assert 'Error while accessing' in core.log.debug.call_args[0][0]
|
||||
assert 'cables are not the same' in core.log.debug.call_args[0][2].strerror
|
||||
assert core.log.debug.call_args[0][2].errno == os.errno.EPERM
|
||||
assert core.log.debug.call_args[0][1] == '/etc/iscsi/initiatorname.iscsi'
|
||||
|
||||
@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.ENOENT, '')))
|
||||
@patch('salt.grains.core.log', MagicMock())
|
||||
def test_linux_iqn_no_iscsii_initiator(self):
|
||||
'''
|
||||
Test if linux_iqn is running on salt-master as root.
|
||||
iscsii initiator is not there accessible or is not supported.
|
||||
:return:
|
||||
'''
|
||||
assert core._linux_iqn() == []
|
||||
core.log.debug.assert_not_called()
|
||||
|
51
tests/unit/grains/test_fibre_channel.py
Normal file
51
tests/unit/grains/test_fibre_channel.py
Normal file
@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Shane Lee <slee@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
patch,
|
||||
mock_open,
|
||||
MagicMock,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.grains.fibre_channel as fibre_channel
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class FibreChannelGrainsTestCase(TestCase):
|
||||
'''
|
||||
Test cases for iscsi grains
|
||||
'''
|
||||
def test_windows_fibre_channel_wwns_grains(self):
|
||||
wwns = ['20:00:00:25:b5:11:11:4c',
|
||||
'20:00:00:25:b5:11:11:5c',
|
||||
'20:00:00:25:b5:44:44:4c',
|
||||
'20:00:00:25:b5:44:44:5c']
|
||||
cmd_run_mock = MagicMock(return_value=wwns)
|
||||
with patch('salt.modules.cmdmod.powershell', cmd_run_mock):
|
||||
ret = fibre_channel._windows_wwns()
|
||||
self.assertEqual(ret, wwns)
|
||||
|
||||
def test_linux_fibre_channel_wwns_grains(self):
|
||||
|
||||
def multi_mock_open(*file_contents):
|
||||
mock_files = [mock_open(read_data=content).return_value for content in file_contents]
|
||||
mock_opener = mock_open()
|
||||
mock_opener.side_effect = mock_files
|
||||
|
||||
return mock_opener
|
||||
|
||||
files = ['file1', 'file2']
|
||||
with patch('glob.glob', MagicMock(return_value=files)):
|
||||
with patch('salt.utils.files.fopen', multi_mock_open('0x500143802426baf4', '0x500143802426baf5')):
|
||||
ret = fibre_channel._linux_wwns()
|
||||
|
||||
self.assertEqual(ret, ['500143802426baf4', '500143802426baf5'])
|
94
tests/unit/grains/test_iscsi.py
Normal file
94
tests/unit/grains/test_iscsi.py
Normal file
@ -0,0 +1,94 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Shane Lee <slee@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
patch,
|
||||
mock_open,
|
||||
MagicMock,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.grains.iscsi as iscsi
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class IscsiGrainsTestCase(TestCase):
|
||||
'''
|
||||
Test cases for iscsi grains
|
||||
'''
|
||||
def test_windows_iscsi_iqn_grains(self):
|
||||
cmd_run_mock = MagicMock(
|
||||
return_value={'stdout': 'iSCSINodeName\n'
|
||||
'iqn.1991-05.com.microsoft:simon-x1\n'}
|
||||
)
|
||||
_grains = {}
|
||||
with patch('salt.utils.path.which', MagicMock(return_value=True)):
|
||||
with patch('salt.modules.cmdmod.run_all', cmd_run_mock):
|
||||
_grains['iscsi_iqn'] = iscsi._windows_iqn()
|
||||
|
||||
self.assertEqual(_grains.get('iscsi_iqn'),
|
||||
['iqn.1991-05.com.microsoft:simon-x1'])
|
||||
|
||||
def test_aix_iscsi_iqn_grains(self):
|
||||
cmd_run_mock = MagicMock(
|
||||
return_value='initiator_name iqn.localhost.hostid.7f000001'
|
||||
)
|
||||
|
||||
_grains = {}
|
||||
with patch('salt.modules.cmdmod.run', cmd_run_mock):
|
||||
_grains['iscsi_iqn'] = iscsi._aix_iqn()
|
||||
|
||||
self.assertEqual(_grains.get('iscsi_iqn'),
|
||||
['iqn.localhost.hostid.7f000001'])
|
||||
|
||||
def test_linux_iscsi_iqn_grains(self):
|
||||
_iscsi_file = '## DO NOT EDIT OR REMOVE THIS FILE!\n' \
|
||||
'## If you remove this file, the iSCSI daemon will not start.\n' \
|
||||
'## If you change the InitiatorName, existing access control lists\n' \
|
||||
'## may reject this initiator. The InitiatorName must be unique\n' \
|
||||
'## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames.\n' \
|
||||
'InitiatorName=iqn.1993-08.org.debian:01:d12f7aba36\n'
|
||||
|
||||
with patch('salt.utils.files.fopen', mock_open()) as iscsi_initiator_file:
|
||||
iscsi_initiator_file.return_value.__iter__.return_value = _iscsi_file.splitlines()
|
||||
iqn = iscsi._linux_iqn()
|
||||
|
||||
assert isinstance(iqn, list)
|
||||
assert len(iqn) == 1
|
||||
assert iqn == ['iqn.1993-08.org.debian:01:d12f7aba36']
|
||||
|
||||
@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.EPERM,
|
||||
'The cables are not the same length.')))
|
||||
@patch('salt.grains.iscsi.log', MagicMock())
|
||||
def test_linux_iqn_non_root(self):
|
||||
'''
|
||||
Test if linux_iqn is running on salt-master as non-root
|
||||
and handling access denial properly.
|
||||
:return:
|
||||
'''
|
||||
assert iscsi._linux_iqn() == []
|
||||
iscsi.log.debug.assert_called()
|
||||
assert 'Error while accessing' in iscsi.log.debug.call_args[0][0]
|
||||
assert 'cables are not the same' in iscsi.log.debug.call_args[0][2].strerror
|
||||
assert iscsi.log.debug.call_args[0][2].errno == os.errno.EPERM
|
||||
assert iscsi.log.debug.call_args[0][1] == '/etc/iscsi/initiatorname.iscsi'
|
||||
|
||||
@patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.ENOENT, '')))
|
||||
@patch('salt.grains.iscsi.log', MagicMock())
|
||||
def test_linux_iqn_no_iscsii_initiator(self):
|
||||
'''
|
||||
Test if linux_iqn is running on salt-master as root.
|
||||
iscsii initiator is not there accessible or is not supported.
|
||||
:return:
|
||||
'''
|
||||
assert iscsi._linux_iqn() == []
|
||||
iscsi.log.debug.assert_not_called()
|
@ -34,6 +34,12 @@ from tests.support.mock import (
|
||||
|
||||
from salt.modules import x509
|
||||
|
||||
try:
|
||||
import M2Crypto # pylint: disable=unused-import
|
||||
HAS_M2CRYPTO = True
|
||||
except ImportError:
|
||||
HAS_M2CRYPTO = False
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(not bool(pytest), False)
|
||||
@ -65,3 +71,96 @@ class X509TestCase(TestCase, LoaderModuleMockMixin):
|
||||
assert x509.log.trace.call_args[0][0] == "Missing attribute '%s'. Error: %s"
|
||||
assert x509.log.trace.call_args[0][1] == list(subj.nid.keys())[0]
|
||||
assert isinstance(x509.log.trace.call_args[0][2], TypeError)
|
||||
|
||||
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble')
|
||||
def test_get_pem_entry(self):
|
||||
'''
|
||||
Test private function _parse_subject(subject) it handles a missing fields
|
||||
:return:
|
||||
'''
|
||||
ca_key = '''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls
|
||||
pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1
|
||||
2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB
|
||||
AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr
|
||||
yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH
|
||||
hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R
|
||||
3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7
|
||||
u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy
|
||||
kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj
|
||||
35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk
|
||||
TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK
|
||||
tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj
|
||||
c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
'''
|
||||
|
||||
ret = x509.get_pem_entry(ca_key)
|
||||
self.assertEqual(ret, ca_key)
|
||||
|
||||
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble')
|
||||
def test_get_private_key_size(self):
|
||||
'''
|
||||
Test private function _parse_subject(subject) it handles a missing fields
|
||||
:return:
|
||||
'''
|
||||
ca_key = '''
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls
|
||||
pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1
|
||||
2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB
|
||||
AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr
|
||||
yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH
|
||||
hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R
|
||||
3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7
|
||||
u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy
|
||||
kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj
|
||||
35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk
|
||||
TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK
|
||||
tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj
|
||||
c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
'''
|
||||
|
||||
ret = x509.get_private_key_size(ca_key)
|
||||
self.assertEqual(ret, 1024)
|
||||
|
||||
@skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble')
|
||||
def test_create_certificate(self):
|
||||
'''
|
||||
Test private function _parse_subject(subject) it handles a missing fields
|
||||
:return:
|
||||
'''
|
||||
ca_key = '''
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls
|
||||
pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1
|
||||
2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB
|
||||
AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr
|
||||
yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH
|
||||
hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R
|
||||
3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7
|
||||
u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy
|
||||
kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj
|
||||
35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk
|
||||
TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK
|
||||
tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj
|
||||
c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
'''
|
||||
|
||||
ret = x509.create_certificate(text=True,
|
||||
signing_private_key=ca_key,
|
||||
CN='Redacted Root CA',
|
||||
O='Redacted',
|
||||
C='BE',
|
||||
ST='Antwerp',
|
||||
L='Local Town',
|
||||
Email='certadm@example.org',
|
||||
basicConstraints="critical CA:true",
|
||||
keyUsage="critical cRLSign, keyCertSign",
|
||||
subjectKeyIdentifier='hash',
|
||||
authorityKeyIdentifier='keyid,issuer:always',
|
||||
days_valid=3650,
|
||||
days_remaining=0)
|
||||
self.assertIn('BEGIN CERTIFICATE', ret)
|
||||
|
@ -56,7 +56,6 @@ class MongodbDatabaseTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'changes': {'mydb': 'Absent'}})
|
||||
self.assertDictEqual(mongodb_database.absent(name), ret)
|
||||
|
||||
comt = ('User {0} is not present, so it cannot be removed'
|
||||
.format(name))
|
||||
comt = 'Database {0} is not present'.format(name)
|
||||
ret.update({'comment': comt, 'changes': {}})
|
||||
self.assertDictEqual(mongodb_database.absent(name), ret)
|
||||
|
@ -98,7 +98,6 @@ class MongodbUserTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'changes': {name: 'Absent'}})
|
||||
self.assertDictEqual(mongodb_user.absent(name), ret)
|
||||
|
||||
comt = ('User {0} is not present, so it cannot be removed'
|
||||
.format(name))
|
||||
comt = 'User {0} is not present'.format(name)
|
||||
ret.update({'comment': comt, 'result': True, 'changes': {}})
|
||||
self.assertDictEqual(mongodb_user.absent(name), ret)
|
||||
|
@ -4,37 +4,19 @@ Tests for salt.utils.json
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import errno
|
||||
import functools
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.paths import TMP
|
||||
from tests.support.unit import TestCase, LOREM_IPSUM
|
||||
from tests.support.helpers import with_tempfile
|
||||
from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON
|
||||
from tests.support.unit import TestCase, LOREM_IPSUM, skipIf
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.files
|
||||
import salt.utils.json
|
||||
import salt.utils.platform
|
||||
import salt.utils.stringutils
|
||||
|
||||
|
||||
def with_tempfile(func):
|
||||
'''
|
||||
Generate a temp directory for a test
|
||||
'''
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
temp_file = salt.utils.files.mkstemp(dir=TMP)
|
||||
try:
|
||||
return func(self, temp_file, *args, **kwargs)
|
||||
finally:
|
||||
try:
|
||||
os.remove(temp_file)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise
|
||||
return wrapper
|
||||
from salt.ext import six
|
||||
|
||||
|
||||
class JSONTestCase(TestCase):
|
||||
@ -113,6 +95,21 @@ class JSONTestCase(TestCase):
|
||||
# Test to see if a ValueError is raised if no JSON is passed in
|
||||
self.assertRaises(ValueError, salt.utils.json.find_json, LOREM_IPSUM)
|
||||
|
||||
@skipIf(salt.utils.platform.is_windows(), 'skip until we figure out what to do about decoding unicode on windows')
|
||||
@skipIf(not six.PY2, 'Test only needed on Python 2')
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
def test_find_json_unicode_splitlines(self):
|
||||
'''
|
||||
Tests a case in salt-ssh where a unicode string is split into a list of
|
||||
str types by .splitlines().
|
||||
'''
|
||||
raw = '{"foo": "öäü"}'
|
||||
mock_split = MagicMock(return_value=[raw.encode('utf8')])
|
||||
|
||||
with patch.object(salt.utils.json, '__split', mock_split):
|
||||
ret = salt.utils.json.find_json(raw)
|
||||
self.assertEqual(ret, {'foo': 'öäü'})
|
||||
|
||||
def test_dumps_loads(self):
|
||||
'''
|
||||
Test dumping to and loading from a string
|
||||
|
Loading…
Reference in New Issue
Block a user