Merge remote-tracking branch 'upstream/2015.5' into merge-forward-2015.8

Conflicts:
    salt/minion.py
    salt/modules/gpg.py
    salt/modules/keystone.py
    salt/modules/pw_user.py
    salt/states/reg.py
    setup.py
This commit is contained in:
Colton Myers 2015-08-17 16:54:16 -06:00
commit d3d0e004d9
37 changed files with 2967 additions and 476 deletions

View file

@ -215,6 +215,48 @@ If you would like to log to the console instead of to the log file, remove the
</topics/installation/osx>` instructions.
Changing Default Paths
~~~~~~~~~~~~~~~~~~~~~~
Instead of updating your configuration files to point to the new root directory
and having to pass the new configuration directory path to all of Salt's CLI
tools, you can explicitly tweak the default system paths that Salt expects:
.. code-block:: bash
GENERATE_SALT_SYSPATHS=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
install -e ./salt # the path to the salt git clone from above
You can now call all of Salt's CLI tools without explicitly passing the configuration directory.
Additional Options
..................
In case you want to distribute your virtualenv, you probably don't want to
include Salt's clone ``.git/`` directory, and, without it, Salt won't report
the accurate version. You can tell ``setup.py`` to generate the hardcoded
version information which is distributable:
.. code-block:: bash
GENERATE_SALT_SYSPATHS=1 WRITE_SALT_VERSION=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
install -e ./salt # the path to the salt git clone from above
Instead of passing those two environmental variables, you can just pass a
single one which will trigger the other two:
.. code-block:: bash
MIMIC_SALT_INSTALL=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
install -e ./salt # the path to the salt git clone from above
This last one will grant you an edditable salt installation with hardcoded
system paths and version information.
Installing Salt from the Python Package Index
---------------------------------------------

View file

@ -139,8 +139,8 @@ Install the following software:
Download the Prerequisite zip file for your CPU architecture from the
SaltStack download site:
* `Salt32.zip <http://docs.saltstack.com/downloads/windows-deps/Salt32.zip/>`_
* `Salt64.zip <http://docs.saltstack.com/downloads/windows-deps/Salt64.zip/>`_
* `Salt32.zip <http://repo.saltstack.com/windows/dependencies/Salt32.zip/>`_
* `Salt64.zip <http://repo.saltstack.com/windows/dependencies/Salt64.zip/>`_
These files contain all sofware required to build and develop salt. Unzip the
contents of the file to ``C:\Salt-Dev\temp``.

File diff suppressed because it is too large Load diff

View file

