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. </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 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 Download the Prerequisite zip file for your CPU architecture from the
SaltStack download site: SaltStack download site:
* `Salt32.zip <http://docs.saltstack.com/downloads/windows-deps/Salt32.zip/>`_ * `Salt32.zip <http://repo.saltstack.com/windows/dependencies/Salt32.zip/>`_
* `Salt64.zip <http://docs.saltstack.com/downloads/windows-deps/Salt64.zip/>`_ * `Salt64.zip <http://repo.saltstack.com/windows/dependencies/Salt64.zip/>`_
These files contain all sofware required to build and develop salt. Unzip the These files contain all sofware required to build and develop salt. Unzip the
contents of the file to ``C:\Salt-Dev\temp``. 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 RETVAL=1
else else
daemon --pidfile=$PID_FILE --check $SERVICE $SALTAPI $CONFIG_ARGS 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
fi fi
RETVAL=$? RETVAL=$?
@ -97,6 +100,10 @@ stop() {
fi fi
else else
killproc $PROCESS killproc $PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
return $RETVAL
fi fi
RETVAL=$? RETVAL=$?
echo echo

View file

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

View file

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

View file

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

View file

@ -954,10 +954,10 @@ ARGS = {9}\n'''.format(self.minion_config,
pass pass
# Execute shim # 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 # 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 return ret

View file

@ -1654,7 +1654,6 @@ def request_instance(vm_=None, call=None):
} }
try: try:
rd_data = aws.query(rd_params, rd_data = aws.query(rd_params,
return_root=True,
location=get_location(), location=get_location(),
provider=get_provider(), provider=get_provider(),
opts=__opts__, opts=__opts__,
@ -2363,7 +2362,7 @@ def create(vm_=None, call=None):
'volumes': volumes, 'volumes': volumes,
'zone': ret['placement']['availabilityZone'], 'zone': ret['placement']['availabilityZone'],
'instance_id': ret['instanceId'], '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' call='action'
) )
@ -3780,38 +3779,52 @@ def delete_keypair(kwargs=None, call=None):
def create_snapshot(kwargs=None, call=None, wait_to_finish=False): 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': if call != 'function':
log.error( raise SaltCloudSystemExit(
'The create_snapshot function must be called with -f ' 'The create_snapshot function must be called with -f '
'or --function.' 'or --function.'
) )
return False
if 'volume_id' not in kwargs: if kwargs is None:
log.error('A volume_id must be specified to create a snapshot.') kwargs = {}
return False
if 'description' not in kwargs: volume_id = kwargs.get('volume_id', None)
kwargs['description'] = '' 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 = {'Action': 'CreateSnapshot',
params['VolumeId'] = kwargs['volume_id'] 'VolumeId': volume_id,
'Description': description}
if 'description' in kwargs:
params['Description'] = kwargs['description']
log.debug(params) log.debug(params)
data = aws.query(params, data = aws.query(params,
return_url=True, return_url=True,
return_root=True,
location=get_location(), location=get_location(),
provider=get_provider(), provider=get_provider(),
opts=__opts__, opts=__opts__,
sigver='4') sigver='4')[0]
r_data = {} r_data = {}
for d in data: for d in data:
@ -3827,7 +3840,7 @@ def create_snapshot(kwargs=None, call=None, wait_to_finish=False):
argument_being_watched='status', argument_being_watched='status',
required_argument_response='completed') required_argument_response='completed')
return data return r_data
def delete_snapshot(kwargs=None, call=None): 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') autosign_dir = os.path.join(self.opts['pki_dir'], 'minions_autosign')
# cleanup expired files # cleanup expired files
expire_minutes = self.opts.get('autosign_expire_minutes', 10) expire_minutes = self.opts.get('autosign_timeout', 120)
if expire_minutes > 0: if expire_minutes > 0:
min_time = time.time() - (60 * int(expire_minutes)) min_time = time.time() - (60 * int(expire_minutes))
for root, dirs, filenames in os.walk(autosign_dir): for root, dirs, filenames in os.walk(autosign_dir):

View file

@ -1558,6 +1558,7 @@ class Minion(MinionBase):
del self.pub_channel del self.pub_channel
self._connect_master_future = self.connect_master() self._connect_master_future = self.connect_master()
self.block_until_connected() # TODO: remove self.block_until_connected() # TODO: remove
self.functions, self.returners, self.function_errors = self._load_modules()
self._fire_master_minion_start() self._fire_master_minion_start()
log.info('Minion is ready to receive requests!') 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): if isinstance(ok_actions, string_types):
ok_actions = ok_actions.split(",") ok_actions = ok_actions.split(",")
# convert action names into ARN's # convert provided action names into ARN's
alarm_actions = convert_to_arn(alarm_actions, region, key, keyid, profile) if alarm_actions:
insufficient_data_actions = convert_to_arn( alarm_actions = convert_to_arn(alarm_actions,
insufficient_data_actions, region, key, keyid, profile region=region,
) key=key,
ok_actions = convert_to_arn(ok_actions, region, key, keyid, profile) 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) 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) ret['special'].append(dat)
elif line.startswith('#'): elif line.startswith('#'):
# It's a comment! Catch it! # It's a comment! Catch it!
comment = line.lstrip('# ') comment_line = line.lstrip('# ')
# load the identifier if any # load the identifier if any
if SALT_CRON_IDENTIFIER in comment: if SALT_CRON_IDENTIFIER in comment_line:
parts = comment.split(SALT_CRON_IDENTIFIER) parts = comment_line.split(SALT_CRON_IDENTIFIER)
comment = parts[0].rstrip() comment_line = parts[0].rstrip()
# skip leading : # skip leading :
if len(parts[1]) > 1: if len(parts[1]) > 1:
identifier = parts[1][1:] identifier = parts[1][1:]
if comment is None:
comment = comment_line
else:
comment += '\n' + comment_line
elif len(line.split()) > 5: elif len(line.split()) > 5:
# Appears to be a standard cron line # Appears to be a standard cron line
comps = line.split() comps = line.split()

View file

@ -26,6 +26,14 @@ from salt.exceptions import SaltInvocationError
# Import 3rd-party libs # Import 3rd-party libs
import salt.ext.six as six 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 # Set up logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -826,15 +834,15 @@ def trust_key(keyid=None,
if trust_level not in _VALID_TRUST_LEVELS: if trust_level not in _VALID_TRUST_LEVELS:
return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS)) return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS))
cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(fingerprint, cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(_cmd_quote(fingerprint),
NUM_TRUST_DICT[trust_level], _cmd_quote(NUM_TRUST_DICT[trust_level]),
_check_gpg()) _cmd_quote(_check_gpg()))
_user = user _user = user
if user == 'salt': if user == 'salt':
homeDir = os.path.join(salt.syspaths.CONFIG_DIR, 'gpgkeys') homeDir = os.path.join(salt.syspaths.CONFIG_DIR, 'gpgkeys')
cmd = '{0} --homedir {1}'.format(cmd, homeDir) cmd = '{0} --homedir {1}'.format(cmd, homeDir)
_user = 'root' _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: if not res['retcode'] == 0:
ret['res'] = False ret['res'] = False

View file

@ -51,6 +51,7 @@ Module for handling openstack keystone calls.
# Import Python libs # Import Python libs
from __future__ import absolute_import from __future__ import absolute_import
import logging
# Import Salt Libs # Import Salt Libs
import salt.ext.six as six import salt.ext.six as six
@ -66,6 +67,8 @@ try:
except ImportError: except ImportError:
pass pass
log = logging.getLogger(__name__)
def __virtual__(): def __virtual__():
''' '''
@ -707,7 +710,13 @@ def user_get(user_id=None, name=None, profile=None, **connection_args):
break break
if not user_id: if not user_id:
return {'Error': 'Unable to resolve 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, ret[user.name] = {'id': user.id,
'name': user.name, 'name': user.name,
'email': user.email, 'email': user.email,

View file

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

View file

@ -130,3 +130,23 @@ def chgid(name, gid):
if post_gid != pre_gid: if post_gid != pre_gid:
return post_gid == gid return post_gid == gid
return False 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 import logging
try: try:
import pwd import pwd
HAS_PWD = True
except ImportError: except ImportError:
pass HAS_PWD = False
# Import 3rd party libs # Import 3rd party libs
import salt.ext.six as six import salt.ext.six as six
@ -29,7 +30,9 @@ def __virtual__():
''' '''
Set the user module if the kernel is FreeBSD 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): def _get_gecos(name):

View file

@ -1,29 +1,36 @@
# -*- coding: utf-8 -*- # -*- 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 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. 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. Hives are the main sections of the registry and all begin with the word HKEY.
- HKEY_LOCAL_MACHINE - HKEY_LOCAL_MACHINE
- HKEY_CURRENT_USER - HKEY_CURRENT_USER
- HKEY_USER - HKEY_USER
### Keys ----
Keys
----
Keys are the folders in the registry. Keys can have many nested subkeys. 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) 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) Values or Entries
value corresponds to the Key, the rest are their own value pairs. -----------------
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 :depends: - winreg Python module
''' '''
# TODO: Figure out the exceptions _winreg can raise and properly catch # TODO: Figure out the exceptions _winreg can raise and properly catch them
# them instead of a bare except that catches any exception at all
# Import python libs # Import python libs
from __future__ import absolute_import from __future__ import absolute_import
@ -142,44 +149,35 @@ def read_key(hkey, path, key=None):
key=path, key=path,
vname=key) vname=key)
registry = Registry() return read_value(hive=hkey, key=path)
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
def read_value(hive, key, vname=None): def read_value(hive, key, vname=None):
r''' 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 :param str hive:
The name of the hive. Can be one of the following The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM - HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU - HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU - HKEY_USER or HKU
:param key: string :param str key:
The key (looks like a path) to the value name. The key (looks like a path) to the value name.
:param vname: string :param str vname:
The value name. These are the individual name/data pairs under the key. If The value name. These are the individual name/data pairs under the key.
not passed, the key (Default) value will be returned If not passed, the key (Default) value will be returned
:return: dict :return:
A dictionary containing the passed settings as well as the value_data if A dictionary containing the passed settings as well as the value_data if
successful. If unsuccessful, sets success to False 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: CLI Example:
@ -205,9 +203,9 @@ def read_value(hive, key, vname=None):
try: try:
handle = _winreg.OpenKey(hive, key) handle = _winreg.OpenKey(hive, key)
value, vtype = _winreg.QueryValueEx(handle, vname) vdata, vtype = _winreg.QueryValueEx(handle, vname)
if value: if vdata:
ret['vdata'] = value ret['vdata'] = vdata
ret['vtype'] = registry.vtype_reverse[vtype] ret['vtype'] = registry.vtype_reverse[vtype]
else: else:
ret['comment'] = 'Empty Value' ret['comment'] = 'Empty Value'
@ -257,53 +255,45 @@ def set_key(hkey, path, value, key=None, vtype='REG_DWORD', reflection=True):
vdata=value, vdata=value,
vtype=vtype) vtype=vtype)
registry = Registry() return set_value(hive=hkey, key=path, vdata=value, vtype=vtype)
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
def set_value(hive, key, vname=None, vdata=None, vtype='REG_SZ', reflection=True): 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 :param str hive:
The name of the hive. Can be one of the following The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM - HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU - HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU - HKEY_USER or HKU
:param key: string :param str key:
The key (looks like a path) to the value name. The key (looks like a path) to the value name.
:param vname: string :param str vname:
The value name. These are the individual name/data pairs under the key. If The value name. These are the individual name/data pairs under the key.
not passed, the key (Default) value will be set. If not passed, the key (Default) value will be set.
:param vdata: string :param str vdata:
The value data to be set. The value data to be set.
:param vtype: string :param str vtype:
The value type. Can be one of the following: The value type. Can be one of the following:
- REG_BINARY - REG_BINARY
- REG_DWORD - REG_DWORD
- REG_EXPAND_SZ - REG_EXPAND_SZ
- REG_MULTI_SZ - REG_MULTI_SZ
- REG_SZ - REG_SZ
:param reflection: boolean :param bool reflection:
A boolean value indicating that the value should also be set in the 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 Wow6432Node portion of the registry. Only applies to 64 bit Windows.
setting is ignored for 32 bit Windows. This setting is ignored for 32 bit Windows.
:return: boolean :return:
Returns True if successful, False if not Returns True if successful, False if not
:rtype: bool
CLI Example: 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.SetValueEx(handle, vname, 0, vtype, vdata)
_winreg.CloseKey(handle) _winreg.CloseKey(handle)
return True return True
except WindowsError as exc: # pylint: disable=E0602 except (WindowsError, ValueError) as exc: # pylint: disable=E0602
log.error(exc) log.error(exc)
return False 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' salt '*' reg.create_key HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' '0.97'
''' '''
if key: # This if statement will be removed in Boron 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 ' 'value. This functionality will be '
'removed in Salt Boron') 'removed in Salt Boron')
return set_value(hive=hkey, return set_value(hive=hkey,
@ -362,21 +352,10 @@ def create_key(hkey, path, key=None, value=None, reflection=True):
vdata=value, vdata=value,
vtype='REG_SZ') vtype='REG_SZ')
registry = Registry() return set_value(hive=hkey, key=path)
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
def delete_key(hkey, path, key=None, reflection=True): def delete_key(hkey, path, key=None, reflection=True, force=False):
''' '''
*** Incorrect Usage *** *** Incorrect Usage ***
The name of this function is misleading and will be changed to reflect 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 Delete a registry key
Note: This cannot delete a key with subkeys
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
salt '*' reg.delete_key HKEY_CURRENT_USER 'SOFTWARE\\Salt' 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 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',
'value. This functionality will be ' 'Variable names will be changed to match Windows '
'removed in Salt Boron') 'Registry terminology. These changes will be '
'made in Boron')
return delete_value(hive=hkey, return delete_value(hive=hkey,
key=path, key=path,
vname=key, vname=key,
reflection=reflection) reflection=reflection)
if force:
return delete_key_recursive(hkey, path)
registry = Registry() registry = Registry()
hive = registry.hkeys[hkey] hive = registry.hkeys[hkey]
key = path key = path
try: try:
# Can't use delete_value to delete a key
_winreg.DeleteKey(hive, key) _winreg.DeleteKey(hive, key)
return True return True
except WindowsError as exc: # pylint: disable=E0602 except WindowsError as exc: # pylint: disable=E0602
@ -426,30 +438,102 @@ def delete_key(hkey, path, key=None, reflection=True):
return False 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): 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 :param str hive:
The name of the hive. Can be one of the following The name of the hive. Can be one of the following
- HKEY_LOCAL_MACHINE or HKLM - HKEY_LOCAL_MACHINE or HKLM
- HKEY_CURRENT_USER or HKCU - HKEY_CURRENT_USER or HKCU
- HKEY_USER or HKU - HKEY_USER or HKU
:param key: string :param str key:
The key (looks like a path) to the value name. The key (looks like a path) to the value name.
:param vname: string :param str vname:
The value name. These are the individual name/data pairs under the key. If The value name. These are the individual name/data pairs under the key.
not passed, the key (Default) value will be deleted. If not passed, the key (Default) value will be deleted.
:param reflection: boolean :param bool reflection:
A boolean value indicating that the value should also be set in the 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 Wow6432Node portion of the registry. Only applies to 64 bit Windows.
setting is ignored for 32 bit Windows. This setting is ignored for 32 bit Windows.
:return: boolean :return:
Returns True if successful, False if not Returns True if successful, False if not
:rtype: bool
CLI Example: CLI Example:

