Skip to content

Commit

Permalink
Merge pull request #21571 from basepi/merge-forward-develop
Browse files Browse the repository at this point in the history
Merge forward from 2015.2 to develop
  • Loading branch information
Nicole Thomas committed Mar 11, 2015
2 parents 76d2759 + fec632a commit ad7c0bb
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 56 deletions.
2 changes: 2 additions & 0 deletions doc/ref/configuration/master.rst
Expand Up @@ -1832,6 +1832,8 @@ There are additional details at :ref:`salt-pillars`
``ext_pillar_first``
--------------------

.. versionadded:: 2015.2

The ext_pillar_first option allows for external pillar sources to populate
before file system pillar. This allows for targeting file system pillar from
ext_pillar.
Expand Down
95 changes: 55 additions & 40 deletions salt/auth/django.py
Expand Up @@ -2,42 +2,49 @@
'''
Provide authentication using Django Web Framework
Django authentication depends on the presence of the django
framework in the PYTHONPATH, the django project's settings.py file being in
the PYTHONPATH and accessible via the DJANGO_SETTINGS_MODULE environment
variable. This can be hard to debug.
:depends: - Django Web Framework
django auth can be defined like any other eauth module:
Django authentication depends on the presence of the django framework in the
``PYTHONPATH``, the Django project's ``settings.py`` file being in the
``PYTHONPATH`` and accessible via the ``DJANGO_SETTINGS_MODULE`` environment
variable.
external_auth:
django:
fred:
- .*
- '@runner'
Django auth can be defined like any other eauth module:
This will authenticate Fred via django and allow him to run any
execution module and all runners.
.. code-block:: yaml
The details of the django auth can also be located inside the django database. The
relevant entry in the models.py file would look like this:
external_auth:
django:
fred:
- .*
- '@runner'
class SaltExternalAuthModel(models.Model):
This will authenticate Fred via Django and allow him to run any execution
module and all runners.
user_fk = models.ForeignKey(auth.User)
minion_matcher = models.CharField()
minion_fn = models.CharField()
The authorization details can optionally be located inside the Django database.
The relevant entry in the ``models.py`` file would look like this:
Then, in the master's config file the external_auth clause should look like
.. code-block:: python
external_auth:
django:
^model: <fully-qualified reference to model class>
class SaltExternalAuthModel(models.Model):
user_fk = models.ForeignKey(auth.User)
minion_matcher = models.CharField()
minion_fn = models.CharField()
When a user attempts to authenticate via Django, Salt will import the package
indicated via the keyword '^model'. That model must have the fields
indicated above, though the model DOES NOT have to be named 'SaltExternalAuthModel'.
The :conf_master:`external_auth` clause in the master config would then look
like this:
:depends: - Django Web Framework
.. code-block:: yaml
external_auth:
django:
^model: <fully-qualified reference to model class>
When a user attempts to authenticate via Django, Salt will import the package
indicated via the keyword ``^model``. That model must have the fields
indicated above, though the model DOES NOT have to be named
'SaltExternalAuthModel'.
'''

# Import python libs
Expand Down Expand Up @@ -123,30 +130,38 @@ def retrieve_auth_entries(u=None):
'''
:param u: Username to filter for
:return: Dictionary that can be slotted into the __opts__ structure for eauth that designates the
user and his or her ACL
:return: Dictionary that can be slotted into the ``__opts__`` structure for
eauth that designates the user associated ACL
Database records such as:
=========== ==================== =========
username minion_or_fn_matcher minion_fn
=========== ==================== =========
fred test.ping
fred server1 network.interfaces
fred server1 raid.list
fred server2 .*
guru .*
smartadmin server1 .*
=========== ==================== =========
Should result in an eauth config such as:
.. code-block:: yaml
Should result in
fred:
- test.ping
- server1:
- network.interfaces
- raid.list
- server2:
fred:
- test.ping
- server1:
- network.interfaces
- raid.list
- server2:
- .*
guru:
- .*
guru:
- .*
smartadmin:
- server1:
- .*
smartadmin:
- server1:
- .*
'''
django_auth_setup()
Expand Down
43 changes: 33 additions & 10 deletions salt/client/__init__.py
Expand Up @@ -42,9 +42,10 @@
import salt.utils.event
import salt.utils.minions
import salt.utils.verify
import salt.utils.jid
import salt.syspaths as syspaths
from salt.exceptions import (
EauthAuthenticationError, SaltInvocationError, SaltReqTimeoutError
EauthAuthenticationError, SaltInvocationError, SaltReqTimeoutError, SaltClientError
)

