Merge pull request #40677 from rallytime/merge-develop

[develop] Merge forward from nitrogen to develop
This commit is contained in:
Nicole Thomas 2017-04-13 13:19:07 -06:00 committed by GitHub
commit 88f514835d
22 changed files with 675 additions and 249 deletions

View file

@ -746,6 +746,10 @@
#
#zmq_monitor: False
# Number of times to try to authenticate with the salt master when reconnecting
# to the master
#tcp_authentication_retries: 5
###### Module configuration #####
###########################################
# Salt allows for modules to be passed arbitrary configuration data, any data

View file

@ -2274,6 +2274,20 @@ ZeroMQ is installed.
.. conf_minion:: failhard
``tcp_authentication_retries``
------------------------------
Default: ``5``
The number of times to retry authenticating with the salt master when it comes
back online.
Zeromq does a lot to make sure when connections come back online that they
reauthenticate. The tcp transport should try to connect with a new connection
if the old one times out on reauthenticating.
`-1` for infinite tries.
``failhard``
------------

View file

@ -47,9 +47,9 @@ has now been extended to pillar SLS files as well. See :ref:`here
Grains Changes
==============
- The ``os_release`` grain has been changed from a string to an integer.
State files, especially those using a templating language like Jinja
may need to be adjusted to account for this change.
- The ``osmajorrelease`` grain has been changed from a string to an integer.
State files, especially those using a templating language like Jinja, may
need to be adjusted to account for this change.
- Add ability to specify disk backing mode in the VMWare salt cloud profile.
State Module Changes
@ -61,48 +61,50 @@ State Module Changes
start/stop the service using the ``--no-block`` flag in the ``systemctl``
command. On non-systemd minions, a warning will be issued.
- The :py:func:`module.run <salt.states.module.run>` state has dropped its previous
syntax with ``m_`` prefix for reserved keywords. Additionally, it allows
running several functions in a batch.
- The :py:func:`module.run <salt.states.module.run>` state has dropped its
previous syntax with ``m_`` prefix for reserved keywords. Additionally, it
allows running several functions in a batch.
.. note::
It is nesessary to explicitly turn on the new behaviour (see below)
.. note::
It is necessary to explicitly turn on the new behavior (see below)
Before and after:
.. code-block:: yaml
.. code-block:: yaml
# Before
run_something:
module.run:
- name: mymodule.something
- m_name: 'some name'
- kwargs: {
first_arg: 'one',
second_arg: 'two',
do_stuff: 'True'
}
# Before
run_something:
module.run:
- name: mymodule.something
- m_name: 'some name'
- kwargs: {
first_arg: 'one',
second_arg: 'two',
do_stuff: 'True'
}
# After
run_something:
module.run:
mymodule.something:
- name: some name
- first_arg: one
- second_arg: two
- do_stuff: True
- Previous behaviour of the function :py:func:`module.run <salt.states.module.run>` is
still kept by default and can be bypassed in case you want to use behaviour above.
Please keep in mind that the old syntax will no longer be supported in the ``Oxygen``
release of Salt. To enable the new behavior, add the following to the minion config file:
# After
run_something:
module.run:
mymodule.something:
- name: some name
- first_arg: one
- second_arg: two
- do_stuff: True
.. code-block:: yaml
Since a lot of users are already using :py:func:`module.run
<salt.states.module.run>` states, this new behavior must currently be
explicitly turned on, to allow users to take their time updating their SLS
files. However, please keep in mind that the new syntax will take effect in
the next feature release of Salt (Oxygen) and the old usage will no longer be
supported at that time.
use_superseded:
- module.run
To enable the new behavior for :py:func:`module.run <salt.states.module.run>`,
add the following to the minion config file:
.. code-block:: yaml
use_superseded:
- module.run
Execution Module Changes
@ -128,6 +130,8 @@ Execution Module Changes
- A ``pkg.list_repo_pkgs`` function has been added for both
:py:func:`Debian/Ubuntu <salt.modules.aptpkg.list_repo_pkgs>` and
:py:func:`Arch Linux <salt.modules.pacman.list_repo_pkgs>`-based distros.
- The :mod:`system <salt.modules.system>` module changed its return format
from "HH:MM AM/PM" to "HH:MM:SS AM/PM" for `get_system_time`.
Proxy Module Changes
@ -149,29 +153,26 @@ connection with the remote device only when required.
Wildcard Versions in :py:func:`pkg.installed <salt.states.pkg.installed>` States
================================================================================
The :py:func:`pkg.installed <salt.states.pkg.installed>` state now supports
wildcards in package versions, for the following platforms:
- The :py:func:`pkg.installed <salt.states.pkg.installed>` state now supports
wildcards in package versions, for the following platforms:
- Debian/Ubuntu
- RHEL/CentOS
- Arch Linux
- Debian/Ubuntu
- RHEL/CentOS
- Arch Linux
This support also extends to any derivatives of these distros, which use the
:mod:`aptpkg <salt.modules.aptpkg>`, :mod:`yumpkg <salt.modules.yumpkg>`, or
:mod:`pacman <salt.modules.pacman>` providers for the ``pkg`` virtual module.
This support also extends to any derivatives of these distros, which use the
:mod:`aptpkg <salt.modules.aptpkg>`, :mod:`yumpkg <salt.modules.yumpkg>`, or
:mod:`pacman <salt.modules.pacman>` providers for the ``pkg`` virtual module.
Using wildcards can be useful for packages where the release name is built into
the version in some way, such as for RHEL/CentOS which typically has version
numbers like ``1.2.34-5.el7``. An example of the usage for this would be:
Using wildcards can be useful for packages where the release name is built into
the version in some way, such as for RHEL/CentOS which typically has version
numbers like ``1.2.34-5.el7``. An example of the usage for this would be:
.. code-block:: yaml
.. code-block:: yaml
mypkg:
pkg.installed:
- version: '1.2.34*'
- The :mod:`system <salt.modules.system>` module changed the returned format
from "HH:MM AM/PM" to "HH:MM:SS AM/PM" for `get_system_time`.
mypkg:
pkg.installed:
- version: '1.2.34*'
Master Configuration Additions
==============================

View file

@ -1 +0,0 @@
../salt-api.service

14
pkg/suse/salt-api.service Normal file
View file

@ -0,0 +1,14 @@
[Unit]
Description=The Salt API
After=network.target
[Service]
User=salt
Type=simple
Environment=SHELL=/bin/bash
LimitNOFILE=8192
ExecStart=/usr/bin/salt-api
TimeoutStopSec=3
[Install]
WantedBy=multi-user.target

View file

@ -567,10 +567,20 @@ class LocalClient(object):
if 'gather_job_timeout' in kwargs:
opts['gather_job_timeout'] = kwargs['gather_job_timeout']
eauth = {}
if 'eauth' in kwargs:
eauth['eauth'] = kwargs.pop('eauth')
if 'username' in kwargs:
eauth['username'] = kwargs.pop('username')
if 'password' in kwargs:
eauth['password'] = kwargs.pop('password')
if 'token' in kwargs:
eauth['token'] = kwargs.pop('token')
for key, val in six.iteritems(self.opts):
if key not in opts:
opts[key] = val
batch = salt.cli.batch.Batch(opts, quiet=True)
batch = salt.cli.batch.Batch(opts, eauth=eauth, quiet=True)
for ret in batch.run():
yield ret

View file

@ -218,7 +218,7 @@ def prep_trans_tar(opts, file_client, chunks, file_refs, pillar=None, id_=None):
files = ''
if files:
for filename in files:
fn = filename[len(cache_dest):].strip('/')
fn = filename[len(file_client.get_cachedir(cache_dest)):].strip('/')
tgt = os.path.join(
env_root,
short,

View file

@ -1035,6 +1035,10 @@ VALID_OPTS = {
# django auth
'django_auth_path': str,
'django_auth_settings': str,
# Number of times to try to auth with the master on a reconnect with the
# tcp transport
'tcp_authentication_retries': int,
}
# default configurations
@ -1176,6 +1180,7 @@ DEFAULT_MINION_OPTS = {
'file_buffer_size': 262144,
'tcp_pub_port': 4510,
'tcp_pull_port': 4511,
'tcp_authentication_retries': 5,
'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'minion'),
'log_level': 'warning',
'log_level_logfile': None,

View file

@ -5,41 +5,59 @@ Junos Syslog Engine
.. versionadded:: Nitrogen
An engine that listen to syslog message from Junos devices,
:depends: pyparsing, twisted
An engine that listens to syslog message from Junos devices,
extract event information and generate message on SaltStack bus.
One can customize the name of the topic.
The event data consists of the following fields:
a. hostname
b. hostip
c. daemon
d. event
e. severity
f. priority
g. timestamp
h. message
i. pid
j. raw (the raw event data forwarded from the device)
The topic can consist of any of the combination of above fields,
The event topic sent to salt is dynamically generated according to the topic title
specified by the user. The incoming event data (from the junos device) consists
of the following fields:
1. hostname
2. hostip
3. daemon
4. event
5. severity
6. priority
7. timestamp
8. message
9. pid
10. raw (the raw event data forwarded from the device)
The topic title can consist of any of the combination of above fields,
but the topic has to start with jnpr/syslog.
So, we can have different combinations like:
jnpr/syslog/hostip/daemon/event
jnpr/syslog/daemon/severity
The default topic is jnpr/syslog/hostname/event.
The topic is to be specified in the configuration file.
So, we can have different combinations:
- jnpr/syslog/hostip/daemon/event
- jnpr/syslog/daemon/severity
The corresponding dynamic topic sent on salt event bus would look something like:
- jnpr/syslog/1.1.1.1/mgd/UI_COMMIT_COMPLETED
- jnpr/syslog/sshd/7
The default topic title is jnpr/syslog/hostname/event.
The user can choose the type of data he/she wants of the event bus.
Like, if one wants events pertaining to a particular daemon, he/she can
Like, if one wants only events pertaining to a particular daemon, he/she can
specify that in the configuration file:
.. code-block:: yaml
daemon: mgd
One can even have a list of daemons like:
.. code-block:: yaml
daemon:
- mgd
- sshd
:configuration:
Example configuration
Example configuration (to be written in master config file)
.. code-block:: yaml
engines:
- junos_syslog:
@ -49,15 +67,21 @@ One can even have a list of daemons like:
- mgd
- sshd
For junos_syslog engine to receive events syslog must be set on junos device.
For junos_syslog engine to receive events, syslog must be set on the junos device.
This can be done via following configuration:
.. code-block:: shell
set system syslog host <ip-of-the-salt-device> port 516 any any
Here is a sample syslog event which is received from the junos device:
'<30>May 29 05:18:12 bng-ui-vm-9 mspd[1492]: No chassis configuration found'
Below is a sample syslog event which is received from the junos device:
.. code-block:: shell
'<30>May 29 05:18:12 bng-ui-vm-9 mspd[1492]: No chassis configuration found'
The source for parsing the syslog messages is taken from:
https://gist.github.com/leandrosilva/3651640#file-xlog-py
https://gist.github.com/leandrosilva/3651640#file-xlog-py
'''
from __future__ import absolute_import
@ -92,12 +116,12 @@ def __virtual__():
Load only if twisted and pyparsing libs are present.
'''
if not HAS_TWISTED_AND_PYPARSING:
return (False, 'junos_syslog could not be loaded. \
Make sure you have twisted and pyparsing python libraries.')
return (False, 'junos_syslog could not be loaded.'
' Make sure you have twisted and pyparsing python libraries.')
return True
class Parser(object):
class _Parser(object):
def __init__(self):
ints = Word(nums)
@ -210,11 +234,11 @@ class Parser(object):
return payload
class SyslogServerFactory(DatagramProtocol):
class _SyslogServerFactory(DatagramProtocol):
def __init__(self, options):
self.options = options
self.obj = Parser()
self.obj = _Parser()
data = [
"hostip",
"priority",
@ -360,5 +384,5 @@ class SyslogServerFactory(DatagramProtocol):
def start(port=516, **kwargs):
log.info('Starting junos syslog engine (port {0})'.format(port))
reactor.listenUDP(port, SyslogServerFactory(kwargs))
reactor.listenUDP(port, _SyslogServerFactory(kwargs))
reactor.run()

View file

@ -132,11 +132,7 @@ class Client(object):
'''
Return the local location to cache the file, cache dirs will be made
'''
if cachedir is None:
cachedir = self.opts['cachedir']
elif not os.path.isabs(cachedir):
cachedir = os.path.join(self.opts['cachedir'], cachedir)
cachedir = self.get_cachedir(cachedir)
dest = salt.utils.path_join(cachedir,
'files',
saltenv,
@ -159,6 +155,13 @@ class Client(object):
yield dest
os.umask(cumask)
def get_cachedir(self, cachedir=None):
if cachedir is None:
cachedir = self.opts['cachedir']
elif not os.path.isabs(cachedir):
cachedir = os.path.join(self.opts['cachedir'], cachedir)
return cachedir
def get_file(self,
path,
dest='',
@ -250,10 +253,7 @@ class Client(object):
# prefix = ''
# else:
# prefix = separated[0]
if cachedir is None:
cachedir = self.opts['cachedir']
elif not os.path.isabs(cachedir):
cachedir = os.path.join(self.opts['cachedir'], cachedir)
cachedir = self.get_cachedir(cachedir)
dest = salt.utils.path_join(cachedir, 'files', saltenv)
for fn_ in self.file_list_emptydirs(saltenv):
@ -818,22 +818,6 @@ class LocalClient(Client):
if not fnd_path:
return ''
try:
fnd_mode = fnd.get('stat', [])[0]
except (IndexError, TypeError):
fnd_mode = None
if not salt.utils.is_windows():
if fnd_mode is not None:
try:
if os.stat(dest).st_mode != fnd_mode:
try:
os.chmod(dest, fnd_mode)
except OSError as exc:
log.warning('Failed to chmod %s: %s', dest, exc)
except Exception:
pass
return fnd_path
def file_list(self, saltenv='base', prefix=''):
@ -1085,47 +1069,7 @@ class RemoteClient(Client):
mode_local = None
if hash_local == hash_server:
if not salt.utils.is_windows():
if mode_server is None:
log.debug('No file mode available for \'%s\'', path)
elif mode_local is None:
log.debug(
'No file mode available for \'%s\'',
dest2check
)
else:
if mode_server == mode_local:
log.info(
'Fetching file from saltenv \'%s\', '
'** skipped ** latest already in cache '
'\'%s\', mode up-to-date', saltenv, path
)
else:
try:
os.chmod(dest2check, mode_server)
log.info(
'Fetching file from saltenv \'%s\', '
'** updated ** latest already in cache, '
'\'%s\', mode updated from %s to %s',
saltenv,
path,
salt.utils.st_mode_to_octal(mode_local),
salt.utils.st_mode_to_octal(mode_server)
)
except OSError as exc:
log.warning(
'Failed to chmod %s: %s', dest2check, exc
)
# We may not have been able to check/set the mode, but we
# don't want to re-download the file because of a failure
# in mode checking. Return the cached path.
return dest2check
else:
log.info(
'Fetching file from saltenv \'%s\', ** skipped ** '
'latest already in cache \'%s\'', saltenv, path
)
return dest2check
return dest2check
log.debug(
'Fetching file from saltenv \'%s\', ** attempting ** \'%s\'',
@ -1242,23 +1186,6 @@ class RemoteClient(Client):
saltenv, path
)
if not salt.utils.is_windows():
if mode_server is not None:
try:
if os.stat(dest).st_mode != mode_server:
try:
os.chmod(dest, mode_server)
log.info(
'Fetching file from saltenv \'%s\', '
'** done ** \'%s\', mode set to %s',
saltenv,
path,
salt.utils.st_mode_to_octal(mode_server)
)
except OSError:
log.warning('Failed to chmod %s: %s', dest, exc)
except OSError:
pass
return dest
def file_list(self, saltenv='base', prefix=''):

View file

@ -1669,7 +1669,14 @@ def os_data():
continue
osrelease_info[idx] = int(value)
grains['osrelease_info'] = tuple(osrelease_info)
grains['osmajorrelease'] = str(grains['osrelease_info'][0]) # This will be an integer in the two releases
try:
grains['osmajorrelease'] = int(grains['osrelease_info'][0])
except (IndexError, TypeError, ValueError):
log.debug(
'Unable to derive osmajorrelease from osrelease_info \'%s\'. '
'The osmajorrelease grain will not be set.',
grains['osrelease_info']
)
os_name = grains['os' if grains.get('os') in (
'FreeBSD', 'OpenBSD', 'NetBSD', 'Mac', 'Raspbian') else 'osfullname']
grains['osfinger'] = '{0}-{1}'.format(

View file

@ -632,6 +632,28 @@ def hash_file(path, saltenv='base'):
return _client().hash_file(path, saltenv)
def stat_file(path, saltenv='base', octal=True):
'''
Return the permissions of a file, to get the permissions of a file on the
salt master file server prepend the path with salt://<file on server>
otherwise, prepend the file with / for a local file.
CLI Example:
.. code-block:: bash
salt '*' cp.stat_file salt://path/to/file
'''
path, senv = salt.utils.url.split_env(path)
if senv:
saltenv = senv
stat = _client().hash_and_stat_file(path, saltenv)[1]
if stat is None:
return stat
return salt.utils.st_mode_to_octal(stat[0]) if octal is True else stat[0]
def push(path, keep_symlinks=False, upload_path=None, remove_source=False):
'''
WARNING Files pushed to the master will have global read permissions..

View file

@ -5240,7 +5240,7 @@ def sls(name, mods=None, saltenv='base', **kwargs):
copy_to(name,
trans_tar,
os.path.join(trans_dest_path, 'salt_state.tgz'),
exec_driver='nsenter',
exec_driver=_get_exec_driver(),
overwrite=True)
# Now execute the state into the container

View file

@ -4395,7 +4395,7 @@ def check_managed_changes(
if _urlparse(source).scheme in ('salt', 'file') \
or source.startswith('/'):
try:
mode = salt.utils.st_mode_to_octal(os.stat(sfn).st_mode)
mode = __salt__['cp.stat_file'](source, saltenv=saltenv, octal=True)
except Exception as exc:
log.warning('Unable to stat %s: %s', sfn, exc)
changes = check_file_meta(name, sfn, source, source_sum, user,
@ -4661,6 +4661,13 @@ def manage_file(name,
a local file on the minion), the mode of the destination file will be
set to the mode of the source file.
.. note:: keep_mode does not work with salt-ssh.
As a consequence of how the files are transfered to the minion, and
the inability to connect back to the master with salt-ssh, salt is
unable to stat the file as it exists on the fileserver and thus
cannot mirror the mode on the salt-ssh minion
encoding : None
If None, str() will be applied to contents.
If not None, specified encoding will be used.
@ -4710,7 +4717,7 @@ def manage_file(name,
if _urlparse(source).scheme in ('salt', 'file') \
or source.startswith('/'):
try:
mode = salt.utils.st_mode_to_octal(os.stat(sfn).st_mode)
mode = __salt__['cp.stat_file'](source, saltenv=saltenv, octal=True)
except Exception as exc:
log.warning('Unable to stat %s: %s', sfn, exc)

View file

@ -1,6 +1,17 @@
# -*- coding: utf-8 -*-
'''
Module to interact with Junos devices.
:maturity: new
:dependencies: junos-eznc, jxmlease
.. note::
Those who wish to use junos-eznc (PyEZ) version >= 2.1.0, must
use the latest salt code from github until the next release.
Refer to :mod:`junos <salt.proxy.junos>` for information on connecting to junos proxy.
'''
from __future__ import absolute_import
@ -19,6 +30,7 @@ except ImportError:
try:
# pylint: disable=W0611
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
from jnpr.junos.utils.sw import SW
from jnpr.junos.utils.scp import SCP
import jnpr.junos.utils
@ -394,7 +406,7 @@ def commit(**kwargs):
def rollback(id=0, **kwargs):
'''
To rollback the last committed configuration changes
To rollback the last committed configuration changes and commit the same.
Usage:
@ -471,7 +483,7 @@ def rollback(id=0, **kwargs):
ret['out'] = False
ret['message'] = \
'Rollback successful but commit failed with error "{0}"'\
.format(exception)
.format(exception)
return ret
else:
ret['message'] = 'Rollback succesfull but pre-commit check failed.'
@ -757,6 +769,8 @@ def install_config(path=None, **kwargs):
the content is treated as Junos OS 'set' commands.(default = None)
Optional
* kwargs: Keyworded arguments which can be provided like-
* mode: The mode in which the configuration is locked.
(Options: private, dynamic, batch, exclusive; default= exclusive)
* dev_timeout:
Set NETCONF RPC timeout. Can be used for commands which
take a while to execute. (default = 30 seconds)
@ -767,6 +781,17 @@ def install_config(path=None, **kwargs):
Specify whether the configuration file uses "replace:" statements.
Those statements under the 'replace' tag will only be changed.\
(default = False)
* format:
Determines the format of the contents.
* update:
Compare a complete loaded configuration against
the candidate configuration. For each hierarchy level or
configuration object that is different in the two configurations,
the version in the loaded configuration replaces the version in the
candidate configuration. When the configuration is later committed,
only system processes that are affected by the changed configuration
elements parse the new configuration. This action is supported from
PyEZ 2.1 (default = False)
* comment:
Provide a comment to the commit. (default = None)
* confirm:
@ -850,64 +875,66 @@ def install_config(path=None, **kwargs):
op['merge'] = True
del op['overwrite']
try:
conn.cu.load(**op)
except Exception as exception:
ret['message'] = 'Could not load configuration due to : "{0}"'.format(
exception)
ret['format'] = template_format
ret['out'] = False
return ret
finally:
safe_rm(template_cached_path)
config_diff = conn.cu.diff()
if config_diff is None:
ret['message'] = 'Configuration already applied!'
ret['out'] = True
return ret
commit_params = {}
if 'confirm' in op:
commit_params['confirm'] = op['confirm']
if 'comment' in op:
commit_params['comment'] = op['comment']
try:
check = conn.cu.commit_check()
except Exception as exception:
ret['message'] = \
'Commit check threw the following exception: "{0}"'\
.format(exception)
ret['out'] = False
return ret
if check:
db_mode = op.pop('mode', 'exclusive')
with Config(conn, mode=db_mode) as cu:
try:
conn.cu.commit(**commit_params)
ret['message'] = 'Successfully loaded and committed!'
cu.load(**op)
except Exception as exception:
ret['message'] = \
'Commit check successful but commit failed with "{0}"'\
.format(exception)
ret['message'] = 'Could not load configuration due to : "{0}"'.format(
exception)
ret['format'] = template_format
ret['out'] = False
return ret
else:
ret['message'] = 'Loaded configuration but commit check failed.'
ret['out'] = False
conn.cu.rollback()
try:
if write_diff and config_diff is not None:
with fopen(write_diff, 'w') as fp:
fp.write(config_diff)
except Exception as exception:
ret['message'] = 'Could not write into diffs_file due to: "{0}"'.format(
exception)
ret['out'] = False
finally:
safe_rm(template_cached_path)
config_diff = cu.diff()
if config_diff is None:
ret['message'] = 'Configuration already applied!'
ret['out'] = True
return ret
commit_params = {}
if 'confirm' in op:
commit_params['confirm'] = op['confirm']
if 'comment' in op:
commit_params['comment'] = op['comment']
try:
check = cu.commit_check()
except Exception as exception:
ret['message'] = \
'Commit check threw the following exception: "{0}"'\
.format(exception)
ret['out'] = False
return ret
if check:
try:
cu.commit(**commit_params)
ret['message'] = 'Successfully loaded and committed!'
except Exception as exception:
ret['message'] = \
'Commit check successful but commit failed with "{0}"'\
.format(exception)
ret['out'] = False
return ret
else:
ret['message'] = 'Loaded configuration but commit check failed.'
ret['out'] = False
cu.rollback()
try:
if write_diff and config_diff is not None:
with fopen(write_diff, 'w') as fp:
fp.write(config_diff)
except Exception as exception:
ret['message'] = 'Could not write into diffs_file due to: "{0}"'.format(
exception)
ret['out'] = False
return ret
@ -1070,3 +1097,212 @@ def file_copy(src=None, dest=None):
ret['message'] = 'Could not copy file : "{0}"'.format(exception)
ret['out'] = False
return ret
def lock():
"""
Attempts an exclusive lock on the candidate configuration. This
is a non-blocking call.
.. note::
Any user who wishes to use lock, must necessarily unlock the
configuration too. Ensure :py:func:`unlock <salt.modules.junos.unlock>`
is called in the same orchestration run in which the lock is called.
Usage:
.. code-block:: bash
salt 'device_name' junos.lock
"""
conn = __proxy__['junos.conn']()
ret = dict()
ret['out'] = True
try:
conn.cu.lock()
ret['message'] = "Successfully locked the configuration."
except jnpr.junos.exception.LockError as exception:
ret['message'] = 'Could not gain lock due to : "{0}"'.format(exception)
ret['out'] = False
return ret
def unlock():
"""
Unlocks the candidate configuration.
Usage:
.. code-block:: bash
salt 'device_name' junos.unlock
"""
conn = __proxy__['junos.conn']()
ret = dict()
ret['out'] = True
try:
conn.cu.unlock()
ret['message'] = "Successfully unlocked the configuration."
except jnpr.junos.exception.UnlockError as exception:
ret['message'] = \
'Could not unlock configuration due to : "{0}"'.format(exception)
ret['out'] = False
return ret
def load(path=None, **kwargs):
"""
Loads the configuration from the file provided onto the device.
Usage:
.. code-block:: bash
salt 'device_name' junos.load 'salt://production/network/routers/config.set'
salt 'device_name' junos.load 'salt://templates/replace_config.conf' replace=True
salt 'device_name' junos.load 'salt://my_new_configuration.conf' overwrite=True
salt 'device_name' junos.load 'salt://syslog_template.conf' template_vars='{"syslog_host": "10.180.222.7"}'
Parameters:
Required
* path:
Path where the configuration/template file is present. If the file has a \
'*.conf' extension,
the content is treated as text format. If the file has a '*.xml' \
extension,
the content is treated as XML format. If the file has a '*.set' \
extension,
the content is treated as Junos OS 'set' commands.(default = None)
Optional
* kwargs: Keyworded arguments which can be provided like-
* overwrite:
Set to True if you want this file is to completely replace the\
configuration file. (default = False)
* replace:
Specify whether the configuration file uses "replace:" statements.
Those statements under the 'replace' tag will only be changed.\
(default = False)
* format:
Determines the format of the contents.
* update:
Compare a complete loaded configuration against
the candidate configuration. For each hierarchy level or
configuration object that is different in the two configurations,
the version in the loaded configuration replaces the version in the
candidate configuration. When the configuration is later committed,
only system processes that are affected by the changed configuration
elements parse the new configuration. This action is supported from
PyEZ 2.1 (default = False)
* template_vars:
Variables to be passed into the template processing engine in addition
to those present in __pillar__, __opts__, __grains__, etc.
You may reference these variables in your template like so:
{{ template_vars["var_name"] }}
"""
conn = __proxy__['junos.conn']()
ret = dict()
ret['out'] = True
if path is None:
ret['message'] = \
'Please provide the salt path where the configuration is present'
ret['out'] = False
return ret
op = dict()
if '__pub_arg' in kwargs:
if kwargs['__pub_arg']:
if isinstance(kwargs['__pub_arg'][-1], dict):
op.update(kwargs['__pub_arg'][-1])
else:
op.update(kwargs)
template_vars = dict()
if "template_vars" in op:
template_vars = op["template_vars"]
template_cached_path = files.mkstemp()
__salt__['cp.get_template'](
path,
template_cached_path,
template_vars=template_vars)
if not os.path.isfile(template_cached_path):
ret['message'] = 'Invalid file path.'
ret['out'] = False
return ret
if os.path.getsize(template_cached_path) == 0:
ret['message'] = 'Template failed to render'
ret['out'] = False
return ret
op['path'] = template_cached_path
if 'format' not in op:
if path.endswith('set'):
template_format = 'set'
elif path.endswith('xml'):
template_format = 'xml'
else:
template_format = 'text'
op['format'] = template_format
if 'replace' in op and op['replace']:
op['merge'] = False
del op['replace']
elif 'overwrite' in op and op['overwrite']:
op['overwrite'] = True
elif 'overwrite' in op and not op['overwrite']:
op['merge'] = True
del op['overwrite']
try:
conn.cu.load(**op)
ret['message'] = "Successfully loaded the configuration."
except Exception as exception:
ret['message'] = 'Could not load configuration due to : "{0}"'.format(
exception)
ret['format'] = template_format
ret['out'] = False
return ret
finally:
safe_rm(template_cached_path)
return ret
def commit_check():
"""
Perform a commit check on the configuration.
Usage:
.. code-block:: bash
salt 'device_name' junos.commit_check
"""
conn = __proxy__['junos.conn']()
ret = dict()
ret['out'] = True
try:
conn.cu.commit_check()
ret['message'] = 'Commit check succeeded.'
except Exception as exception:
ret['message'] = 'Commit check failed with {0}'.format(exception)
ret['out'] = False
return ret

View file

@ -113,6 +113,20 @@ class NetapiClient(object):
local = salt.client.get_local_client(mopts=self.opts)
return local.cmd_subset(*args, **kwargs)
def local_batch(self, *args, **kwargs):
'''
Run :ref:`execution modules <all-salt.modules>` against batches of minions
.. versionadded:: 0.8.4
Wraps :py:meth:`salt.client.LocalClient.cmd_batch`
:return: Returns the result from the exeuction module for each batch of
returns
'''
local = salt.client.get_local_client(mopts=self.opts)
return local.cmd_batch(*args, **kwargs)
def ssh(self, *args, **kwargs):
'''
Run salt-ssh commands synchronously

View file

@ -1735,6 +1735,13 @@ def managed(name,
the ``contents`` options, setting the ``mode`` to ``keep`` is also
incompatible with the ``contents`` options.
.. note:: keep does not work with salt-ssh.
As a consequence of how the files are transfered to the minion, and
the inability to connect back to the master with salt-ssh, salt is
unable to stat the file as it exists on the fileserver and thus
cannot mirror the mode on the salt-ssh minion
template
If this setting is applied, the named templating engine will be used to
render the downloaded file. The following templates are supported:

View file

@ -3,7 +3,14 @@
State modules to interact with Junos devices.
==============================================
State module to connect the junos devices connected via a junos proxy.
:maturity: new
:dependencies: junos-eznc, jxmlease
.. note::
Those who wish to use junos-eznc (PyEZ) version >= 2.1.0, must
use the latest salt code from github until the next release.
Refer to :mod:`junos <salt.proxy.junos>` for information on connecting to junos proxy.
'''
from __future__ import absolute_import
@ -281,8 +288,8 @@ def install_config(name, **kwargs):
Parameters:
Required
* path or template_path (Either one of the keyword must be there):
Path where the configuration file is present. If the file has a \
* name:
Path where the configuration/template file is present. If the file has a \
'*.conf' extension,
the content is treated as text format. If the file has a '*.xml' \
extension,
@ -291,8 +298,6 @@ def install_config(name, **kwargs):
the content is treated as Junos OS 'set' commands.(default = None)
Optional
* kwargs: Keyworded arguments which can be provided like-
* template_path:
Path where the jinja template is present on the master.
* template_vars:
The dictionary of data for the jinja variables present in the \
jinja template
@ -396,3 +401,119 @@ def file_copy(name, dest=None, **kwargs):
ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''}
ret['changes'] = __salt__['junos.file_copy'](name, dest, **kwargs)
return ret
def lock(name):
"""
Attempts an exclusive lock on the candidate configuration. This
is a non-blocking call.
.. note::
Any user who wishes to use lock, must necessarily unlock the
configuration too. Ensure :py:func:`unlock <salt.states.junos.unlock>`
is called in the same orchestration run in which the lock is called.
.. code-block:: yaml
lock the config:
junos.lock
"""
ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''}
ret['changes'] = __salt__['junos.lock']()
return ret
def unlock(name):
"""
Unlocks the candidate configuration.
.. code-block:: yaml
unlock the config:
junos.unlock
"""
ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''}
ret['changes'] = __salt__['junos.unlock']()
return ret
def load(name, **kwargs):
"""
Loads the configuration provided onto the junos device.
.. code-block:: yaml
Install the mentioned config:
junos:
- load
- path: salt//configs/interface.set
.. code-block:: yaml
Install the mentioned config:
junos:
- load
- template_path: salt//configs/interface.set
- template_vars:
interface_name: lo0
description: Creating interface via SaltStack.
Parameters:
Required
* name:
Path where the configuration/template file is present. If the file has a \
'*.conf' extension,
the content is treated as text format. If the file has a '*.xml' \
extension,
the content is treated as XML format. If the file has a '*.set' \
extension,
the content is treated as Junos OS 'set' commands.(default = None)
Optional
* kwargs: Keyworded arguments which can be provided like-
* overwrite:
Set to True if you want this file is to completely replace the\
configuration file. (default = False)
* replace:
Specify whether the configuration file uses "replace:" statements.
Those statements under the 'replace' tag will only be changed.\
(default = False)
* format:
Determines the format of the contents.
* update:
Compare a complete loaded configuration against
the candidate configuration. For each hierarchy level or
configuration object that is different in the two configurations,
the version in the loaded configuration replaces the version in the
candidate configuration. When the configuration is later committed,
only system processes that are affected by the changed configuration
elements parse the new configuration. This action is supported from
PyEZ 2.1 (default = False)
* template_vars:
Variables to be passed into the template processing engine in addition
to those present in __pillar__, __opts__, __grains__, etc.
You may reference these variables in your template like so:
{{ template_vars["var_name"] }}
"""
ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''}
ret['changes'] = __salt__['junos.load'](name, **kwargs)
return ret
def commit_check(name):
"""
Perform a commit check on the configuration.
.. code-block:: yaml
perform commit check:
junos.commit_check
"""
ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''}
ret['changes'] = __salt__['junos.commit_check']()
return ret

View file

@ -408,7 +408,14 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran
raise tornado.gen.Return(True)
if force_auth or not self.auth.authenticated:
yield self.auth.authenticate()
count = 0
while count <= self.opts['tcp_authentication_retries'] or self.opts['tcp_authentication_retries'] < 0:
try:
yield self.auth.authenticate()
break
except SaltClientError as exc:
log.debug(exc)
count += 1
try:
ret = yield _do_transfer()
raise tornado.gen.Return(ret)

View file

@ -1153,7 +1153,7 @@ class GitPython(GitProvider):
'''
Using the blob object, write the file to the destination path
'''
with salt.utils.fopen(dest, 'w+') as fp_:
with salt.utils.fopen(dest, 'wb+') as fp_:
blob.stream_data(fp_)
@ -1819,7 +1819,7 @@ class Pygit2(GitProvider):
'''
Using the blob object, write the file to the destination path
'''
with salt.utils.fopen(dest, 'w+') as fp_:
with salt.utils.fopen(dest, 'wb+') as fp_:
fp_.write(blob.data)

View file

@ -207,7 +207,8 @@ class CkMinions(object):
'''
if isinstance(expr, six.string_types):
expr = [m for m in expr.split(',') if m]
return [x for x in expr if x in self._pki_minions()]
minions = self._pki_minions()
return [x for x in expr if x in minions]
def _check_pcre_minions(self, expr, greedy): # pylint: disable=unused-argument
'''

View file

@ -30,6 +30,9 @@ import signal
import select
import logging
# Import salt libs
from salt.ext import six
mswindows = (sys.platform == "win32")
try:
@ -566,7 +569,10 @@ class Terminal(object):
try:
if self.stdin_logger:
self.stdin_logger.log(self.stdin_logger_level, data)
written = os.write(self.child_fd, data)
if six.PY3:
written = os.write(self.child_fd, data.encode(__salt_system_encoding__))
else:
written = os.write(self.child_fd, data)
except OSError as why:
if why.errno == errno.EPIPE: # broken pipe
os.close(self.child_fd)

View file

@ -262,7 +262,7 @@ PATCHLEVEL = 3
'osfullname': "SLES",
'osrelease': '11.3',
'osrelease_info': [11, 3],
'osmajorrelease': '11',
'osmajorrelease': 11,
'files': ["/etc/SuSE-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -286,7 +286,7 @@ PATCHLEVEL = 3
'osfullname': "SLES",
'osrelease': '11.4',
'osrelease_info': [11, 4],
'osmajorrelease': '11',
'osmajorrelease': 11,
'files': ["/etc/os-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -310,7 +310,7 @@ PATCHLEVEL = 3
'osfullname': "SLES",
'osrelease': '12',
'osrelease_info': [12],
'osmajorrelease': '12',
'osmajorrelease': 12,
'files': ["/etc/os-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -334,7 +334,7 @@ PATCHLEVEL = 3
'osfullname': "SLES",
'osrelease': '12.1',
'osrelease_info': [12, 1],
'osmajorrelease': '12',
'osmajorrelease': 12,
'files': ["/etc/os-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -358,7 +358,7 @@ PATCHLEVEL = 3
'osfullname': "Leap",
'osrelease': '42.1',
'osrelease_info': [42, 1],
'osmajorrelease': '42',
'osmajorrelease': 42,
'files': ["/etc/os-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -382,7 +382,7 @@ PATCHLEVEL = 3
'osfullname': "Tumbleweed",
'osrelease': '20160504',
'osrelease_info': [20160504],
'osmajorrelease': '20160504',
'osmajorrelease': 20160504,
'files': ["/etc/os-release"],
}
self._run_suse_os_grains_tests(_os_release_map)
@ -404,7 +404,7 @@ PATCHLEVEL = 3
'osfullname': 'Ubuntu',
'osrelease': '16.04',
'osrelease_info': [16, 4],
'osmajorrelease': '16',
'osmajorrelease': 16,
'osfinger': 'Ubuntu-16.04',
}
self._run_ubuntu_os_grains_tests(_os_release_map)