View file

@ -97,6 +97,11 @@ def list_(show_all=False, where=None, return_yaml=True):
del schedule[job] del schedule[job]
continue 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]): for item in pycopy.copy(schedule[job]):
if item not in SCHEDULE_CONF: if item not in SCHEDULE_CONF:
del schedule[job][item] del schedule[job][item]

View file

@ -9,8 +9,9 @@ import re
try: try:
import pwd import pwd
HAS_PWD = True
except ImportError: except ImportError:
pass HAS_PWD = False
import logging import logging
import copy import copy
@ -32,10 +33,9 @@ __virtualname__ = 'user'
def __virtual__(): def __virtual__():
''' '''
Set the user module if the kernel is Linux, OpenBSD or NetBSD 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 __virtualname__
return False return False

View file

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

View file

@ -125,8 +125,7 @@ from salt.ext.six import string_types
import salt.utils import salt.utils
from salt.modules.cron import ( from salt.modules.cron import (
_needs_change, _needs_change,
_cron_matched, _cron_matched
SALT_CRON_NO_IDENTIFIER
) )
@ -217,7 +216,7 @@ def present(name,
month='*', month='*',
dayweek='*', dayweek='*',
comment=None, comment=None,
identifier=None): identifier=False):
''' '''
Verifies that the specified cron job is present for the specified user. 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 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 edits. This defaults to the state id
''' '''
name = ' '.join(name.strip().split()) name = ' '.join(name.strip().split())
if not identifier: if identifier is False:
identifier = SALT_CRON_NO_IDENTIFIER identifier = name
ret = {'changes': {}, ret = {'changes': {},
'comment': '', 'comment': '',
'name': name, 'name': name,
@ -313,7 +312,7 @@ def present(name,
def absent(name, def absent(name,
user='root', user='root',
identifier=None, identifier=False,
**kwargs): **kwargs):
''' '''
Verifies that the specified cron job is absent for the specified user; only 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. ### of unsupported arguments will result in a traceback.
name = ' '.join(name.strip().split()) name = ' '.join(name.strip().split())
if not identifier: if identifier is False:
identifier = SALT_CRON_NO_IDENTIFIER identifier = name
ret = {'name': name, ret = {'name': name,
'result': True, 'result': True,
'changes': {}, 'changes': {},

View file

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

View file

@ -8,10 +8,12 @@ Manage OpenStack configuration file settings.
:platform: linux :platform: linux
''' '''
# Import Python Libs
from __future__ import absolute_import from __future__ import absolute_import
# Import salt libs # Import Salt Libs
import salt.exceptions from salt.exceptions import CommandExecutionError
def __virtual__(): def __virtual__():
@ -48,18 +50,30 @@ def present(name, filename, section, value, parameter=None):
if parameter is None: if parameter is None:
parameter = name parameter = name
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
try: try:
old_value = __salt__['openstack_config.get'](filename=filename, old_value = __salt__['openstack_config.get'](filename=filename,
section=section, section=section,
parameter=parameter) parameter=parameter)
if old_value == value: if old_value == value:
return {'name': name, ret['result'] = True
'changes': {}, ret['comment'] = 'The value is already set to the correct value'
'result': True, return ret
'comment': 'The value is already set to the correct value'}
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:'): if not str(e).lower().startswith('parameter not found:'):
raise raise
@ -68,10 +82,11 @@ def present(name, filename, section, value, parameter=None):
parameter=parameter, parameter=parameter,
value=value) value=value)
return {'name': name, ret['changes'] = {'Value': 'Updated'}
'changes': {'Value': 'Updated'}, ret['result'] = True
'result': True, ret['comment'] = 'The value has been updated'
'comment': 'The value has been updated'}
return ret
def absent(name, filename, section, parameter=None): def absent(name, filename, section, parameter=None):
@ -92,23 +107,35 @@ def absent(name, filename, section, parameter=None):
if parameter is None: if parameter is None:
parameter = name parameter = name
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
try: try:
old_value = __salt__['openstack_config.get'](filename=filename, old_value = __salt__['openstack_config.get'](filename=filename,
section=section, section=section,
parameter=parameter) parameter=parameter)
except salt.exceptions.CommandExecutionError as e: except CommandExecutionError as e:
if str(e).lower().startswith('parameter not found:'): if str(e).lower().startswith('parameter not found:'):
return {'name': name, ret['result'] = True
'changes': {}, ret['comment'] = 'The value is already absent'
'result': True, return ret
'comment': 'The value is already absent'}
raise 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, __salt__['openstack_config.delete'](filename=filename,
section=section, section=section,
parameter=parameter) parameter=parameter)
return {'name': name, ret['changes'] = {'Value': 'Deleted'}
'changes': {'Value': 'Deleted'}, ret['result'] = True
'result': True, ret['comment'] = 'The value has been deleted'
'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__ 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 __virtualname__
return False return False
@ -100,6 +100,16 @@ def _check_pkg_version_format(pkg):
ret = {'result': False, 'comment': None, ret = {'result': False, 'comment': None,
'prefix': None, 'version_spec': 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 from_vcs = False
try: try:
# Get the requirement object from the pip library # Get the requirement object from the pip library

View file

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

View file

@ -1,11 +1,68 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' r'''
Manage the registry on Windows ===========================
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 from __future__ import absolute_import
# Import python libs
import logging import logging
# Import salt libs
import salt.utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -23,7 +80,7 @@ def _parse_key_value(key):
splt = key.split("\\") splt = key.split("\\")
hive = splt.pop(0) hive = splt.pop(0)
vname = splt.pop(-1) vname = splt.pop(-1)
key = r'\\'.join(splt) key = '\\'.join(splt)
return hive, key, vname return hive, key, vname
@ -33,71 +90,149 @@ def _parse_key(key):
''' '''
splt = key.split("\\") splt = key.split("\\")
hive = splt.pop(0) hive = splt.pop(0)
key = r'\\'.join(splt) key = '\\'.join(splt)
return hive, key 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. :param str name:
``reflection`` has no effect on a 32-bit OS. 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 ``HKEY_LOCAL_MACHINE\\SOFTWARE\\Salt``
the key in:
``HKEY_CURRENT_USER\\SOFTWARE\\Wow6432Node\\Salt\\version`` 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: Example:
.. code-block:: yaml .. code-block:: yaml
HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version: HKEY_CURRENT_USER\\SOFTWARE\\Salt:
reg.present: reg.present:
- value: 0.15.3 - vdata: 0.15.3
- vtype: REG_SZ
- reflection: False - 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: In the above example the path is interpreted as follows:
- ``HKEY_CURRENT_USER`` is the hive - ``HKEY_CURRENT_USER`` is the hive
- ``SOFTWARE\\Salt`` is the key - ``SOFTWARE\\Salt`` is the key
- ``version`` is the value name - ``vname`` is the value name ('version') that will be created under the key
So ``version`` will be created in the ``SOFTWARE\\Salt`` key in the - ``vdata`` is the data that will be assigned to 'version'
``HKEY_CURRENT_USER`` hive and given the ``REG_SZ`` value of ``0.15.3``.
''' '''
ret = {'name': name, ret = {'name': name,
'result': True, 'result': True,
'changes': {}, 'changes': {},
'comment': ''} '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 # Determine what to do
if value == __salt__['reg.read_value'](hive, key, vname)['vdata']: reg_current = __salt__['reg.read_value'](hive, key, vname)
ret['comment'] = '{0} is already configured'.format(name)
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 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 # Check for test option
if __opts__['test']: if __opts__['test']:
ret['result'] = None ret['result'] = None
ret['changes'] = {'reg': {'Will add': add_change}}
return ret return ret
# Configure the value # 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) reflection)
if not ret: if not ret['result']:
ret['changes'] = {} 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 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: Example:
@ -118,14 +253,89 @@ def absent(name):
'changes': {}, 'changes': {},
'comment': ''} 'comment': ''}
hive, key, vname = _parse_key_value(name) hive, key = _parse_key(name)
# Determine what to do # Determine what to do
if not __salt__['reg.read_value'](hive, key, vname)['success']: 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) ret['comment'] = '{0} is already absent'.format(name)
return ret 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 # Check for test option
if __opts__['test']: if __opts__['test']:
@ -133,9 +343,10 @@ def absent(name):
return ret return ret
# Delete the value # Delete the value
ret['result'] = __salt__['reg.delete_value'](hive, key, vname) __salt__['reg.delete_key'](hive, key, force=force)
if not ret['result']: if __salt__['reg.read_value'](hive, key)['success']:
ret['result'] = False
ret['changes'] = {} ret['changes'] = {}
ret['comment'] = 'failed to remove registry key {0}'.format(name) ret['comment'] = 'Failed to remove registry key {0}'.format(name)
return ret return ret

View file

@ -1,11 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
Manage Windows Package Repository Manage Windows Package Repository
.. note::
This state only loads on minions that have the ``roles: salt-master`` grain
set.
''' '''
from __future__ import absolute_import from __future__ import absolute_import
@ -21,15 +16,7 @@ import salt.config
def __virtual__(): def __virtual__():
''' return 'winrepo'
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
def genrepo(name, force=False, allow_empty=False): 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 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 region = None
try: try:

View file

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

327
setup.py
View file

@ -5,7 +5,7 @@ The setup script for salt
''' '''
from __future__ import absolute_import from __future__ import absolute_import
# pylint: disable=file-perms
# pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903 # pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903
# For Python 2.5. A no-op on 2.6 and above. # For Python 2.5. A no-op on 2.6 and above.
@ -18,7 +18,7 @@ import time
try: try:
from urllib2 import urlopen from urllib2 import urlopen
except ImportError: except ImportError:
from urllib.request import urlopen from urllib.request import urlopen # pylint: disable=no-name-in-module
from datetime import datetime from datetime import datetime
# pylint: disable=E0611 # pylint: disable=E0611
import distutils.dist import distutils.dist
@ -71,6 +71,7 @@ WITH_SETUPTOOLS = False
if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules: if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
try: try:
from setuptools import setup from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install from setuptools.command.install import install
from setuptools.command.sdist import sdist from setuptools.command.sdist import sdist
from setuptools.command.egg_info import egg_info 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 = 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_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_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_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') 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() line = line.strip()
if not line or line.startswith(('#', '-r')): if not line or line.startswith(('#', '-r')):
continue continue
if IS_WINDOWS_PLATFORM and 'libcloud' in line: if IS_WINDOWS_PLATFORM:
continue 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) parsed_requirements.append(line)
return parsed_requirements return parsed_requirements
# <---- Helper Functions --------------------------------------------------------------------------------------------- # <---- Helper Functions ---------------------------------------------------------------------------------------------
@ -139,10 +149,14 @@ class WriteSaltVersion(Command):
user_options = [] user_options = []
def initialize_options(self): def initialize_options(self):
pass '''
Abstract method that is required to be overwritten
'''
def finalize_options(self): def finalize_options(self):
pass '''
Abstract method that is required to be overwritten
'''
def run(self): def run(self):
if not os.path.exists(SALT_VERSION_HARDCODED): if not os.path.exists(SALT_VERSION_HARDCODED):
@ -161,10 +175,9 @@ class WriteSaltVersion(Command):
# pylint: enable=E0602 # pylint: enable=E0602
class WriteSaltSshPackaingFile(Command): class GenerateSaltSyspaths(Command):
description = 'Write salt\'s ssh packaging file' description = 'Generate salt\'s hardcoded syspaths file'
user_options = []
def initialize_options(self): def initialize_options(self):
pass pass
@ -172,6 +185,45 @@ class WriteSaltSshPackaingFile(Command):
def finalize_options(self): def finalize_options(self):
pass 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): def run(self):
if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE): if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
# Write the salt-ssh packaging file # Write the salt-ssh packaging file
@ -184,6 +236,198 @@ class WriteSaltSshPackaingFile(Command):
# pylint: enable=E0602 # 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): class Sdist(sdist):
def make_release_tree(self, base_dir, files): def make_release_tree(self, base_dir, files):
@ -318,7 +562,9 @@ class TestCommand(Command):
self.runtests_opts = None self.runtests_opts = None
def finalize_options(self): def finalize_options(self):
pass '''
Abstract method that is required to be overwritten
'''
def run(self): def run(self):
from subprocess import Popen from subprocess import Popen
@ -389,24 +635,10 @@ class Build(build):
self.run_command('write_salt_version') self.run_command('write_salt_version')
# Write the system paths file # 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' self.build_lib, 'salt', '_syspaths.py'
) )
open(system_paths_file_path, 'w').write( self.run_command('generate_salt_syspaths')
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 Install(install): class Install(install):
@ -499,6 +731,20 @@ class Install(install):
self.distribution.salt_version_hardcoded_path = os.path.join( self.distribution.salt_version_hardcoded_path = os.path.join(
self.build_lib, 'salt', '_version.py' 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 # Run install.run
install.run(self) install.run(self)
@ -597,7 +843,6 @@ class SaltDistribution(distutils.dist.Distribution):
self.salt_logs_dir = None self.salt_logs_dir = None
self.salt_pidfile_dir = None self.salt_pidfile_dir = None
self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt' self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt'
self.salt_version = __version__ # pylint: disable=undefined-variable self.salt_version = __version__ # pylint: disable=undefined-variable
self.description = 'Portable, distributed, remote execution and configuration management system' self.description = 'Portable, distributed, remote execution and configuration management system'
@ -610,10 +855,19 @@ class SaltDistribution(distutils.dist.Distribution):
'sdist': Sdist, 'sdist': Sdist,
'install': Install, 'install': Install,
'write_salt_version': WriteSaltVersion, 'write_salt_version': WriteSaltVersion,
'generate_salt_syspaths': GenerateSaltSyspaths,
'write_salt_ssh_packaging_file': WriteSaltSshPackaingFile}) 'write_salt_ssh_packaging_file': WriteSaltSshPackaingFile})
if not IS_WINDOWS_PLATFORM: if not IS_WINDOWS_PLATFORM:
self.cmdclass.update({'sdist': CloudSdist, self.cmdclass.update({'sdist': CloudSdist,
'install_lib': InstallLib}) '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.license = 'Apache Software License 2.0'
self.packages = self.discover_packages() self.packages = self.discover_packages()
@ -731,6 +985,7 @@ class SaltDistribution(distutils.dist.Distribution):
if IS_WINDOWS_PLATFORM: if IS_WINDOWS_PLATFORM:
install_requires.append('WMI') install_requires.append('WMI')
install_requires.append('pypiwin32 >= 219')
if self.salt_transport == 'zeromq': if self.salt_transport == 'zeromq':
install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS) install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS)
@ -911,14 +1166,6 @@ class SaltDistribution(distutils.dist.Distribution):
def parse_command_line(self): def parse_command_line(self):
args = distutils.dist.Distribution.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: if not self.ssh_packaging and PACKAGED_FOR_SALT_SSH:
self.ssh_packaging = 1 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 return args
# <---- Overridden Methods --------------------------------------------------------------------------------------- # <---- Overridden Methods ---------------------------------------------------------------------------------------

View file

@ -18,7 +18,11 @@ from salttesting.mock import (
# Import Salt Libs # Import Salt Libs
from salt.modules import pw_user from salt.modules import pw_user
from salt.exceptions import CommandExecutionError from salt.exceptions import CommandExecutionError
import pwd try:
import pwd
HAS_PWD = True
except ImportError:
HAS_PWD = False
# Globals # Globals
@ -27,6 +31,7 @@ pw_user.__salt__ = {}
pw_user.__context__ = {} 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) @skipIf(NO_MOCK, NO_MOCK_REASON)
class PwUserTestCase(TestCase): class PwUserTestCase(TestCase):
''' '''
@ -49,16 +54,21 @@ class PwUserTestCase(TestCase):
with patch.dict(pw_user.__salt__, {'cmd.run_all': mock}): with patch.dict(pw_user.__salt__, {'cmd.run_all': mock}):
self.assertTrue(pw_user.delete('A'), 1) self.assertTrue(pw_user.delete('A'), 1)
@patch('salt.modules.pw_user.__context__', MagicMock(return_value='A'))
def test_getent(self): def test_getent(self):
''' '''
Test if user.getent already have a value Test if user.getent already have a value
''' '''
self.assertTrue(pw_user.getent()) mock_user = 'saltdude'
mock = MagicMock(return_value='A') class MockData(object):
with patch.object(pw_user, 'info', mock): pw_name = mock_user
self.assertEqual(pw_user.getent(True)[0], 'A')
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): def test_chuid(self):
''' '''
@ -291,13 +301,22 @@ class PwUserTestCase(TestCase):
''' '''
Return a list of groups the named user belongs to 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): def test_list_users(self):
''' '''
Return a list of all users 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): 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.exists'].called)
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called) self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
self.assertTrue(boto_elb.__salt__['state.single'].called) self.assertTrue(boto_elb.__salt__['state.single'].called)
self.assertTrue( self.assertFalse(
boto_elb.__salt__['boto_elb.get_attributes'].called boto_elb.__salt__['boto_elb.get_attributes'].called
) )
self.assertTrue( self.assertTrue(

View file

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

View file

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

View file

@ -36,28 +36,43 @@ class RegTestCase(TestCase):
''' '''
Test to set a registry entry. Test to set a registry entry.
''' '''
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version' name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
value = '0.15.3' vname = 'version'
vdata = '0.15.3'
ret = {'name': name, ret = {'name': name,
'changes': {}, 'changes': {},
'result': True, '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) 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}): '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}): with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None, ret.update({'comment': '', 'result': None,
'changes': {'reg': 'configured to 0.15.3'}}) 'changes': {'reg': {'Will add': {'Key': name,
self.assertDictEqual(reg.present(name, value), ret) 'Entry': vname,
'Value': vdata}}}})
self.assertDictEqual(reg.present(name,
vname=vname,
vdata=vdata), ret)
with patch.dict(reg.__opts__, {'test': False}): with patch.dict(reg.__opts__, {'test': False}):
ret.update({'result': True}) ret.update({'comment': 'Added {0} to {0}'.format(name),
self.assertDictEqual(reg.present(name, value), ret) '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 # 'absent' function tests: 1
@ -65,27 +80,35 @@ class RegTestCase(TestCase):
''' '''
Test to remove a registry entry. Test to remove a registry entry.
''' '''
name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version' name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
vname = 'version'
ret = {'name': name, ret = {'name': name,
'changes': {}, 'changes': {},
'result': True, 'result': True,
'comment': '{0} is already absent'.format(name)} '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) 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}): '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}): with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None, ret.update({'comment': '', 'result': None,
'changes': {'reg': 'Removed {0}'.format(name)}}) 'changes': {'reg': {'Will remove': {'Entry': vname,
self.assertDictEqual(reg.absent(name), ret) 'Key': name}}}})
self.assertDictEqual(reg.absent(name, vname), ret)
with patch.dict(reg.__opts__, {'test': False}): with patch.dict(reg.__opts__, {'test': False}):
ret.update({'result': True}) ret.update({'result': True,
self.assertDictEqual(reg.absent(name), ret) 'changes': {'reg': {'Removed': {'Entry': vname,
'Key': name}}},
'comment': 'Removed {0} from {0}'.format(name)})
self.assertDictEqual(reg.absent(name, vname), ret)
if __name__ == '__main__': if __name__ == '__main__':