# Import third party libs
Expand Down Expand Up @@ -857,7 +858,7 @@ def get_iter_returns(
yield {}
# stop the iteration, since the jid is invalid
raise StopIteration()
except salt.exceptions.SaltMasterError as exc:
except Exception as exc:
log.warning('Returner unavailable: {exc}'.format(exc=exc))
# Wait for the hosts to check in
syndic_wait = 0
Expand Down Expand Up @@ -1018,9 +1019,13 @@ def get_returns(
found = set()
ret = {}
# Check to see if the jid is real, if not return the empty dict
if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}:
log.warning('jid does not exist')
return ret
try:
if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}:
log.warning('jid does not exist')
return ret
except Exception as exc:
raise SaltClientError('Master job cache returner [{0}] failed to verify jid. '
'Exception details: {1}'.format(self.opts['master_job_cache'], exc))

# Wait for the hosts to check in
while True:
Expand Down Expand Up @@ -1062,7 +1067,13 @@ def get_full_returns(self, jid, minions, timeout=None):
# create the iterator-- since we want to get anyone in the middle
event_iter = self.get_event_iter_returns(jid, minions, timeout=timeout)

data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid)
try:
data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid)
except Exception as exc:
raise SaltClientError('Returner {0} could not fetch jid data. '
'Exception details: {1}'.format(
self.opts['master_job_cache'],
exc))
for minion in data:
m_data = {}
if u'return' in data[minion]:
Expand Down Expand Up @@ -1105,7 +1116,13 @@ def get_cache_returns(self, jid):
'''
ret = {}

data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid)
try:
data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid)
except Exception as exc:
raise SaltClientError('Could not examine master job cache. '
'Error occured in {0} returner. '
'Exception details: {1}'.format(self.opts['master_job_cache'],
exc))
for minion in data:
m_data = {}
if u'return' in data[minion]:
Expand Down Expand Up @@ -1151,9 +1168,15 @@ def get_cli_static_event_returns(
found = set()
ret = {}
# Check to see if the jid is real, if not return the empty dict
if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}:
log.warning('jid does not exist')
return ret
try:
if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}:
log.warning('jid does not exist')
return ret
except Exception as exc:
raise SaltClientError('Load could not be retreived from '
'returner {0}. Exception details: {1}'.format(
self.opts['master_job_cache'],
exc))
# Wait for the hosts to check in
while True:
# Process events until timeout is reached or all minions have returned
Expand Down
8 changes: 7 additions & 1 deletion salt/cloud/deploy/bootstrap-salt.sh
Expand Up @@ -17,7 +17,7 @@
# CREATED: 10/15/2012 09:49:37 PM WEST
#======================================================================================================================
set -o nounset # Treat unset variables as an error
__ScriptVersion="2015.03.04"
__ScriptVersion="2015.03.15"
__ScriptName="bootstrap-salt.sh"

#======================================================================================================================
Expand Down Expand Up @@ -4513,6 +4513,12 @@ config_salt() {
chmod 664 "$_PKI_DIR/minion/minion.pub" || return 1
CONFIGURED_ANYTHING=$BS_TRUE
fi
# For multi-master-pki, copy the master_sign public key if found
if [ -f "$_TEMP_CONFIG_DIR/master_sign.pub" ]; then
movefile "$_TEMP_CONFIG_DIR/master_sign.pub" "$_PKI_DIR/minion/" || return 1
chmod 664 "$_PKI_DIR/minion/master_sign.pub" || return 1
CONFIGURED_ANYTHING=$BS_TRUE
fi
fi


Expand Down
3 changes: 2 additions & 1 deletion salt/modules/file.py
Expand Up @@ -1313,7 +1313,8 @@ def replace(path,

# Keep track of show_changes here, in case the file isn't
# modified
if show_changes:
if show_changes or append_if_not_found or \
prepend_if_not_found:
orig_file.append(line)
new_file.append(result)

Expand Down
2 changes: 2 additions & 0 deletions salt/modules/win_status.py
Expand Up @@ -301,6 +301,8 @@ def _byte_calc(val):

def master(master=None, connected=True):
'''
.. versionadded:: 2015.2.0
Fire an event if the minion gets disconnected from its master. This
function is meant to be run via a scheduled job from the minion. If
master_ip is an FQDN/Hostname, is must be resolvable to a valid IPv4
Expand Down
28 changes: 24 additions & 4 deletions salt/utils/cloud.py
Expand Up @@ -360,6 +360,8 @@ def bootstrap(vm_, opts):
'conf_file': opts['conf_file'],
'minion_pem': vm_['priv_key'],
'minion_pub': vm_['pub_key'],
'master_sign_pub_file': salt.config.get_cloud_config_value(
'master_sign_pub_file', vm_, opts, default=None),
'keep_tmp': opts['keep_tmp'],
'sudo': salt.config.get_cloud_config_value(
'sudo', vm_, opts, default=(ssh_username != 'root')
Expand Down Expand Up @@ -841,6 +843,7 @@ def deploy_windows(host,
master=None,
tmp_dir='C:\\salttmp',
opts=None,
master_sign_pub_file=None,
**kwargs):
'''
Copy the install files to a remote Windows box, and execute them
Expand Down Expand Up @@ -879,10 +882,19 @@ def deploy_windows(host,
if minion_pem:
salt.utils.smb.put_str(minion_pem, 'salt\\conf\\pki\\minion\\minion.pem', conn=smb_conn)

# Shell out to smbclient to copy over win_installer
# win_installer refers to a file such as:
# /root/Salt-Minion-0.17.0-win32-Setup.exe
# ..which exists on the same machine as salt-cloud
if master_sign_pub_file:
# Read master-sign.pub file
log.debug("Copying master_sign.pub file from {0} to minion".format(master_sign_pub_file))
try:
with salt.utils.fopen(master_sign_pub_file, 'rb') as master_sign_fh:
smb_conn.putFile('C$', 'salt\\conf\\pki\\minion\\master_sign.pub', master_sign_fh.read)
except Exception as e:
log.debug("Exception copying master_sign.pub file {0} to minion".format(master_sign_pub_file))

# Copy over win_installer
# win_installer refers to a file such as:
# /root/Salt-Minion-0.17.0-win32-Setup.exe
# ..which exists on the same machine as salt-cloud
comps = win_installer.split('/')
local_path = '/'.join(comps[:-1])
installer = comps[-1]
Expand Down Expand Up @@ -988,6 +1000,7 @@ def deploy_script(host,
opts=None,
tmp_dir='/tmp/.saltcloud',
file_map=None,
master_sign_pub_file=None,
**kwargs):
'''
Copy a deploy script to a remote server, execute it, and remove it
Expand Down Expand Up @@ -1110,6 +1123,9 @@ def deploy_script(host,
if minion_pub:
sftp_file('{0}/minion.pub'.format(tmp_dir), minion_pub, ssh_kwargs)

if master_sign_pub_file:
sftp_file('{0}/master_sign.pub'.format(tmp_dir), kwargs=ssh_kwargs, local_file=master_sign_pub_file)

if minion_conf:
if not isinstance(minion_conf, dict):
# Let's not just fail regarding this change, specially
Expand Down Expand Up @@ -1333,6 +1349,10 @@ def deploy_script(host,
root_cmd('rm -f \'{0}/minion\''.format(tmp_dir),
tty, sudo, **ssh_kwargs)
log.debug('Removed {0}/minion'.format(tmp_dir))
if master_sign_pub_file:
root_cmd('rm -f {0}/master_sign.pub'.format(tmp_dir),
tty, sudo, **ssh_kwargs)
log.debug('Removed {0}/master_sign.pub'.format(tmp_dir))

# Remove master configuration
if master_pub:
Expand Down

0 comments on commit ad7c0bb

Please sign in to comment.