@ -73,7 +73,10 @@ start() {
RETVAL=1
else
daemon --pidfile=$PID_FILE --check $SERVICE $SALTAPI $CONFIG_ARGS
RETVAL=0
RETVAL=$?
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
echo
return $RETVAL
fi
fi
RETVAL=$?
@ -97,6 +100,10 @@ stop() {
fi
else
killproc $PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
return $RETVAL
fi
RETVAL=$?
echo

View file

@ -64,6 +64,10 @@ start() {
fi
else
daemon --check $SERVICE $SALTMASTER -d $MASTER_ARGS
RETVAL=$?
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
echo
return $RETVAL
fi
RETVAL=$?
echo
@ -86,6 +90,10 @@ stop() {
fi
else
killproc $PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
return $RETVAL
fi
RETVAL=$?
echo

View file

@ -68,7 +68,11 @@ start() {
RETVAL=$?
echo -n "already running"
else
daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
RETVAL=$?
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
echo
return $RETVAL
fi
fi
RETVAL=$?
@ -94,6 +98,7 @@ stop() {
else
killproc $PROCESS
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
# tidy up any rogue processes:
PROCS=`ps -ef | grep "$SALTMINION" | grep -v grep | awk '{print $2}'`
if [ -n "$PROCS" ]; then

View file

@ -65,6 +65,10 @@ start() {
fi
else
daemon --check $SERVICE $SALTSYNDIC -d $SYNDIC_ARGS
RETVAL=$?
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
echo
return $RETVAL
fi
RETVAL=$?
echo
@ -87,6 +91,10 @@ stop() {
fi
else
killproc $PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
return $RETVAL
fi
RETVAL=$?
echo

View file

@ -954,10 +954,10 @@ ARGS = {9}\n'''.format(self.minion_config,
pass
# Execute shim
ret = self.shell.exec_cmd('/bin/sh $HOME/{0}'.format(target_shim_file))
ret = self.shell.exec_cmd('/bin/sh \'$HOME/{0}\''.format(target_shim_file))
# Remove shim from target system
self.shell.exec_cmd('rm $HOME/{0}'.format(target_shim_file))
self.shell.exec_cmd('rm \'$HOME/{0}\''.format(target_shim_file))
return ret

View file

@ -1654,7 +1654,6 @@ def request_instance(vm_=None, call=None):
}
try:
rd_data = aws.query(rd_params,
return_root=True,
location=get_location(),
provider=get_provider(),
opts=__opts__,
@ -2363,7 +2362,7 @@ def create(vm_=None, call=None):
'volumes': volumes,
'zone': ret['placement']['availabilityZone'],
'instance_id': ret['instanceId'],
'del_all_vols_on_destroy': vm_.get('set_del_all_vols_on_destroy', False)
'del_all_vols_on_destroy': vm_.get('del_all_vols_on_destroy', False)
},
call='action'
)
@ -3780,38 +3779,52 @@ def delete_keypair(kwargs=None, call=None):
def create_snapshot(kwargs=None, call=None, wait_to_finish=False):
'''
Create a snapshot
Create a snapshot.
volume_id
The ID of the Volume from which to create a snapshot.
description
The optional description of the snapshot.
CLI Exampe:
.. code-block:: bash
salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826
salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826 \\
description="My Snapshot Description"
'''
if call != 'function':
log.error(
raise SaltCloudSystemExit(
'The create_snapshot function must be called with -f '
'or --function.'
)
return False
if 'volume_id' not in kwargs:
log.error('A volume_id must be specified to create a snapshot.')
return False
if kwargs is None:
kwargs = {}
if 'description' not in kwargs:
kwargs['description'] = ''
volume_id = kwargs.get('volume_id', None)
description = kwargs.get('description', '')
params = {'Action': 'CreateSnapshot'}
if volume_id is None:
raise SaltCloudSystemExit(
'A volume_id must be specified to create a snapshot.'
)
if 'volume_id' in kwargs:
params['VolumeId'] = kwargs['volume_id']
if 'description' in kwargs:
params['Description'] = kwargs['description']
params = {'Action': 'CreateSnapshot',
'VolumeId': volume_id,
'Description': description}
log.debug(params)
data = aws.query(params,
return_url=True,
return_root=True,
location=get_location(),
provider=get_provider(),
opts=__opts__,
sigver='4')
sigver='4')[0]
r_data = {}
for d in data:
@ -3827,7 +3840,7 @@ def create_snapshot(kwargs=None, call=None, wait_to_finish=False):
argument_being_watched='status',
required_argument_response='completed')
return data
return r_data
def delete_snapshot(kwargs=None, call=None):

View file

@ -348,7 +348,7 @@ class AutoKey(object):
autosign_dir = os.path.join(self.opts['pki_dir'], 'minions_autosign')
# cleanup expired files
expire_minutes = self.opts.get('autosign_expire_minutes', 10)
expire_minutes = self.opts.get('autosign_timeout', 120)
if expire_minutes > 0:
min_time = time.time() - (60 * int(expire_minutes))
for root, dirs, filenames in os.walk(autosign_dir):

View file

@ -1558,6 +1558,7 @@ class Minion(MinionBase):
del self.pub_channel
self._connect_master_future = self.connect_master()
self.block_until_connected() # TODO: remove
self.functions, self.returners, self.function_errors = self._load_modules()
self._fire_master_minion_start()
log.info('Minion is ready to receive requests!')

View file

@ -231,12 +231,25 @@ def create_or_update_alarm(
if isinstance(ok_actions, string_types):
ok_actions = ok_actions.split(",")
# convert action names into ARN's
alarm_actions = convert_to_arn(alarm_actions, region, key, keyid, profile)
insufficient_data_actions = convert_to_arn(
insufficient_data_actions, region, key, keyid, profile
)
ok_actions = convert_to_arn(ok_actions, region, key, keyid, profile)
# convert provided action names into ARN's
if alarm_actions:
alarm_actions = convert_to_arn(alarm_actions,
region=region,
key=key,
keyid=keyid,
profile=profile)
if insufficient_data_actions:
insufficient_data_actions = convert_to_arn(insufficient_data_actions,
region=region,
key=key,
keyid=keyid,
profile=profile)
if ok_actions:
ok_actions = convert_to_arn(ok_actions,
region=region,
key=key,
keyid=keyid,
profile=profile)
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)

View file

@ -276,14 +276,20 @@ def list_tab(user):
ret['special'].append(dat)
elif line.startswith('#'):
# It's a comment! Catch it!
comment = line.lstrip('# ')
comment_line = line.lstrip('# ')
# load the identifier if any
if SALT_CRON_IDENTIFIER in comment:
parts = comment.split(SALT_CRON_IDENTIFIER)
comment = parts[0].rstrip()
if SALT_CRON_IDENTIFIER in comment_line:
parts = comment_line.split(SALT_CRON_IDENTIFIER)
comment_line = parts[0].rstrip()
# skip leading :
if len(parts[1]) > 1:
identifier = parts[1][1:]
if comment is None:
comment = comment_line
else:
comment += '\n' + comment_line
elif len(line.split()) > 5:
# Appears to be a standard cron line
comps = line.split()

View file

@ -26,6 +26,14 @@ from salt.exceptions import SaltInvocationError
# Import 3rd-party libs
import salt.ext.six as six
try:
from shlex import quote as _cmd_quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _cmd_quote
from salt.exceptions import (
SaltInvocationError
)
# Set up logging
log = logging.getLogger(__name__)
@ -826,15 +834,15 @@ def trust_key(keyid=None,
if trust_level not in _VALID_TRUST_LEVELS:
return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS))
cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(fingerprint,
NUM_TRUST_DICT[trust_level],
_check_gpg())
cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(_cmd_quote(fingerprint),
_cmd_quote(NUM_TRUST_DICT[trust_level]),
_cmd_quote(_check_gpg()))
_user = user
if user == 'salt':
homeDir = os.path.join(salt.syspaths.CONFIG_DIR, 'gpgkeys')
cmd = '{0} --homedir {1}'.format(cmd, homeDir)
_user = 'root'
res = __salt__['cmd.run_all'](cmd, runas=_user)
res = __salt__['cmd.run_all'](cmd, runas=_user, python_shell=True)
if not res['retcode'] == 0:
ret['res'] = False

View file

@ -51,6 +51,7 @@ Module for handling openstack keystone calls.
# Import Python libs
from __future__ import absolute_import
import logging
# Import Salt Libs
import salt.ext.six as six
@ -66,6 +67,8 @@ try:
except ImportError:
pass
log = logging.getLogger(__name__)
def __virtual__():
'''
@ -707,7 +710,13 @@ def user_get(user_id=None, name=None, profile=None, **connection_args):
break
if not user_id:
return {'Error': 'Unable to resolve user id'}
user = kstone.users.get(user_id)
try:
user = kstone.users.get(user_id)
except keystoneclient.exceptions.NotFound:
msg = 'Could not find user \'{0}\''.format(user_id)
log.error(msg)
return {'Error': msg}
ret[user.name] = {'id': user.id,
'name': user.name,
'email': user.email,

View file

@ -391,7 +391,7 @@ def vgremove(vgname):
salt mymachine lvm.vgremove vgname
salt mymachine lvm.vgremove vgname force=True
'''
cmd = ['vgremove' '-f', vgname]
cmd = ['vgremove', '-f', vgname]
out = __salt__['cmd.run'](cmd, python_shell=False)
return out.strip()

View file

@ -130,3 +130,23 @@ def chgid(name, gid):
if post_gid != pre_gid:
return post_gid == gid
return False
def members(name, members_list):
'''
Replaces members of the group with a provided list.
.. versionadded:: 2015.5.4
CLI Example:
salt '*' group.members foo 'user1,user2,user3,...'
Replaces a membership list for a local group 'foo'.
foo:x:1234:user1,user2,user3,...
'''
retcode = __salt__['cmd.retcode']('pw groupmod {0} -M {1}'.format(
name, members_list), python_shell=False)
return not retcode

View file

@ -9,8 +9,9 @@ import copy
import logging
try:
import pwd
HAS_PWD = True
except ImportError:
pass
HAS_PWD = False
# Import 3rd party libs
import salt.ext.six as six
@ -29,7 +30,9 @@ def __virtual__():
'''
Set the user module if the kernel is FreeBSD
'''
return __virtualname__ if __grains__['kernel'] == 'FreeBSD' else False
if HAS_PWD and __grains__['kernel'] == 'FreeBSD':
return __virtualname__
return False
def _get_gecos(name):

View file

@ -1,29 +1,36 @@
# -*- coding: utf-8 -*-
'''
Manage the registry on Windows.
===========================
Manage the Windows registry
===========================
The read_key and set_key functions will be updated in Boron to reflect proper
registry usage. The registry has three main components. Hives, Keys, and Values.
### Hives
-----
Hives
-----
Hives are the main sections of the registry and all begin with the word HKEY.
- HKEY_LOCAL_MACHINE
- HKEY_CURRENT_USER
- HKEY_USER
### Keys
----
Keys
----
Keys are the folders in the registry. Keys can have many nested subkeys. Keys
can have a value assigned to them under the (Default)
### Values
Values are name/data pairs. There can be many values in a key. The (Default)
value corresponds to the Key, the rest are their own value pairs.
-----------------
Values or Entries
-----------------
Values/Entries are name/data pairs. There can be many values in a key. The
(Default) value corresponds to the Key, the rest are their own value pairs.
:depends: - winreg Python module
'''
# TODO: Figure out the exceptions _winreg can raise and properly catch
# them instead of a bare except that catches any exception at all
# TODO: Figure out the exceptions _winreg can raise and properly catch them
# Import python libs
from __future__ import absolute_import
@ -142,44 +149,35 @@ def read_key(hkey, path, key=None):
key=path,
vname=key)
registry = Registry()
hive = registry.hkeys[hkey]
try:
value = _winreg.QueryValue(hive, path)
if value:
ret['vdata'] = value
else:
ret['vdata'] = None
ret['comment'] = 'Empty Value'
except WindowsError as exc: # pylint: disable=E0602
log.debug(exc)
ret['comment'] = '{0}'.format(exc)
ret['success'] = False
return ret
return read_value(hive=hkey, key=path)
def read_value(hive, key, vname=None):
r'''
Reads a registry value or the default value for a key.
Reads a registry value entry or the default value for a key.
:param hive: string
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param str hive:
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param key: string
The key (looks like a path) to the value name.
:param str key:
The key (looks like a path) to the value name.
:param vname: string
The value name. These are the individual name/data pairs under the key. If
not passed, the key (Default) value will be returned
:param str vname:
The value name. These are the individual name/data pairs under the key.
If not passed, the key (Default) value will be returned
:return: dict
A dictionary containing the passed settings as well as the value_data if
successful. If unsuccessful, sets success to False
:return:
A dictionary containing the passed settings as well as the value_data if
successful. If unsuccessful, sets success to False
If vname is not passed:
- Returns the first unnamed value (Default) as a string.
- Returns none if first unnamed value is empty.
- Returns False if key not found.
:rtype: dict
CLI Example:
@ -205,9 +203,9 @@ def read_value(hive, key, vname=None):
try:
handle = _winreg.OpenKey(hive, key)
value, vtype = _winreg.QueryValueEx(handle, vname)
if value:
ret['vdata'] = value
vdata, vtype = _winreg.QueryValueEx(handle, vname)
if vdata:
ret['vdata'] = vdata
ret['vtype'] = registry.vtype_reverse[vtype]
else:
ret['comment'] = 'Empty Value'
@ -257,53 +255,45 @@ def set_key(hkey, path, value, key=None, vtype='REG_DWORD', reflection=True):
vdata=value,
vtype=vtype)
registry = Registry()
hive = registry.hkeys[hkey]
vtype = registry.vtype['REG_SZ']
try:
_winreg.SetValue(hive, path, vtype, value)
return True
except WindowsError as exc: # pylint: disable=E0602
log.error(exc)
return False
return set_value(hive=hkey, key=path, vdata=value, vtype=vtype)
def set_value(hive, key, vname=None, vdata=None, vtype='REG_SZ', reflection=True):
'''
Sets a registry value.
Sets a registry value entry or the default value for a key.
:param hive: string
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param str hive:
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param key: string
The key (looks like a path) to the value name.
:param str key:
The key (looks like a path) to the value name.
:param vname: string
The value name. These are the individual name/data pairs under the key. If
not passed, the key (Default) value will be set.
:param str vname:
The value name. These are the individual name/data pairs under the key.
If not passed, the key (Default) value will be set.
:param vdata: string
The value data to be set.
:param str vdata:
The value data to be set.
:param vtype: string
The value type. Can be one of the following:
- REG_BINARY
- REG_DWORD
- REG_EXPAND_SZ
- REG_MULTI_SZ
- REG_SZ
:param str vtype:
The value type. Can be one of the following:
- REG_BINARY
- REG_DWORD
- REG_EXPAND_SZ
- REG_MULTI_SZ
- REG_SZ
:param reflection: boolean
A boolean value indicating that the value should also be set in the
Wow6432Node portion of the registry. Only applies to 64 bit Windows. This
setting is ignored for 32 bit Windows.
:param bool reflection:
A boolean value indicating that the value should also be set in the
Wow6432Node portion of the registry. Only applies to 64 bit Windows.
This setting is ignored for 32 bit Windows.
:return: boolean
Returns True if successful, False if not
:return:
Returns True if successful, False if not
:rtype: bool
CLI Example:
@ -321,7 +311,7 @@ def set_value(hive, key, vname=None, vdata=None, vtype='REG_SZ', reflection=True
_winreg.SetValueEx(handle, vname, 0, vtype, vdata)
_winreg.CloseKey(handle)
return True
except WindowsError as exc: # pylint: disable=E0602
except (WindowsError, ValueError) as exc: # pylint: disable=E0602
log.error(exc)
return False
@ -353,7 +343,7 @@ def create_key(hkey, path, key=None, value=None, reflection=True):
salt '*' reg.create_key HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' '0.97'
'''
if key: # This if statement will be removed in Boron
salt.utils.warn_until('Boron', 'Use reg.set_value to set a registry '
salt.utils.warn_until('Boron', 'Use reg.set_value to create a registry '
'value. This functionality will be '
'removed in Salt Boron')
return set_value(hive=hkey,
@ -362,21 +352,10 @@ def create_key(hkey, path, key=None, value=None, reflection=True):
vdata=value,
vtype='REG_SZ')
registry = Registry()
hive = registry.hkeys[hkey]
key = path
access_mask = registry.reflection_mask[reflection]
try:
handle = _winreg.CreateKeyEx(hive, key, 0, access_mask)
_winreg.CloseKey(handle)
return True
except WindowsError as exc: # pylint: disable=E0602
log.error(exc)
return False
return set_value(hive=hkey, key=path)
def delete_key(hkey, path, key=None, reflection=True):
def delete_key(hkey, path, key=None, reflection=True, force=False):
'''
*** Incorrect Usage ***
The name of this function is misleading and will be changed to reflect
@ -396,29 +375,62 @@ def delete_key(hkey, path, key=None, reflection=True):
Delete a registry key
Note: This cannot delete a key with subkeys
CLI Example:
.. code-block:: bash
salt '*' reg.delete_key HKEY_CURRENT_USER 'SOFTWARE\\Salt'
:param str hkey: (will be changed to hive)
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param str path: (will be changed to key)
The key (looks like a path) to remove.
:param str key: (used incorrectly)
Will be removed in Boron
:param bool reflection:
A boolean value indicating that the value should also be removed from
the Wow6432Node portion of the registry. Only applies to 64 bit Windows.
This setting is ignored for 32 bit Windows.
Only applies to delete value. If the key parameter is passed, this
function calls delete_value instead. Will be changed in Boron.
:param bool force:
A boolean value indicating that all subkeys should be removed as well.
If this is set to False (default) and there are subkeys, the delete_key
function will fail.
:return:
Returns True if successful, False if not
If force=True, the results of delete_key_recursive are returned.
:rtype: bool
'''
if key: # This if statement will be removed in Boron
salt.utils.warn_until('Boron', 'Use reg.set_value to set a registry '
'value. This functionality will be '
'removed in Salt Boron')
salt.utils.warn_until('Boron',
'Variable names will be changed to match Windows '
'Registry terminology. These changes will be '
'made in Boron')
return delete_value(hive=hkey,
key=path,
vname=key,
reflection=reflection)
if force:
return delete_key_recursive(hkey, path)
registry = Registry()
hive = registry.hkeys[hkey]
key = path
try:
# Can't use delete_value to delete a key
_winreg.DeleteKey(hive, key)
return True
except WindowsError as exc: # pylint: disable=E0602
@ -426,30 +438,102 @@ def delete_key(hkey, path, key=None, reflection=True):
return False
def delete_key_recursive(hive, key):
'''
.. versionadded:: 2015.5.4
Delete a registry key to include all subkeys.
:param hive:
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param key:
The key to remove (looks like a path)
:return:
A dictionary listing the keys that deleted successfully as well as those
that failed to delete.
:rtype: dict
'''
# Functions for traversing the registry tree
def subkeys(key):
i = 0
while True:
try:
subkey = _winreg.EnumKey(key, i)
yield subkey
i += 1
except WindowsError: # pylint: disable=E0602
break
def traverse_registry_tree(hkey, keypath, ret):
key = _winreg.OpenKey(hkey, keypath, 0, _winreg.KEY_READ)
for subkeyname in subkeys(key):
subkeypath = r'{0}\{1}'.format(keypath, subkeyname)
ret = traverse_registry_tree(hkey, subkeypath, ret)
ret.append('{0}'.format(subkeypath))
return ret
# Instantiate the registry object
registry = Registry()
hkey = registry.hkeys[hive]
keypath = key
# Get a reverse list of registry keys to be deleted
key_list = []
key_list = traverse_registry_tree(hkey, keypath, key_list)
ret = {'Deleted': [],
'Failed': []}
# Delete all subkeys
for keypath in key_list:
try:
_winreg.DeleteKey(hkey, keypath)
ret['Deleted'].append(r'{0}\{1}'.format(hive, keypath))
except WindowsError as exc: # pylint: disable=E0602
log.error(exc)
ret['Failed'].append(r'{0}\{1} {2}'.format(hive, key, exc))
# Delete the key now that all the subkeys are deleted
try:
_winreg.DeleteKey(hkey, key)
ret['Deleted'].append(r'{0}\{1}'.format(hive, key))
except WindowsError as exc: # pylint: disable=E0602
log.error(exc)
ret['Failed'].append(r'{0}\{1} {2}'.format(hive, key, exc))
return ret
def delete_value(hive, key, vname=None, reflection=True):
'''
Deletes a registry value.
Delete a registry value entry or the default value for a key.
:param hive: string
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param str hive:
The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param key: string
The key (looks like a path) to the value name.
:param str key:
The key (looks like a path) to the value name.
:param vname: string
The value name. These are the individual name/data pairs under the key. If
not passed, the key (Default) value will be deleted.
:param str vname:
The value name. These are the individual name/data pairs under the key.
If not passed, the key (Default) value will be deleted.
:param reflection: boolean
A boolean value indicating that the value should also be set in the
Wow6432Node portion of the registry. Only applies to 64 bit Windows. This
setting is ignored for 32 bit Windows.
:param bool reflection:
A boolean value indicating that the value should also be set in the
Wow6432Node portion of the registry. Only applies to 64 bit Windows.
This setting is ignored for 32 bit Windows.
:return: boolean
Returns True if successful, False if not
:return:
Returns True if successful, False if not
:rtype: bool
CLI Example:

View file

@ -97,6 +97,11 @@ def list_(show_all=False, where=None, return_yaml=True):
del schedule[job]
continue
# if enabled is not included in the job,
# assume job is enabled.
if 'enabled' not in schedule[job]:
schedule[job]['enabled'] = True
for item in pycopy.copy(schedule[job]):
if item not in SCHEDULE_CONF:
del schedule[job][item]

View file

@ -9,8 +9,9 @@ import re
try:
import pwd
HAS_PWD = True
except ImportError:
pass
HAS_PWD = False
import logging
import copy
@ -32,10 +33,9 @@ __virtualname__ = 'user'
def __virtual__():
'''
Set the user module if the kernel is Linux, OpenBSD or NetBSD
and remove some of the functionality on OS X
'''
if __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD'):
if HAS_PWD and __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD'):
return __virtualname__
return False

View file

@ -193,7 +193,11 @@ Overriding the alarm values on the resource:
attributes:
threshold: 2.0
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Libs
import salt.utils.dictupdate as dictupdate
from salt.exceptions import SaltInvocationError
import salt.ext.six as six
@ -307,13 +311,17 @@ def present(
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _attributes_present(name, attributes, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
if attributes:
_ret = _attributes_present(name, attributes, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _health_check_present(name, health_check, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])

View file

@ -125,8 +125,7 @@ from salt.ext.six import string_types
import salt.utils
from salt.modules.cron import (
_needs_change,
_cron_matched,
SALT_CRON_NO_IDENTIFIER
_cron_matched
)
@ -217,7 +216,7 @@ def present(name,
month='*',
dayweek='*',
comment=None,
identifier=None):
identifier=False):
'''
Verifies that the specified cron job is present for the specified user.
For more advanced information about what exactly can be set in the cron
@ -257,8 +256,8 @@ def present(name,
edits. This defaults to the state id
'''
name = ' '.join(name.strip().split())
if not identifier:
identifier = SALT_CRON_NO_IDENTIFIER
if identifier is False:
identifier = name
ret = {'changes': {},
'comment': '',
'name': name,
@ -313,7 +312,7 @@ def present(name,
def absent(name,
user='root',
identifier=None,
identifier=False,
**kwargs):
'''
Verifies that the specified cron job is absent for the specified user; only
@ -335,8 +334,8 @@ def absent(name,
### of unsupported arguments will result in a traceback.
name = ' '.join(name.strip().split())
if not identifier:
identifier = SALT_CRON_NO_IDENTIFIER
if identifier is False:
identifier = name
ret = {'name': name,
'result': True,
'changes': {},

View file

@ -220,8 +220,7 @@ def absent(name, destructive=False):
'changes': {},
'result': True,
'comment': ''}
grain = __grains__.get(name)
if grain:
if name in __grains__:
if __opts__['test']:
ret['result'] = None
if destructive is True:

View file

@ -8,10 +8,12 @@ Manage OpenStack configuration file settings.
:platform: linux
'''
# Import Python Libs
from __future__ import absolute_import
# Import salt libs
import salt.exceptions
# Import Salt Libs
from salt.exceptions import CommandExecutionError
def __virtual__():
@ -48,18 +50,30 @@ def present(name, filename, section, value, parameter=None):
if parameter is None:
parameter = name
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
try:
old_value = __salt__['openstack_config.get'](filename=filename,
section=section,
parameter=parameter)
if old_value == value:
return {'name': name,
'changes': {},
'result': True,
'comment': 'The value is already set to the correct value'}
ret['result'] = True
ret['comment'] = 'The value is already set to the correct value'
return ret
except salt.exceptions.CommandExecutionError as e:
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Value \'{0}\' is set to be changed to \'{1}\'.'.format(
old_value,
value
)
return ret
except CommandExecutionError as e:
if not str(e).lower().startswith('parameter not found:'):
raise
@ -68,10 +82,11 @@ def present(name, filename, section, value, parameter=None):
parameter=parameter,
value=value)
return {'name': name,
'changes': {'Value': 'Updated'},
'result': True,
'comment': 'The value has been updated'}
ret['changes'] = {'Value': 'Updated'}
ret['result'] = True
ret['comment'] = 'The value has been updated'
return ret
def absent(name, filename, section, parameter=None):
@ -92,23 +107,35 @@ def absent(name, filename, section, parameter=None):
if parameter is None:
parameter = name
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
try:
old_value = __salt__['openstack_config.get'](filename=filename,
section=section,
parameter=parameter)
except salt.exceptions.CommandExecutionError as e:
except CommandExecutionError as e:
if str(e).lower().startswith('parameter not found:'):
return {'name': name,
'changes': {},
'result': True,
'comment': 'The value is already absent'}
ret['result'] = True
ret['comment'] = 'The value is already absent'
return ret
raise
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Value \'{0}\' is set to be deleted.'.format(
old_value
)
return ret
__salt__['openstack_config.delete'](filename=filename,
section=section,
parameter=parameter)
return {'name': name,
'changes': {'Value': 'Deleted'},
'result': True,
'comment': 'The value has been deleted'}
ret['changes'] = {'Value': 'Deleted'}
ret['result'] = True
ret['comment'] = 'The value has been deleted'
return ret

View file

@ -59,7 +59,7 @@ def __virtual__():
'''
Only load if the pip module is available in __salt__
'''
if HAS_PIP and 'pip.list' in __salt__:
if 'pip.list' in __salt__:
return __virtualname__
return False
@ -100,6 +100,16 @@ def _check_pkg_version_format(pkg):
ret = {'result': False, 'comment': None,
'prefix': None, 'version_spec': None}
if not HAS_PIP:
ret['comment'] = (
'An importable pip module is required but could not be found on '
'your system. This usually means that the system''s pip package '
'is not installed properly.'
)
return ret
from_vcs = False
try:
# Get the requirement object from the pip library

View file

@ -42,14 +42,14 @@ def __virtual__():
return salt.utils.which('rabbitmqctl') is not None
def _check_perms_changes(name, newperms):
def _check_perms_changes(name, newperms, runas=None):
'''
Whether Rabbitmq user's permissions need to be changed
'''
if not newperms:
return False
existing_perms = __salt__['rabbitmq.list_user_permissions'](name)
existing_perms = __salt__['rabbitmq.list_user_permissions'](name, runas=runas)
perm_need_change = False
for vhost_perms in newperms:
@ -63,14 +63,14 @@ def _check_perms_changes(name, newperms):
return perm_need_change
def _check_tags_changes(name, newtags):
def _check_tags_changes(name, newtags, runas=None):
'''
Whether Rabbitmq user's tags need to be changed
'''
if newtags:
if isinstance(newtags, str):
newtags = newtags.split()
return __salt__['rabbitmq.list_users']()[name] - set(newtags)
return __salt__['rabbitmq.list_users'](runas=runas)[name] - set(newtags)
else:
return []
@ -147,7 +147,7 @@ def present(name,
name, runas=runas)
changes['old'] += 'Removed password.\n'
if _check_tags_changes(name, tags):
if _check_tags_changes(name, tags, runas=runas):
if __opts__['test']:
ret['result'] = None
ret['comment'] += ('Tags for user {0} '
@ -158,7 +158,7 @@ def present(name,
)
changes['new'] += 'Set tags: {0}\n'.format(tags)
if _check_perms_changes(name, perms):
if _check_perms_changes(name, perms, runas=runas):
if __opts__['test']:
ret['result'] = None
ret['comment'] += ('Permissions for user {0} '
@ -167,7 +167,7 @@ def present(name,
for vhost_perm in perms:
for vhost, perm in six.iteritems(vhost_perm):
result.update(__salt__['rabbitmq.set_permissions'](
vhost, name, perm[0], perm[1], perm[2], runas)
vhost, name, perm[0], perm[1], perm[2], runas=runas)
)
changes['new'] += (
'Set permissions {0} for vhost {1}'

View file

@ -1,11 +1,68 @@
# -*- coding: utf-8 -*-
'''
Manage the registry on Windows
r'''
===========================
Manage the Windows registry
===========================
Many python developers think of registry keys as if they were python keys in a
dictionary which is not the case. The windows registry is broken down into the
following components:
-----
Hives
-----
This is the top level of the registry. They all begin with HKEY.
- HKEY_CLASSES_ROOT (HKCR)
- HKEY_CURRENT_USER(HKCU)
- HKEY_LOCAL MACHINE (HKLM)
- HKEY_USER (HKU)
- HKEY_CURRENT_CONFIG
----
Keys
----
Hives contain keys. These are basically the folders beneath the hives. They can
contain any number of subkeys.
-----------------
Values or Entries
-----------------
Values or Entries are the name/data pairs beneath the keys and subkeys. All keys
have a default name/data pair. It is usually "(Default)"="(value not set)". The
actual value for the name and the date is Null. The registry editor will display
"(Default)" and "(value not set)".
-------
Example
-------
The following example is taken from the windows startup portion of the registry:
```
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"RTHDVCPL"="\"C:\\Program Files\\Realtek\\Audio\\HDA\\RtkNGUI64.exe\" -s"
"NvBackend"="\"C:\\Program Files (x86)\\NVIDIA Corporation\\Update Core\\NvBackend.exe\""
"BTMTrayAgent"="rundll32.exe \"C:\\Program Files (x86)\\Intel\\Bluetooth\\btmshellex.dll\",TrayApp"
```
In this example these are the values for each:
Hive: `HKEY_LOCAL_MACHINE`
Key and subkeys: `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`
Value:
- There are 3 value names: `RTHDVCPL`, `NvBackend`, and `BTMTrayAgent`
- Each value name has a corresponding value
'''
from __future__ import absolute_import
# Import python libs
import logging
# Import salt libs
import salt.utils
log = logging.getLogger(__name__)
@ -23,7 +80,7 @@ def _parse_key_value(key):
splt = key.split("\\")
hive = splt.pop(0)
vname = splt.pop(-1)
key = r'\\'.join(splt)
key = '\\'.join(splt)
return hive, key, vname
@ -33,71 +90,149 @@ def _parse_key(key):
'''
splt = key.split("\\")
hive = splt.pop(0)
key = r'\\'.join(splt)
key = '\\'.join(splt)
return hive, key
def present(name, value, vtype='REG_SZ', reflection=True):
def present(name, value=None, vname=None, vdata=None, vtype='REG_SZ', reflection=True):
'''
Set a registry value
Ensure a registry key or value is present.
Optionally set ``reflection`` to ``False`` to disable reflection.
``reflection`` has no effect on a 32-bit OS.
:param str name:
A string value representing the full path of the key to include the
HIVE, Key, and all Subkeys. For example:
In the example below, this will prevent Windows from silently creating
the key in:
``HKEY_CURRENT_USER\\SOFTWARE\\Wow6432Node\\Salt\\version``
``HKEY_LOCAL_MACHINE\\SOFTWARE\\Salt``
Valid hive values include:
- HKEY_CURRENT_USER or HKCU
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_USERS or HKU
:param str value:
Deprecated. Use vname and vdata instead. Included here for backwards
compatability.
:param str vname:
The name of the value you'd like to create beneath the Key. If this
parameter is not passed it will assume you want to set the (Default)
value
:param str vdata:
The value you'd like to set for the Key. If a value name (vname) is
passed, this will be the data for that value name. If not, this will be
the (Default) value for the key.
The type for the (Default) value is always REG_SZ and cannot be changed.
This parameter is optional. If not passed, the Key will be created with.
:param str vtype:
The value type for the data you wish to store in the registry. Valid
values are:
- REG_BINARY
- REG_DWORD
- REG_EXPAND_SZ
- REG_MULTI_SZ
- REG_SZ (Default)
:param bool reflection:
On 64 bit machines a duplicate value will be created in the
``Wow6432Node`` for 32bit programs. This only applies to the SOFTWARE
key. This option is ignored on 32bit operating systems. This value
defaults to True. Set it to False to disable reflection.
:return:
Returns a dictionary showing the results of the registry operation.
:rtype: dict
The following example will set the ``(Default)`` value for the
``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to ``0.15.3``. The
value will not be reflected in ``Wow6432Node``:
Example:
.. code-block:: yaml
HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version:
HKEY_CURRENT_USER\\SOFTWARE\\Salt:
reg.present:
- value: 0.15.3
- vtype: REG_SZ
- vdata: 0.15.3
- reflection: False
The following example will set the value for the ``version`` entry under the
``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to ``0.15.3``. The
value will be reflected in ``Wow6432Node``:
Example:
.. code-block:: yaml
HKEY_CURRENT_USER\\SOFTWARE\\Salt:
reg.present:
- vname: version
- vdata: 0.15.3
In the above example the path is interpreted as follows:
- ``HKEY_CURRENT_USER`` is the hive
- ``SOFTWARE\\Salt`` is the key
- ``version`` is the value name
So ``version`` will be created in the ``SOFTWARE\\Salt`` key in the
``HKEY_CURRENT_USER`` hive and given the ``REG_SZ`` value of ``0.15.3``.
- ``vname`` is the value name ('version') that will be created under the key
- ``vdata`` is the data that will be assigned to 'version'
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
hive, key, vname = _parse_key_value(name)
# This is for backwards compatibility
# If 'value' is passed a value, vdata becomes value and the vname is
# obtained from the key path
if value:
hive, key, vname = _parse_key_value(name)
vdata = value
ret['comment'] = 'State file is using deprecated syntax. Please update.'
salt.utils.warn_until(
'Boron',
'The \'value\' argument has been deprecated. '
'Please use vdata instead.'
)
else:
hive, key = _parse_key(name)
# Determine what to do
if value == __salt__['reg.read_value'](hive, key, vname)['vdata']:
ret['comment'] = '{0} is already configured'.format(name)
reg_current = __salt__['reg.read_value'](hive, key, vname)
if vdata == reg_current['vdata'] and reg_current['success']:
ret['comment'] = '{0} in {1} is already configured'.\
format(vname if vname else '(Default)', name)
return ret
else:
ret['changes'] = {'reg': 'configured to {0}'.format(value)}
add_change = {'Key': r'{0}\{1}'.format(hive, key),
'Entry': '{0}'.format(vname if vname else '(Default)'),
'Value': '{0}'.format(vdata if vdata else '(Empty String)')}
# Check for test option
if __opts__['test']:
ret['result'] = None
ret['changes'] = {'reg': {'Will add': add_change}}
return ret
# Configure the value
ret['result'] = __salt__['reg.set_value'](hive, key, vname, value, vtype,
ret['result'] = __salt__['reg.set_value'](hive, key, vname, vdata, vtype,
reflection)
if not ret:
if not ret['result']:
ret['changes'] = {}
ret['comment'] = 'could not configure the registry key'
ret['comment'] = r'Failed to add {0} to {1}\{2}'.format(name, hive, key)
else:
ret['changes'] = {'reg': {'Added': add_change}}
ret['comment'] = r'Added {0} to {1}\{2}'.format(name, hive, key)
return ret
def absent(name):
def absent(name, vname=None):
'''
Remove a registry value
Ensure a registry value is removed. To remove a key use key_absent.
Example:
@ -118,14 +253,89 @@ def absent(name):
'changes': {},
'comment': ''}
hive, key, vname = _parse_key_value(name)
hive, key = _parse_key(name)
# Determine what to do
if not __salt__['reg.read_value'](hive, key, vname)['success']:
hive, key, vname = _parse_key_value(name)
if not __salt__['reg.read_value'](hive, key, vname)['success']:
ret['comment'] = '{0} is already absent'.format(name)
return ret
remove_change = {'Key': r'{0}\{1}'.format(hive, key),
'Entry': '{0}'.format(vname if vname else '(Default)')}
# Check for test option
if __opts__['test']:
ret['result'] = None
ret['changes'] = {'reg': {'Will remove': remove_change}}
return ret
# Delete the value
ret['result'] = __salt__['reg.delete_value'](hive, key, vname)
if not ret['result']:
ret['changes'] = {}
ret['comment'] = r'Failed to remove {0} from {1}\{2}'.format(name, hive,
key)
else:
ret['changes'] = {'reg': {'Removed': remove_change}}
ret['comment'] = r'Removed {0} from {1}\{2}'.format(name, hive, key)
return ret
def key_absent(name, force=False):
r'''
.. versionadded:: 2015.5.4
Ensure a registry key is removed. This will remove a key and all value
entries it contains. It will fail if the key contains subkeys.
:param str name:
A string representing the full path to the key to be removed to include
the hive and the keypath. The hive can be any of the following:
- HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU
:param bool force:
A boolean value indicating that all subkeys should be deleted with the
key. If force=False and subkeys exists beneath the key you want to
delete, key_absent will fail. Use with caution. The default is False.
:return:
Returns a dictionary showing the results of the registry operation.
:rtype: dict
The following example will delete the ``SOFTWARE\Salt`` key and all subkeys
under the ``HKEY_CURRENT_USER`` hive.
Example::
'HKEY_CURRENT_USER\SOFTWARE\Salt':
reg.key_absent:
- force: True
In the above example the path is interpreted as follows:
- ``HKEY_CURRENT_USER`` is the hive
- ``SOFTWARE\Salt`` is the key
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
hive, key = _parse_key(name)
# Determine what to do
if not __salt__['reg.read_value'](hive, key)['success']:
ret['comment'] = '{0} is already absent'.format(name)
return ret
else:
ret['changes'] = {'reg': 'Removed {0}'.format(name)}
ret['changes'] = {'reg': {
'Removed': {
'Key': r'{0}\{1}'.format(hive, key)
}}}
# Check for test option
if __opts__['test']:
@ -133,9 +343,10 @@ def absent(name):
return ret
# Delete the value
ret['result'] = __salt__['reg.delete_value'](hive, key, vname)
if not ret['result']:
__salt__['reg.delete_key'](hive, key, force=force)
if __salt__['reg.read_value'](hive, key)['success']:
ret['result'] = False
ret['changes'] = {}
ret['comment'] = 'failed to remove registry key {0}'.format(name)
ret['comment'] = 'Failed to remove registry key {0}'.format(name)
return ret

View file

@ -1,11 +1,6 @@
# -*- coding: utf-8 -*-
'''
Manage Windows Package Repository
.. note::
This state only loads on minions that have the ``roles: salt-master`` grain
set.
'''
from __future__ import absolute_import
@ -21,15 +16,7 @@ import salt.config
def __virtual__():
'''
Load this state if this is the salt-master
'''
try:
return ('winrepo'
if 'salt-master' in __grains__.get('roles', [])
else False)
except TypeError:
return False
return 'winrepo'
def genrepo(name, force=False, allow_empty=False):

View file

@ -68,7 +68,7 @@ def get_iam_region(version='latest', url='http://169.254.169.254',
'''
Gets instance identity document and returns region
'''
instance_identity_url = '{0}/{1}/latest/dynamic/instance-identity/document'.format(url, version)
instance_identity_url = '{0}/{1}/dynamic/instance-identity/document'.format(url, version)
region = None
try:

View file

@ -273,7 +273,7 @@ class SerializerExtension(Extension, object):
{%- set json_src = "{'bar': 'for real'}"|load_json %}
Dude, {{ yaml_src.foo }} {{ json_src.bar }}!
will be rendered has::
will be rendered as::
Dude, it works for real!
@ -299,7 +299,7 @@ class SerializerExtension(Extension, object):
{% endload %}
Dude, {{ yaml_src.foo }} {{ json_src.bar }}!
will be rendered has::
will be rendered as::
Dude, it works for real!

327
setup.py
View file

@ -5,7 +5,7 @@ The setup script for salt
'''
from __future__ import absolute_import
# pylint: disable=file-perms
# pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903
# For Python 2.5. A no-op on 2.6 and above.
@ -18,7 +18,7 @@ import time
try:
from urllib2 import urlopen
except ImportError:
from urllib.request import urlopen
from urllib.request import urlopen # pylint: disable=no-name-in-module
from datetime import datetime
# pylint: disable=E0611
import distutils.dist
@ -71,6 +71,7 @@ WITH_SETUPTOOLS = False
if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
try:
from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import egg_info
@ -103,6 +104,7 @@ except ImportError:
SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py')
SALT_VERSION_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_version.py')
SALT_SYSPATHS_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_syspaths.py')
SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'base.txt')
SALT_ZEROMQ_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'zeromq.txt')
SALT_RAET_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'raet.txt')
@ -125,8 +127,16 @@ def _parse_requirements_file(requirements_file):
line = line.strip()
if not line or line.startswith(('#', '-r')):
continue
if IS_WINDOWS_PLATFORM and 'libcloud' in line:
continue
if IS_WINDOWS_PLATFORM:
if 'libcloud' in line:
continue
if 'pycrypto' in line.lower():
# On windows we install PyCrypto using python wheels
continue
if 'm2crypto' in line.lower() and __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
# In Windows, we're installing M2CryptoWin{32,64} which comes
# compiled
continue
parsed_requirements.append(line)
return parsed_requirements
# <---- Helper Functions ---------------------------------------------------------------------------------------------
@ -139,10 +149,14 @@ class WriteSaltVersion(Command):
user_options = []
def initialize_options(self):
pass
'''
Abstract method that is required to be overwritten
'''
def finalize_options(self):
pass
'''
Abstract method that is required to be overwritten
'''
def run(self):
if not os.path.exists(SALT_VERSION_HARDCODED):
@ -161,10 +175,9 @@ class WriteSaltVersion(Command):
# pylint: enable=E0602
class WriteSaltSshPackaingFile(Command):
class GenerateSaltSyspaths(Command):
description = 'Write salt\'s ssh packaging file'
user_options = []
description = 'Generate salt\'s hardcoded syspaths file'
def initialize_options(self):
pass
@ -172,6 +185,45 @@ class WriteSaltSshPackaingFile(Command):
def finalize_options(self):
pass
def run(self):
# Write the syspaths file
if getattr(self.distribution, 'salt_syspaths_hardcoded_path', None) is None:
print('This command is not meant to be called on it\'s own')
exit(1)
# Write the system paths file
open(self.distribution.salt_syspaths_hardcoded_path, 'w').write(
INSTALL_SYSPATHS_TEMPLATE.format(
date=datetime.utcnow(),
root_dir=self.distribution.salt_root_dir,
config_dir=self.distribution.salt_config_dir,
cache_dir=self.distribution.salt_cache_dir,
sock_dir=self.distribution.salt_sock_dir,
srv_root_dir=self.distribution.salt_srv_root_dir,
base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
logs_dir=self.distribution.salt_logs_dir,
pidfile_dir=self.distribution.salt_pidfile_dir,
)
)
class WriteSaltSshPackaingFile(Command):
description = 'Write salt\'s ssh packaging file'
user_options = []
def initialize_options(self):
'''
Abstract method that is required to be overwritten
'''
def finalize_options(self):
'''
Abstract method that is required to be overwritten
'''
def run(self):
if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
# Write the salt-ssh packaging file
@ -184,6 +236,198 @@ class WriteSaltSshPackaingFile(Command):
# pylint: enable=E0602
if WITH_SETUPTOOLS:
class Develop(develop):
user_options = develop.user_options + [
('write_salt_version', None,
'Generate Salt\'s _version.py file which allows proper version '
'reporting. This defaults to False on develop/editable setups. '
'If WRITE_SALT_VERSION is found in the environment this flag is '
'switched to True.'),
('generate_salt_syspaths', None,
'Generate Salt\'s _syspaths.py file which allows tweaking some '
'common paths that salt uses. This defaults to False on '
'develop/editable setups. If GENERATE_SALT_SYSPATHS is found in '
'the environment this flag is switched to True.'),
('mimic_salt_install', None,
'Mimmic the install command when running the develop command. '
'This will generate salt\'s _version.py and _syspaths.py files. '
'Generate Salt\'s _syspaths.py file which allows tweaking some '
'This defaults to False on develop/editable setups. '
'If MIMIC_INSTALL is found in the environment this flag is '
'switched to True.')
]
boolean_options = develop.boolean_options + [
'write_salt_version',
'generate_salt_syspaths',
'mimic_salt_install'
]
def initialize_options(self):
develop.initialize_options(self)
self.write_salt_version = False
self.generate_salt_syspaths = False
self.mimic_salt_install = False
def finalize_options(self):
develop.finalize_options(self)
if 'WRITE_SALT_VERSION' in os.environ:
self.write_salt_version = True
if 'GENERATE_SALT_SYSPATHS' in os.environ:
self.generate_salt_syspaths = True
if 'MIMIC_SALT_INSTALL' in os.environ:
self.mimic_salt_install = True
if self.mimic_salt_install:
self.write_salt_version = True
self.generate_salt_syspaths = True
def run(self):
if IS_WINDOWS_PLATFORM:
if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
# Install M2Crypto first
self.distribution.salt_installing_m2crypto_windows = True
self.run_command('install-m2crypto-windows')
self.distribution.salt_installing_m2crypto_windows = None
# Install PyCrypto
self.distribution.salt_installing_pycrypto_windows = True
self.run_command('install-pycrypto-windows')
self.distribution.salt_installing_pycrypto_windows = None
# Download the required DLLs
self.distribution.salt_download_windows_dlls = True
self.run_command('download-windows-dlls')
self.distribution.salt_download_windows_dlls = None
if self.write_salt_version is True:
self.distribution.running_salt_install = True
self.distribution.salt_version_hardcoded_path = SALT_VERSION_HARDCODED
self.run_command('write_salt_version')
if self.generate_salt_syspaths:
self.distribution.salt_syspaths_hardcoded_path = SALT_SYSPATHS_HARDCODED
self.run_command('generate_salt_syspaths')
# Resume normal execution
develop.run(self)
class InstallM2CryptoWindows(Command):
description = 'Install M2CryptoWindows'
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
if getattr(self.distribution, 'salt_installing_m2crypto_windows', None) is None:
print('This command is not meant to be called on it\'s own')
exit(1)
import platform
from pip.utils import call_subprocess
from pip.utils.logging import indent_log
platform_bits, _ = platform.architecture()
with indent_log():
call_subprocess(
['pip', 'install', '--egg', 'M2CryptoWin{0}'.format(platform_bits[:2])]
)
class InstallPyCryptoWindowsWheel(Command):
description = 'Install PyCrypto on Windows'
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
if getattr(self.distribution, 'salt_installing_pycrypto_windows', None) is None:
print('This command is not meant to be called on it\'s own')
exit(1)
import platform
from pip.utils import call_subprocess
from pip.utils.logging import indent_log
platform_bits, _ = platform.architecture()
call_arguments = ['pip', 'install', 'wheel']
if platform_bits == '64bit':
call_arguments.append(
'http://repo.saltstack.com/windows/dependencies/64/pycrypto-2.6.1-cp27-none-win_amd64.whl'
)
else:
call_arguments.append(
'http://repo.saltstack.com/windows/dependencies/32/pycrypto-2.6.1-cp27-none-win32.whl'
)
with indent_log():
call_subprocess(call_arguments)
class DownloadWindowsDlls(Command):
description = 'Download required DLL\'s for windows'
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
if getattr(self.distribution, 'salt_download_windows_dlls', None) is None:
print('This command is not meant to be called on it\'s own')
exit(1)
import platform
from pip.utils.logging import indent_log
platform_bits, _ = platform.architecture()
url = 'https://repo.saltstack.com/windows/dependencies/{bits}/{fname}32.dll'
dest = os.path.join(os.path.dirname(sys.executable), '{fname}32.dll')
with indent_log():
for fname in ('libeay', 'ssleay'):
furl = url.format(bits=platform_bits[:2], fname=fname)
fdest = dest.format(fname=fname)
if not os.path.exists(fdest):
log.info('Downloading {0}32.dll to {1} from {2}'.format(fname, fdest, furl))
try:
import requests
from contextlib import closing
with closing(requests.get(furl, stream=True)) as req:
if req.status_code == 200:
with open(fdest, 'w') as wfh:
for chunk in req.iter_content(chunk_size=4096):
if chunk: # filter out keep-alive new chunks
wfh.write(chunk)
wfh.flush()
else:
log.error(
'Failed to download {0}32.dll to {1} from {2}'.format(
fname, fdest, furl
)
)
except ImportError:
req = urlopen(furl)
if req.getcode() == 200:
with open(fdest, 'w') as wfh:
while True:
for chunk in req.read(4096):
if not chunk:
break
wfh.write(chunk)
wfh.flush()
else:
log.error(
'Failed to download {0}32.dll to {1} from {2}'.format(
fname, fdest, furl
)
)
class Sdist(sdist):
def make_release_tree(self, base_dir, files):
@ -318,7 +562,9 @@ class TestCommand(Command):
self.runtests_opts = None
def finalize_options(self):
pass
'''
Abstract method that is required to be overwritten
'''
def run(self):
from subprocess import Popen
@ -389,24 +635,10 @@ class Build(build):
self.run_command('write_salt_version')
# Write the system paths file
system_paths_file_path = os.path.join(
self.distribution.salt_syspaths_hardcoded_path = os.path.join(
self.build_lib, 'salt', '_syspaths.py'
)
open(system_paths_file_path, 'w').write(
INSTALL_SYSPATHS_TEMPLATE.format(
date=datetime.utcnow(),
root_dir=self.distribution.salt_root_dir,
config_dir=self.distribution.salt_config_dir,
cache_dir=self.distribution.salt_cache_dir,
sock_dir=self.distribution.salt_sock_dir,
srv_root_dir=self.distribution.salt_srv_root_dir,
base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
logs_dir=self.distribution.salt_logs_dir,
pidfile_dir=self.distribution.salt_pidfile_dir,
)
)
self.run_command('generate_salt_syspaths')
class Install(install):
@ -499,6 +731,20 @@ class Install(install):
self.distribution.salt_version_hardcoded_path = os.path.join(
self.build_lib, 'salt', '_version.py'
)
if IS_WINDOWS_PLATFORM:
if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
# Install M2Crypto first
self.distribution.salt_installing_m2crypto_windows = True
self.run_command('install-m2crypto-windows')
self.distribution.salt_installing_m2crypto_windows = None
# Install PyCrypto
self.distribution.salt_installing_pycrypto_windows = True
self.run_command('install-pycrypto-windows')
self.distribution.salt_installing_pycrypto_windows = None
# Download the required DLLs
self.distribution.salt_download_windows_dlls = True
self.run_command('download-windows-dlls')
self.distribution.salt_download_windows_dlls = None
# Run install.run
install.run(self)
@ -597,7 +843,6 @@ class SaltDistribution(distutils.dist.Distribution):
self.salt_logs_dir = None
self.salt_pidfile_dir = None
self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt'
self.salt_version = __version__ # pylint: disable=undefined-variable
self.description = 'Portable, distributed, remote execution and configuration management system'
@ -610,10 +855,19 @@ class SaltDistribution(distutils.dist.Distribution):
'sdist': Sdist,
'install': Install,
'write_salt_version': WriteSaltVersion,
'generate_salt_syspaths': GenerateSaltSyspaths,
'write_salt_ssh_packaging_file': WriteSaltSshPackaingFile})
if not IS_WINDOWS_PLATFORM:
self.cmdclass.update({'sdist': CloudSdist,
'install_lib': InstallLib})
if IS_WINDOWS_PLATFORM:
self.cmdclass.update({'install-pycrypto-windows': InstallPyCryptoWindowsWheel,
'download-windows-dlls': DownloadWindowsDlls})
if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
self.cmdclass.update({'install-m2crypto-windows': InstallM2CryptoWindows})
if WITH_SETUPTOOLS:
self.cmdclass.update({'develop': Develop})
self.license = 'Apache Software License 2.0'
self.packages = self.discover_packages()
@ -731,6 +985,7 @@ class SaltDistribution(distutils.dist.Distribution):
if IS_WINDOWS_PLATFORM:
install_requires.append('WMI')
install_requires.append('pypiwin32 >= 219')
if self.salt_transport == 'zeromq':
install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS)
@ -911,14 +1166,6 @@ class SaltDistribution(distutils.dist.Distribution):
def parse_command_line(self):
args = distutils.dist.Distribution.parse_command_line(self)
# Setup our property functions after class initialization and
# after parsing the command line since most are set to None
for funcname in dir(self):
if not funcname.startswith('_property_'):
continue
property_name = funcname.split('_property_', 1)[-1]
setattr(self, property_name, getattr(self, funcname))
if not self.ssh_packaging and PACKAGED_FOR_SALT_SSH:
self.ssh_packaging = 1
@ -936,6 +1183,16 @@ class SaltDistribution(distutils.dist.Distribution):
)
)
# Setup our property functions after class initialization and
# after parsing the command line since most are set to None
# ATTENTION: This should be the last step before returning the args or
# some of the requirements won't be correctly set
for funcname in dir(self):
if not funcname.startswith('_property_'):
continue
property_name = funcname.split('_property_', 1)[-1]
setattr(self, property_name, getattr(self, funcname))
return args
# <---- Overridden Methods ---------------------------------------------------------------------------------------

View file

@ -18,7 +18,11 @@ from salttesting.mock import (
# Import Salt Libs
from salt.modules import pw_user
from salt.exceptions import CommandExecutionError
import pwd
try:
import pwd
HAS_PWD = True
except ImportError:
HAS_PWD = False
# Globals
@ -27,6 +31,7 @@ pw_user.__salt__ = {}
pw_user.__context__ = {}
@skipIf(not HAS_PWD, 'These tests can only run on systems with the python pwd module')
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PwUserTestCase(TestCase):
'''
@ -49,16 +54,21 @@ class PwUserTestCase(TestCase):
with patch.dict(pw_user.__salt__, {'cmd.run_all': mock}):
self.assertTrue(pw_user.delete('A'), 1)
@patch('salt.modules.pw_user.__context__', MagicMock(return_value='A'))
def test_getent(self):
'''
Test if user.getent already have a value
'''
self.assertTrue(pw_user.getent())
mock_user = 'saltdude'
mock = MagicMock(return_value='A')
with patch.object(pw_user, 'info', mock):
self.assertEqual(pw_user.getent(True)[0], 'A')
class MockData(object):
pw_name = mock_user
with patch('pwd.getpwall', MagicMock(return_value=[MockData()])):
with patch.dict(pw_user.__context__, {'user.getent': mock_user}):
self.assertEqual(pw_user.getent(), mock_user)
with patch.object(pw_user, 'info', MagicMock(return_value=mock_user)):
self.assertEqual(pw_user.getent(True)[0], mock_user)
def test_chuid(self):
'''
@ -291,13 +301,22 @@ class PwUserTestCase(TestCase):
'''
Return a list of groups the named user belongs to
'''
self.assertEqual(pw_user.list_groups('name'), 'A')
mock_group = 'saltgroup'
with patch('salt.utils.get_group_list', MagicMock(return_value=[mock_group])):
self.assertEqual(pw_user.list_groups('name'), [mock_group])
def test_list_users(self):
'''
Return a list of all users
'''
self.assertTrue(pw_user.list_users())
mock_user = 'saltdude'
class MockData(object):
pw_name = mock_user
with patch('pwd.getpwall', MagicMock(return_value=[MockData()])):
self.assertEqual(pw_user.list_users(), [mock_user])
def test_rename(self):
'''

View file

@ -98,7 +98,7 @@ class BotoElbTestCase(TestCase):
self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called)
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
self.assertTrue(boto_elb.__salt__['state.single'].called)
self.assertTrue(
self.assertFalse(
boto_elb.__salt__['boto_elb.get_attributes'].called
)
self.assertTrue(

View file

@ -139,7 +139,8 @@ class CronTestCase(TestCase):
cron.present(
name='foo',
hour='2',
user='root')
user='root',
identifier=None)
self.assertEqual(
get_crontab(),
('# Lines below here are managed by Salt, do not edit\n'
@ -147,7 +148,7 @@ class CronTestCase(TestCase):
'* 2 * * * foo\n'
'# SALT_CRON_IDENTIFIER:2\n'
'* 2 * * * foo\n'
'* 2 * * * foo\n'))
'* 2 * * * foo'))
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@ -196,214 +197,107 @@ class CronTestCase(TestCase):
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
def test_aissue_1072(self):
def test_multiline_comments_are_updated(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
'# I have a multi-line comment SALT_CRON_IDENTIFIER:1\n'
'# First crontab - single line comment SALT_CRON_IDENTIFIER:1\n'
'* 1 * * * foo'
)
cron.present(
name='foo',
hour='1',
comment='1I have a multi-line comment\n2about my script here.\n',
comment='First crontab\nfirst multi-line comment\n',
identifier='1',
user='root')
cron.present(
name='foo',
hour='1',
comment='3I have a multi-line comment\n3about my script here.\n',
comment='First crontab\nsecond multi-line comment\n',
identifier='1',
user='root')
cron.present(
name='foo',
hour='1',
comment='I have a multi-line comment\nabout my script here.\n',
comment='Second crontab\nmulti-line comment\n',
identifier='2',
user='root')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'# 2about my script here. SALT_CRON_IDENTIFIER:1\n'
'# First crontab\n'
'# second multi-line comment SALT_CRON_IDENTIFIER:1\n'
'* 1 * * * foo\n'
'# I have a multi-line comment\n'
'# about my script here. SALT_CRON_IDENTIFIER:2\n'
'# Second crontab\n'
'# multi-line comment SALT_CRON_IDENTIFIER:2\n'
'* 1 * * * foo')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
def test_issue_11935(self):
def test_existing_unmanaged_jobs_are_made_managed(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
'0 2 * * * find /var/www -type f '
'-mtime -7 -print0 | xargs -0 '
'clamscan -i --no-summary 2>/dev/null'
'0 2 * * * foo'
)
cmd = (
'find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null'
)
self.assertEqual(cron._check_cron('root', cmd, hour='2', minute='0'),
'present')
ret = cron.present(cmd, 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
self.assertEqual(cron._check_cron('root', cmd, hour='3', minute='0'),
'update')
ret = cron.present(cmd, 'root', minute='0', hour='3')
self.assertEqual(ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/null'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
ret = cron._check_cron('root', 'foo', hour='2', minute='0')
self.assertEqual(ret, 'present')
ret = cron.present('foo', 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {'root': 'foo'})
self.assertEqual(ret['comment'], 'Cron foo updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'0 3 * * * find /var/www -type f -mtime -7 -print0 |'
' xargs -0 clamscan -i --no-summary 2>/dev/null')
'# SALT_CRON_IDENTIFIER:foo\n'
'0 2 * * * foo')
ret = cron.present('foo', 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {})
self.assertEqual(ret['comment'], 'Cron foo already present')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
def test_issue_11935_with_id(self):
def test_existing_noid_jobs_are_updated_with_identifier(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
'# SALT_CRON_IDENTIFIER:1\n'
'0 2 * * * find /var/www -type f '
'-mtime -7 -print0 | xargs -0 '
'clamscan -i --no-summary 2>/dev/null'
'# SALT_CRON_IDENTIFIER:NO ID SET\n'
'1 * * * * foo'
)
cmd = (
'find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null'
)
self.assertEqual(cron._check_cron(
'root', cmd, hour='2', minute='0', identifier=1), 'present')
ret = cron.present(cmd, 'root', minute='0', hour='2', identifier='1')
self.assertEqual(ret['changes'], {})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
self.assertEqual(cron._check_cron(
'root', cmd, hour='3', minute='0', identifier='1'), 'update')
ret = cron.present(cmd, 'root', minute='0', hour='3', identifier='1')
self.assertEqual(ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/null'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
ret = cron._check_cron('root', 'foo', minute=1)
self.assertEqual(ret, 'present')
ret = cron.present('foo', 'root', minute=1)
self.assertEqual(ret['changes'], {'root': 'foo'})
self.assertEqual(ret['comment'], 'Cron foo updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'# SALT_CRON_IDENTIFIER:1\n'
'0 3 * * * find /var/www -type f -mtime -7 -print0 |'
' xargs -0 clamscan -i --no-summary 2>/dev/null')
'# SALT_CRON_IDENTIFIER:foo\n'
'1 * * * * foo')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
def test_issue_11935_mixed(self):
def test_existing_duplicate_unmanaged_jobs_are_merged_and_given_id(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
'0 2 * * * find /var/www -type f '
'-mtime -7 -print0 | xargs -0 '
'clamscan -i --no-summary 2>/dev/null'
'0 2 * * * foo\n'
'0 2 * * * foo'
)
cmd = (
'find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null'
)
self.assertEqual(cron._check_cron('root', cmd, hour='2', minute='0'),
'present')
ret = cron.present(cmd, 'root', minute='0', hour='2')
ret = cron._check_cron('root', 'foo', hour='2', minute='0')
self.assertEqual(ret, 'present')
ret = cron.present('foo', 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {'root': 'foo'})
self.assertEqual(ret['comment'], 'Cron foo updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'# SALT_CRON_IDENTIFIER:foo\n'
'0 2 * * * foo')
ret = cron.present('foo', 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
self.assertEqual(cron._check_cron('root', cmd, hour='3', minute='0'),
'update')
ret = cron.present(cmd, 'root', minute='0', hour='3')
self.assertEqual(ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/null'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'0 3 * * * find /var/www -type f -mtime -7 -print0 |'
' xargs -0 clamscan -i --no-summary 2>/dev/null')
self.assertEqual(cron._check_cron(
'root', cmd, hour='2', minute='0', identifier='1'), 'update')
ret = cron.present(cmd, 'root', minute='0', hour='2', identifier='1')
self.assertEqual(
ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/null'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
self.assertEqual(cron._check_cron(
'root', cmd, hour='3', minute='0', identifier='1'), 'update')
ret = cron.present(cmd, 'root', minute='0', hour='3', identifier='1')
self.assertEqual(ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/null'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 '
'| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'# SALT_CRON_IDENTIFIER:1\n'
'0 3 * * * find /var/www -type f -mtime -7 -print0 |'
' xargs -0 clamscan -i --no-summary 2>/dev/null')
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
'0 2 * * * find /var/www -type f '
'-mtime -7 -print0 | xargs -0 '
'clamscan -i --no-summary 2>/dev/null'
)
self.assertEqual(cron._check_cron(
'root', cmd + "a", hour='2', minute='0', identifier='1'), 'absent')
ret = cron.present(
cmd + "a", 'root', minute='0', hour='2', identifier='1')
self.assertEqual(
ret['changes'],
{'root': 'find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/nulla'})
self.assertEqual(
ret['comment'],
'Cron find /var/www -type f -mtime -7 -print0 | '
'xargs -0 clamscan -i --no-summary 2>/dev/nulla added '
'to root\'s crontab')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
'0 2 * * *'
' find /var/www -type f -mtime -7 -print0'
' | xargs -0 clamscan -i --no-summary 2>/dev/null\n'
'# SALT_CRON_IDENTIFIER:1\n'
'0 2 * * *'
' find /var/www -type f -mtime -7 -print0'
' | xargs -0 clamscan -i --no-summary 2>/dev/nulla')
self.assertEqual(ret['comment'], 'Cron foo already present')
if __name__ == '__main__':
from integration import run_tests

View file

@ -22,7 +22,7 @@ ensure_in_syspath('../../')
from salt.states import openstack_config
openstack_config.__salt__ = {}
openstack_config.__opts__ = {}
openstack_config.__opts__ = {'test': False}
@skipIf(NO_MOCK, NO_MOCK_REASON)

View file

@ -36,28 +36,43 @@ class RegTestCase(TestCase):
'''
Test to set a registry entry.
'''
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version'
value = '0.15.3'
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
vname = 'version'
vdata = '0.15.3'
ret = {'name': name,
'changes': {},
'result': True,
'comment': '{0} is already configured'.format(name)}
'comment': '{0} in {1} is already configured'.format(vname, name)}
mock = MagicMock(side_effect=[{'vdata': value}, {'vdata': 'a'}, {'vdata': 'a'}])
mock_read = MagicMock(side_effect=[{'vdata': vdata, 'success': True},
{'vdata': 'a', 'success': True},
{'vdata': 'a', 'success': True}])
mock_t = MagicMock(return_value=True)
with patch.dict(reg.__salt__, {'reg.read_value': mock,
with patch.dict(reg.__salt__, {'reg.read_value': mock_read,
'reg.set_value': mock_t}):
self.assertDictEqual(reg.present(name, value), ret)
self.assertDictEqual(reg.present(name,
vname=vname,
vdata=vdata), ret)
with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None,
'changes': {'reg': 'configured to 0.15.3'}})
self.assertDictEqual(reg.present(name, value), ret)
'changes': {'reg': {'Will add': {'Key': name,
'Entry': vname,
'Value': vdata}}}})
self.assertDictEqual(reg.present(name,
vname=vname,
vdata=vdata), ret)
with patch.dict(reg.__opts__, {'test': False}):
ret.update({'result': True})
self.assertDictEqual(reg.present(name, value), ret)
ret.update({'comment': 'Added {0} to {0}'.format(name),
'result': True,
'changes': {'reg': {'Added': {'Key': name,
'Entry': vname,
'Value': vdata}}}})
self.assertDictEqual(reg.present(name,
vname=vname,
vdata=vdata), ret)
# 'absent' function tests: 1
@ -65,27 +80,35 @@ class RegTestCase(TestCase):
'''
Test to remove a registry entry.
'''
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version'
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
vname = 'version'
ret = {'name': name,
'changes': {},
'result': True,
'comment': '{0} is already absent'.format(name)}
mock = MagicMock(side_effect=[{'success': False}, {'success': True}, {'success': True}])
mock_read = MagicMock(side_effect=[{'success': False},
{'success': False},
{'success': True},
{'success': True}])
mock_t = MagicMock(return_value=True)
with patch.dict(reg.__salt__, {'reg.read_value': mock,
with patch.dict(reg.__salt__, {'reg.read_value': mock_read,
'reg.delete_value': mock_t}):
self.assertDictEqual(reg.absent(name), ret)
self.assertDictEqual(reg.absent(name, vname), ret)
with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None,
'changes': {'reg': 'Removed {0}'.format(name)}})
self.assertDictEqual(reg.absent(name), ret)
'changes': {'reg': {'Will remove': {'Entry': vname,
'Key': name}}}})
self.assertDictEqual(reg.absent(name, vname), ret)
with patch.dict(reg.__opts__, {'test': False}):
ret.update({'result': True})
self.assertDictEqual(reg.absent(name), ret)
ret.update({'result': True,
'changes': {'reg': {'Removed': {'Entry': vname,
'Key': name}}},
'comment': 'Removed {0} from {0}'.format(name)})
self.assertDictEqual(reg.absent(name, vname), ret)
if __name__ == '__main__':