Merge remote branch 'upstream/develop' into develop

This commit is contained in:
Forrest Alvarez 2013-10-24 03:37:11 +00:00
commit 32a3d82595
22 changed files with 900 additions and 246 deletions

135
COPYING Normal file
View File

@ -0,0 +1,135 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: salt
Upstream-Contact: salt-users@googlegroups.com
Source: https://github.com/saltstack/salt
Files: *
Copyright: 2013 SaltStack Team
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License, Version 2.0 can be
found in the file
`/usr/share/common-licenses/Apache-2.0'.
Files: debian/*
Copyright: 2013 Joe Healy <joehealy@gmail.com>
2012 Michael Prokop <mika@debian.org>
2012 Christian Hofstaedtler <christian@hofstaedtler.name>
2012 Ulrich Dangel <mru@spamt.net>
2012 Corey Quinn <corey@sequestered.net>
2011 Aaron Toponce <aaron.toponce@gmail.com>
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License, Version 2.0 can be
found in the file
`/usr/share/common-licenses/Apache-2.0'.
Files: salt/auth/pam.py
Copyright: 2007 Chris AtLee <chris@atlee.ca>
License: MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Files: salt/utils/ipaddr.py
Copyright: 2007 Google Inc.
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License, Version 2.0 can be
found in the file
`/usr/share/common-licenses/Apache-2.0'.
Files: doc/_ext/youtube.py
Copyright: 2009 Chris Pickel <sfiera@gmail.com>
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Files: doc/_ext/images
Copyright: 2013 SaltStack Team
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License, Version 2.0 can be
found in the file
`/usr/share/common-licenses/Apache-2.0'.
.
Files in this directory were created in-house.

View File

@ -383,7 +383,14 @@ Finally you use setup.py to run the tests with the following command:
./setup.py test ./setup.py test
For greater control while running the tests, please try: For greater control while running the tests, please try something like:
.. code-block:: bash
./tests/runtests.py -n integration.modules.virt -vv
./tests/runtests.py -n unit.modules.virt_test -vv
Also see the help for all options:
.. code-block:: bash .. code-block:: bash

View File

@ -193,6 +193,8 @@ and run NSIS.
The NSIS installer can be found here: http://nsis.sourceforge.net/Main_Page The NSIS installer can be found here: http://nsis.sourceforge.net/Main_Page
You also need the nsProcess plugin for NSIS. It can be found here: http://nsis.sourceforge.net/NsProcess_plugin
Testing the Salt minion Testing the Salt minion
======================= =======================

View File

@ -2,6 +2,13 @@
Salt 0.17.1 Release Notes Salt 0.17.1 Release Notes
========================= =========================
.. note::
THIS RELEASE IS NOT COMPATIBLE WITH PREVIOUS VERSIONS. If you update your
master to 0.17.1, you must update your minions as well. Sorry for the
inconvenience -- this is a result of one of the security fixes listed
below.
The 0.17.1 release comes with a number of improvements to salt-ssh, many The 0.17.1 release comes with a number of improvements to salt-ssh, many
bugfixes, and a number of security updates. bugfixes, and a number of security updates.

View File

@ -26,16 +26,6 @@
# processname: /usr/bin/salt-master # processname: /usr/bin/salt-master
if [ -f /etc/default/salt ]; then
. /etc/default/salt
else
SALTMASTER=/usr/bin/salt-master
PYTHON=/usr/bin/python
fi
# Sanity checks.
[ -x $SALTMASTER ] || exit 0
DEBIAN_VERSION=/etc/debian_version DEBIAN_VERSION=/etc/debian_version
SUSE_RELEASE=/etc/SuSE-release SUSE_RELEASE=/etc/SuSE-release
# Source function library. # Source function library.
@ -47,27 +37,35 @@ else
. /etc/rc.d/init.d/functions . /etc/rc.d/init.d/functions
fi fi
# Default values (can be overridden below)
SALTMASTER=/usr/bin/salt-master
PYTHON=/usr/bin/python
MASTER_ARGS=""
if [ -f /etc/default/salt ]; then
. /etc/default/salt
fi
SERVICE=salt-master SERVICE=salt-master
PROCESS=salt-master PROCESS=salt-master
CONFIG_ARGS=" "
RETVAL=0 RETVAL=0
start() { start() {
echo -n $"Starting salt-master daemon: " echo -n $"Starting salt-master daemon: "
if [ -f $SUSE_RELEASE ]; then if [ -f $SUSE_RELEASE ]; then
startproc -f -p /var/run/$SERVICE.pid $SALTMASTER -d $CONFIG_ARGS startproc -f -p /var/run/$SERVICE.pid $SALTMASTER -d $MASTER_ARGS
rc_status -v rc_status -v
elif [ -e $DEBIAN_VERSION ]; then elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then if [ -f $LOCKFILE ]; then
echo -n "already started, lock file found" echo -n "already started, lock file found"
RETVAL=1 RETVAL=1
elif $PYTHON $SALTMASTER -d >& /dev/null; then elif $PYTHON $SALTMASTER -d $MASTER_ARGS >& /dev/null; then
echo -n "OK" echo -n "OK"
RETVAL=0 RETVAL=0
fi fi
else else
daemon --check $SERVICE $SALTMASTER -d $CONFIG_ARGS daemon --check $SERVICE $SALTMASTER -d $MASTER_ARGS
fi fi
RETVAL=$? RETVAL=$?
echo echo
@ -100,7 +98,6 @@ restart() {
start start
} }
# See how we were called. # See how we were called.
case "$1" in case "$1" in
start|stop|restart) start|stop|restart)
@ -129,11 +126,7 @@ case "$1" in
;; ;;
reload) reload)
echo "can't reload configuration, you have to restart it" echo "can't reload configuration, you have to restart it"
if [ -f $SUSE_RELEASE ]; then
rc_status -v
else
RETVAL=$? RETVAL=$?
fi
;; ;;
*) *)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}" echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
@ -141,4 +134,3 @@ case "$1" in
;; ;;
esac esac
exit $RETVAL exit $RETVAL

View File

@ -14,7 +14,8 @@
# Default-Start: 3 5 # Default-Start: 3 5
# Default-Stop: 0 1 2 6 # Default-Stop: 0 1 2 6
# Short-Description: Salt minion daemon # Short-Description: Salt minion daemon
# Description: This is the Salt minion daemon that can be controlled by the Salt master. # Description: This is the Salt minion daemon that can be controlled by the
# Salt master.
### END INIT INFO ### END INIT INFO
@ -26,16 +27,6 @@
# processname: /usr/bin/salt-minion # processname: /usr/bin/salt-minion
if [ -f /etc/default/salt ]; then
. /etc/default/salt
else
SALTMINION=/usr/bin/salt-minion
PYTHON=/usr/bin/python
fi
# Sanity checks.
[ -x $SALTMINION ] || exit 0
DEBIAN_VERSION=/etc/debian_version DEBIAN_VERSION=/etc/debian_version
SUSE_RELEASE=/etc/SuSE-release SUSE_RELEASE=/etc/SuSE-release
# Source function library. # Source function library.
@ -47,22 +38,30 @@ else
. /etc/rc.d/init.d/functions . /etc/rc.d/init.d/functions
fi fi
# Default values (can be overridden below)
SALTMINION=/usr/bin/salt-minion
PYTHON=/usr/bin/python
MINION_ARGS=""
if [ -f /etc/default/salt ]; then
. /etc/default/salt
fi
SERVICE=salt-minion SERVICE=salt-minion
PROCESS=salt-minion PROCESS=salt-minion
CONFIG_ARGS=" "
RETVAL=0 RETVAL=0
start() { start() {
echo -n $"Starting salt-minion daemon: " echo -n $"Starting salt-minion daemon: "
if [ -f $SUSE_RELEASE ]; then if [ -f $SUSE_RELEASE ]; then
startproc -f -p /var/run/$SERVICE.pid $SALTMINION -d $CONFIG_ARGS startproc -f -p /var/run/$SERVICE.pid $SALTMINION -d $MINION_ARGS
rc_status -v rc_status -v
elif [ -e $DEBIAN_VERSION ]; then elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then if [ -f $LOCKFILE ]; then
echo -n "already started, lock file found" echo -n "already started, lock file found"
RETVAL=1 RETVAL=1
elif $PYTHON $SALTMINION -d >& /dev/null; then elif $PYTHON $SALTMINION -d $MINION_ARGS >& /dev/null; then
echo -n "OK" echo -n "OK"
RETVAL=0 RETVAL=0
fi fi
@ -71,7 +70,7 @@ start() {
RETVAL=$? RETVAL=$?
echo -n "already running" echo -n "already running"
else else
daemon --check $SERVICE $SALTMINION -d $CONFIG_ARGS daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
RETVAL=$? RETVAL=$?
fi fi
fi fi
@ -133,11 +132,7 @@ case "$1" in
;; ;;
reload) reload)
echo "can't reload configuration, you have to restart it" echo "can't reload configuration, you have to restart it"
if [ -f $SUSE_RELEASE ]; then
rc_status -v
else
RETVAL=$? RETVAL=$?
fi
;; ;;
*) *)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}" echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"

View File

@ -14,28 +14,19 @@
# Default-Start: 3 5 # Default-Start: 3 5
# Default-Stop: 0 1 2 6 # Default-Stop: 0 1 2 6
# Short-Description: Salt syndic master-minion passthrough daemon # Short-Description: Salt syndic master-minion passthrough daemon
# Description: This is a the Salt syndic daemon that enables Salt master-minion remote control passthrough. # Description: This is a the Salt syndic daemon that enables Salt master-minion
# remote control passthrough.
### END INIT INFO ### END INIT INFO
# chkconfig header # chkconfig header
# chkconfig: 345 99 99 # chkconfig: - 99 99
# description: This is a the Salt syndic daemon that enables Salt master-minion remote control passthrough. # description: This is a the Salt syndic daemon that enables Salt master-minion remote control passthrough.
# #
# processname: /usr/bin/salt-syndic # processname: /usr/bin/salt-syndic
if [ -f /etc/default/salt ]; then
. /etc/default/salt
else
SALTSYNDIC=/usr/bin/salt-syndic
PYTHON=/usr/bin/python
fi
# Sanity checks.
[ -x $SALTSYNDIC ] || exit 0
DEBIAN_VERSION=/etc/debian_version DEBIAN_VERSION=/etc/debian_version
SUSE_RELEASE=/etc/SuSE-release SUSE_RELEASE=/etc/SuSE-release
# Source function library. # Source function library.
@ -47,27 +38,35 @@ else
. /etc/rc.d/init.d/functions . /etc/rc.d/init.d/functions
fi fi
# Default values (can be overridden below)
SALTSYNDIC=/usr/bin/salt-syndic
PYTHON=/usr/bin/python
SYNDIC_ARGS=""
if [ -f /etc/default/salt ]; then
. /etc/default/salt
fi
SERVICE=salt-syndic SERVICE=salt-syndic
PROCESS=salt-syndic PROCESS=salt-syndic
CONFIG_ARGS=" "
RETVAL=0 RETVAL=0
start() { start() {
echo -n $"Starting salt-syndic daemon: " echo -n $"Starting salt-syndic daemon: "
if [ -f $SUSE_RELEASE ]; then if [ -f $SUSE_RELEASE ]; then
startproc -f -p /var/run/$SERVICE.pid $SALTSYNDIC -d $CONFIG_ARGS startproc -f -p /var/run/$SERVICE.pid $SALTSYNDIC -d $SYNDIC_ARGS
rc_status -v rc_status -v
elif [ -e $DEBIAN_VERSION ]; then elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then if [ -f $LOCKFILE ]; then
echo -n "already started, lock file found" echo -n "already started, lock file found"
RETVAL=1 RETVAL=1
elif $PYTHON $SALTSYNDIC -d >& /dev/null; then elif $PYTHON $SALTSYNDIC -d $SYNDIC_ARGS >& /dev/null; then
echo -n "OK" echo -n "OK"
RETVAL=0 RETVAL=0
fi fi
else else
daemon --check $SERVICE $SALTSYNDIC -d $CONFIG_ARGS daemon --check $SERVICE $SALTSYNDIC -d $SYNDIC_ARGS
fi fi
RETVAL=$? RETVAL=$?
echo echo
@ -123,18 +122,16 @@ case "$1" in
RETVAL=$? RETVAL=$?
fi fi
;; ;;
condrestart)
[ -f $LOCKFILE ] && restart || :
;;
reload) reload)
echo "can't reload configuration, you have to restart it" echo "can't reload configuration, you have to restart it"
if [ -f $SUSE_RELEASE ]; then
rc_status -v
else
RETVAL=$? RETVAL=$?
fi
;; ;;
*) *)
echo $"Usage: $0 {start|stop|status|restart|reload}" echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
exit 1 exit 1
;; ;;
esac esac
exit $RETVAL exit $RETVAL

View File

@ -16,7 +16,7 @@
# #
Name: salt Name: salt
Version: 0.17.0 Version: 0.17.1
Release: 0 Release: 0
Summary: A parallel remote execution system Summary: A parallel remote execution system
License: Apache-2.0 License: Apache-2.0
@ -31,10 +31,8 @@ Source5: %{name}-syndic.service
Source6: %{name}-minion.service Source6: %{name}-minion.service
Source7: %{name}.logrotate Source7: %{name}.logrotate
Source8: %{name}.SuSEfirewall2 Source8: %{name}.SuSEfirewall2
%if 0%{?sles_version}
BuildRequires: python #for building
Requires: python
%endif
BuildRequires: python-devel BuildRequires: python-devel
BuildRequires: logrotate BuildRequires: logrotate
BuildRequires: python-Jinja2 BuildRequires: python-Jinja2
@ -42,18 +40,32 @@ BuildRequires: python-M2Crypto
BuildRequires: python-PyYAML BuildRequires: python-PyYAML
BuildRequires: python-msgpack-python BuildRequires: python-msgpack-python
BuildRequires: python-pycrypto BuildRequires: python-pycrypto
BuildRequires: python-pyzmq >= 2.1.9 BuildRequires: python-pyzmq
BuildRequires: unzip %if 0%{?sles_version}
Requires: logrotate BuildRequires: python
Requires: python-Jinja2 Requires: python
Requires: python-PyYAML %endif
Requires: python-Sphinx
Requires(pre): %fillup_prereq
Requires(pre): %insserv_prereq
%if 0%{?suse_version} >= 1210 %if 0%{?suse_version} >= 1210
BuildRequires: systemd BuildRequires: systemd
%{?systemd_requires} %{?systemd_requires}
%endif %endif
#for testing
BuildRequires: python-xml
BuildRequires: python-unittest2
BuildRequires: python-salt-testing
BuildRequires: python-mock
BuildRequires: python-pip
Requires: logrotate
Requires: python-Jinja2
Requires: python-PyYAML
Requires: python-xml
Requires(pre): %fillup_prereq
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq
%endif
BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRoot: %{_tmppath}/%{name}-%{version}-build
%if 0%{?suse_version} && 0%{?suse_version} <= 1110 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
@ -61,16 +73,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildArch: noarch BuildArch: noarch
%endif %endif
## Disabled for now python-mock issues
%if 0%{?suse_version} != 1220 && 0%{?suse_version} != 1230
BuildRequires: python-unittest2
BuildRequires: python-salt-testing
BuildRequires: python-xml
BuildRequires: python-mock
BuildRequires: python-pip
BuildRequires: git
%endif
%description %description
Salt is a distributed remote execution system used to execute commands and Salt is a distributed remote execution system used to execute commands and
query data. It was developed in order to bring the best solutions found in query data. It was developed in order to bring the best solutions found in
@ -83,8 +85,7 @@ servers, handle them quickly and through a simple and manageable interface.
Summary: Management component for salt, a parallel remote execution system Summary: Management component for salt, a parallel remote execution system
Group: System/Monitoring Group: System/Monitoring
Requires: %{name} = %{version} Requires: %{name} = %{version}
Requires: zeromq >= 3.2 Requires: python-pyzmq
Requires: python-pyzmq >= 2.10
Requires: python-M2Crypto Requires: python-M2Crypto
Requires: python-msgpack-python Requires: python-msgpack-python
Requires: python-pycrypto Requires: python-pycrypto
@ -96,8 +97,11 @@ Requires: dmidecode
%endif %endif
%endif %endif
Recommends: python-halite Recommends: python-halite
Requires(pre): %fillup_prereq %if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description master %description master
The Salt master is the central server to which all minions connect. The Salt master is the central server to which all minions connect.
@ -108,13 +112,14 @@ than serially.
Summary: Client component for salt, a parallel remote execution system Summary: Client component for salt, a parallel remote execution system
Group: System/Monitoring Group: System/Monitoring
Requires: %{name} = %{version} Requires: %{name} = %{version}
Requires: zeromq >= 3.2 Requires: python-pyzmq
Requires: python-pyzmq >= 2.10
Requires: python-M2Crypto Requires: python-M2Crypto
Requires: python-msgpack-python Requires: python-msgpack-python
Requires: python-pycrypto Requires: python-pycrypto
Requires(pre): %fillup_prereq %if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description minion %description minion
Salt minion is queried and controlled from the master. Salt minion is queried and controlled from the master.
@ -125,8 +130,10 @@ Summary: Syndic component for salt, a parallel remote execution system
Group: System/Monitoring Group: System/Monitoring
Requires: %{name} = %{version} Requires: %{name} = %{version}
Requires: %{name}-master = %{version} Requires: %{name}-master = %{version}
Requires(pre): %fillup_prereq %if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description syndic %description syndic
Salt syndic is the master-of-masters for salt Salt syndic is the master-of-masters for salt
@ -137,9 +144,13 @@ the management of multiple masters at a time..
Summary: Ssh component for salt, a parallel remote execution system Summary: Ssh component for salt, a parallel remote execution system
Group: System/Monitoring Group: System/Monitoring
Requires: %{name} = %{version} Requires: %{name} = %{version}
Requires: sshpass BuildRequires: python-markupsafe
Requires(pre): %fillup_prereq Requires: python-markupsafe
Recommends: sshpass
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description ssh %description ssh
Salt ssh is a master running without zmq. Salt ssh is a master running without zmq.
@ -154,10 +165,12 @@ python setup.py build
%install %install
python setup.py install --prefix=%{_prefix} --root=%{buildroot} python setup.py install --prefix=%{_prefix} --root=%{buildroot}
##missing directories ## create missing directories
mkdir -p %{buildroot}%{_sysconfdir}/salt/master.d mkdir -p %{buildroot}%{_sysconfdir}/salt/master.d
mkdir -p %{buildroot}%{_sysconfdir}/salt/minion.d mkdir -p %{buildroot}%{_sysconfdir}/salt/minion.d
%if 0%{?suse_version} < 1210
mkdir -p %{buildroot}%{_sysconfdir}/init.d mkdir -p %{buildroot}%{_sysconfdir}/init.d
%endif
mkdir -p %{buildroot}%{_localstatedir}/log/salt mkdir -p %{buildroot}%{_localstatedir}/log/salt
mkdir -p %{buildroot}/%{_sysconfdir}/logrotate.d/ mkdir -p %{buildroot}/%{_sysconfdir}/logrotate.d/
mkdir -p %{buildroot}/%{_sbindir} mkdir -p %{buildroot}/%{_sbindir}
@ -165,92 +178,102 @@ mkdir -p %{buildroot}/var/log/salt
mkdir -p %{buildroot}/srv/salt mkdir -p %{buildroot}/srv/salt
mkdir -p %{buildroot}/srv/pillar mkdir -p %{buildroot}/srv/pillar
# #
##init scripts ## install init scripts
%if 0%{?_unitdir:1}
install -Dpm 0644 %{SOURCE4} %{buildroot}%_unitdir/salt-master.service
install -Dpm 0644 %{SOURCE5} %{buildroot}%_unitdir/salt-syndic.service
install -Dpm 0644 %{SOURCE6} %{buildroot}%_unitdir/salt-minion.service
%else
install -Dpm 0755 %{SOURCE1} %{buildroot}%{_initddir}/salt-master install -Dpm 0755 %{SOURCE1} %{buildroot}%{_initddir}/salt-master
install -Dpm 0755 %{SOURCE2} %{buildroot}%{_initddir}/salt-syndic install -Dpm 0755 %{SOURCE2} %{buildroot}%{_initddir}/salt-syndic
install -Dpm 0755 %{SOURCE3} %{buildroot}%{_initddir}/salt-minion install -Dpm 0755 %{SOURCE3} %{buildroot}%{_initddir}/salt-minion
ln -sf %{_initddir}/salt-master %{buildroot}%{_sbindir}/rcsalt-master ln -sf %{_initddir}/salt-master %{buildroot}%{_sbindir}/rcsalt-master
ln -sf %{_initddir}/salt-syndic %{buildroot}%{_sbindir}/rcsalt-syndic ln -sf %{_initddir}/salt-syndic %{buildroot}%{_sbindir}/rcsalt-syndic
ln -sf %{_initddir}/salt-minion %{buildroot}%{_sbindir}/rcsalt-minion ln -sf %{_initddir}/salt-minion %{buildroot}%{_sbindir}/rcsalt-minion
%if 0%{?_unitdir:1}
install -Dpm 0644 %{SOURCE4} %{buildroot}%_unitdir/salt-master.service
install -Dpm 0644 %{SOURCE5} %{buildroot}%_unitdir/salt-syndic.service
install -Dpm 0644 %{SOURCE6} %{buildroot}%_unitdir/salt-minion.service
%endif %endif
# #
##config files ## install config files
install -Dpm 0644 conf/minion %{buildroot}%{_sysconfdir}/salt/minion install -Dpm 0644 conf/minion %{buildroot}%{_sysconfdir}/salt/minion
install -Dpm 0644 conf/master %{buildroot}%{_sysconfdir}/salt/master install -Dpm 0644 conf/master %{buildroot}%{_sysconfdir}/salt/master
# #
##logrotate file ## install logrotate file
install -Dpm 0644 %{SOURCE7} %{buildroot}%{_sysconfdir}/logrotate.d/salt install -Dpm 0644 %{SOURCE7} %{buildroot}%{_sysconfdir}/logrotate.d/salt
# #
##SuSEfirewall2 file ## install SuSEfirewall2 rules
install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/salt install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/salt
%if 0%{?suse_version} != 1220 && 0%{?suse_version} != 1230
%check %check
#export only_local_network=False
%{__python} setup.py test --runtests-opts=-u %{__python} setup.py test --runtests-opts=-u
%endif
%preun -n salt-syndic %preun -n salt-syndic
%stop_on_removal salt-syndic
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_preun salt-syndic.service %service_del_preun salt-syndic.service
%else
%stop_on_removal salt-syndic
%endif %endif
%post -n salt-syndic %post -n salt-syndic
%fillup_and_insserv
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_add_post salt-syndic.service %service_add_post salt-syndic.service
%fillup_only
%else
%fillup_and_insserv
%endif %endif
%postun -n salt-syndic %postun -n salt-syndic
%restart_on_update salt-syndic
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_postun salt-syndic.service %service_del_postun salt-syndic.service
%endif %else
%insserv_cleanup %insserv_cleanup
%restart_on_update salt-syndic
%endif
%preun -n salt-master %preun -n salt-master
%stop_on_removal salt-master
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_preun salt-master.service %service_del_preun salt-master.service
%else
%stop_on_removal salt-master
%endif %endif
%post -n salt-master %post -n salt-master
%fillup_and_insserv
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_add_post salt-master.service %service_add_post salt-master.service
%fillup_only
%else
%fillup_and_insserv
%endif %endif
%postun -n salt-master %postun -n salt-master
%restart_on_update salt-master
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_postun salt-master.service %service_del_postun salt-master.service
%endif %else
%restart_on_update salt-master
%insserv_cleanup %insserv_cleanup
%endif
%preun -n salt-minion %preun -n salt-minion
%stop_on_removal salt-minion
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_preun salt-minion.service %service_del_preun salt-minion.service
%else
%stop_on_removal salt-minion
%endif %endif
%post -n salt-minion %post -n salt-minion
%fillup_and_insserv
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_add_post salt-minion.service %service_add_post salt-minion.service
%fillup_only
%else
%fillup_and_insserv
%endif %endif
%postun -n salt-minion %postun -n salt-minion
%restart_on_update salt-minion
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%service_del_postun salt-minion.service %service_del_postun salt-minion.service
%endif %else
%insserv_cleanup %insserv_cleanup
%restart_on_update salt-minion
%endif
%files -n salt-ssh %files -n salt-ssh
%defattr(-,root,root) %defattr(-,root,root)
@ -261,22 +284,24 @@ install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2
%defattr(-,root,root) %defattr(-,root,root)
%{_bindir}/salt-syndic %{_bindir}/salt-syndic
%{_mandir}/man1/salt-syndic.1.gz %{_mandir}/man1/salt-syndic.1.gz
%{_sbindir}/rcsalt-syndic
%{_sysconfdir}/init.d/salt-syndic
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%_unitdir/salt-syndic.service %_unitdir/salt-syndic.service
%else
%{_sbindir}/rcsalt-syndic
%{_sysconfdir}/init.d/salt-syndic
%endif %endif
%files -n salt-minion %files -n salt-minion
%defattr(-,root,root) %defattr(-,root,root)
%{_bindir}/salt-minion %{_bindir}/salt-minion
%{_mandir}/man1/salt-minion.1.gz %{_mandir}/man1/salt-minion.1.gz
%{_sbindir}/rcsalt-minion
%config(noreplace) %{_sysconfdir}/init.d/salt-minion
%attr(0644, root, root) %config(noreplace) %{_sysconfdir}/salt/minion %attr(0644, root, root) %config(noreplace) %{_sysconfdir}/salt/minion
%{_sysconfdir}/salt/minion.d %{_sysconfdir}/salt/minion.d
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%_unitdir/salt-minion.service %_unitdir/salt-minion.service
%else
%{_sbindir}/rcsalt-minion
%config(noreplace) %{_sysconfdir}/init.d/salt-minion
%endif %endif
%files -n salt-master %files -n salt-master
@ -291,8 +316,6 @@ install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2
%{_mandir}/man1/salt-cp.1.gz %{_mandir}/man1/salt-cp.1.gz
%{_mandir}/man1/salt-key.1.gz %{_mandir}/man1/salt-key.1.gz
%{_mandir}/man1/salt-run.1.gz %{_mandir}/man1/salt-run.1.gz
%{_sbindir}/rcsalt-master
%config(noreplace) %{_sysconfdir}/init.d/salt-master
%config(noreplace) %{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/salt %config(noreplace) %{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/salt
%attr(0644, root, root) %config(noreplace) %{_sysconfdir}/salt/master %attr(0644, root, root) %config(noreplace) %{_sysconfdir}/salt/master
%{_sysconfdir}/salt/master.d %{_sysconfdir}/salt/master.d
@ -300,6 +323,9 @@ install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2
%dir /srv/pillar %dir /srv/pillar
%if 0%{?_unitdir:1} %if 0%{?_unitdir:1}
%_unitdir/salt-master.service %_unitdir/salt-master.service
%else
%{_sbindir}/rcsalt-master
%config(noreplace) %{_sysconfdir}/init.d/salt-master
%endif %endif
%files %files

View File

@ -170,7 +170,7 @@ class SSH(object):
if deploy.startswith(('n', 'N')): if deploy.startswith(('n', 'N')):
return ret return ret
target['passwd'] = getpass.getpass( target['passwd'] = getpass.getpass(
'Password for {0}@{1}:'.format(host, target['user']) 'Password for {0}@{1}:'.format(target['user'], host)
) )
return self._key_deploy_run(host, target, True) return self._key_deploy_run(host, target, True)
return ret return ret

View File

@ -664,7 +664,7 @@ def syndic_config(master_config_path,
return opts return opts
def get_id(root_dir=None): def get_id(root_dir=None, minion_id=False):
''' '''
Guess the id of the minion. Guess the id of the minion.
@ -704,6 +704,7 @@ def get_id(root_dir=None):
fqdn = socket.getfqdn() fqdn = socket.getfqdn()
if fqdn != 'localhost': if fqdn != 'localhost':
log.info('Found minion id from getfqdn(): {0}'.format(fqdn)) log.info('Found minion id from getfqdn(): {0}'.format(fqdn))
if minion_id:
try: try:
with salt.utils.fopen(id_cache, 'w') as idf: with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(fqdn) idf.write(fqdn)
@ -720,6 +721,7 @@ def get_id(root_dir=None):
'This file should not contain any whitespace.') 'This file should not contain any whitespace.')
else: else:
if name != 'localhost': if name != 'localhost':
if minion_id:
try: try:
with salt.utils.fopen(id_cache, 'w') as idf: with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name) idf.write(name)
@ -740,6 +742,7 @@ def get_id(root_dir=None):
if name != 'localhost': if name != 'localhost':
log.info('Found minion id in hosts file: {0}' log.info('Found minion id in hosts file: {0}'
.format(name)) .format(name))
if minion_id:
try: try:
with salt.utils.fopen(id_cache, 'w') as idf: with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name) idf.write(name)
@ -766,6 +769,7 @@ def get_id(root_dir=None):
if name != 'localhost': if name != 'localhost':
log.info('Found minion id in hosts file: {0}' log.info('Found minion id in hosts file: {0}'
.format(name)) .format(name))
if minion_id:
try: try:
with salt.utils.fopen(id_cache, 'w') as idf: with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name) idf.write(name)
@ -830,8 +834,9 @@ def apply_minion_config(overrides=None,
# No ID provided. Will getfqdn save us? # No ID provided. Will getfqdn save us?
using_ip_for_id = False using_ip_for_id = False
if opts['id'] is None and minion_id: if opts['id'] is None:
opts['id'], using_ip_for_id = get_id(opts['root_dir']) opts['id'], using_ip_for_id = get_id(opts['root_dir'],
minion_id=minion_id)
# it does not make sense to append a domain to an IP based id # it does not make sense to append a domain to an IP based id
if not using_ip_for_id and 'append_domain' in opts: if not using_ip_for_id and 'append_domain' in opts:

View File

@ -142,3 +142,42 @@ def inodeusage(args=None):
log.warn("Problem parsing inode usage information") log.warn("Problem parsing inode usage information")
ret = {} ret = {}
return ret return ret
def percent(args=None):
'''
Return partion information for volumes mounted on this minion
CLI Example::
salt '*' disk.percent /var
'''
if __grains__['kernel'] == 'Linux':
cmd = 'df -P'
elif __grains__['kernel'] == 'OpenBSD':
cmd = 'df -kP'
else:
cmd = 'df'
ret = {}
out = __salt__['cmd.run'](cmd).splitlines()
for line in out:
if not line:
continue
if line.startswith('Filesystem'):
continue
comps = line.split()
while not comps[1].isdigit():
comps[0] = '{0} {1}'.format(comps[0], comps[1])
comps.pop(1)
try:
if __grains__['kernel'] == 'Darwin':
ret[comps[8]] = comps[4]
else:
ret[comps[5]] = comps[4]
except IndexError:
log.warn("Problem parsing disk usage information")
ret = {}
if args:
return ret[args]
else:
return ret

View File

@ -40,6 +40,7 @@ except ImportError:
import salt.utils import salt.utils
import salt.utils.find import salt.utils.find
import salt.utils.filebuffer import salt.utils.filebuffer
import salt.utils.atomicfile
from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.exceptions import CommandExecutionError, SaltInvocationError
import salt._compat import salt._compat
@ -989,6 +990,154 @@ def replace(path,
return has_changes return has_changes
def blockreplace(path,
marker_start='#-- start managed zone --',
marker_end='#-- end managed zone --',
content='',
append_if_not_found=False,
backup='.bak',
dry_run=False,
show_changes=True,
):
'''
Replace content of a text block in a file, delimited by line markers
.. versionadded:: 0.18.0
A block of content delimited by comments can help you manage several lines entries without
worrying about old entries removal.
Note: this function will store two copies of the file in-memory
(the original version and the edited version) in order to detect changes
and only edit the targeted file if necessary.
:param path: Filesystem path to the file to be edited
:param marker_start: The line content identifying a line as the start of
the content block. Note that the whole line containing this marker will
be considered, so whitespaces or extra content before or after the
marker is included in final output
:param marker_end: The line content identifying a line as the end of
the content block. Note that the whole line containing this marker will
be considered, so whitespaces or extra content before or after the
marker is included in final output
:param content: The content to be used between the two lines identified by
marker_start and marker_stop.
:param append_if_not_found: False by default, if markers are not found and
set to True then the markers and content will be appended to the file
:param backup: The file extension to use for a backup of the file if any
edit is made. Set to ``False`` to skip making a backup.
:param dry_run: Don't make any edits to the file
:param show_changes: Output a unified diff of the old file and the new
file. If ``False`` return a boolean if any changes were made.
:rtype: bool or str
CLI Example:
.. code-block:: bash
salt '*' file.blockreplace /etc/hosts '#-- start managed zone foobar : DO NOT EDIT --' \
'#-- end managed zone foobar --' $'10.0.1.1 foo.foobar\n10.0.1.2 bar.foobar' True
'''
if not os.path.exists(path):
raise SaltInvocationError("File not found: %s", path)
if not salt.utils.istextfile(path):
raise SaltInvocationError(
"Cannot perform string replacements on a binary file: %s", path)
# Search the file; track if any changes have been made for the return val
has_changes = False
orig_file = []
new_file = []
in_block = False
old_content = ''
done = False
# we do not use in_place editing to avoid file attrs modifications when
# no changes are required and to avoid any file access on a partially
# written file.
# we could also use salt.utils.filebuffer.BufferedReader
for line in fileinput.input(path,
inplace=False, backup=False,
bufsize=1, mode='rb'):
result = line
if marker_start in line:
# managed block start found, start recording
in_block = True
else:
if in_block:
if marker_end in line:
# end of block detected
in_block = False
# push new block content in file
for cline in content.split("\n"):
new_file.append(cline+"\n")
done = True
else:
# remove old content, but keep a trace
old_content += line
result = None
# else: we are not in the marked block, keep saving things
orig_file.append(line)
if result is not None:
new_file.append(result)
# end for. If we are here without block managment we maybe have some problems,
# or we need to initialise the marked block
if in_block:
# unterminated block => bad, always fail
raise CommandExecutionError("Unterminated marked block. End of file reached before marker_end.")
if not done:
if append_if_not_found:
# add the markers and content at the end of file
new_file.append(marker_start + '\n')
new_file.append(content + '\n')
new_file.append(marker_end + '\n')
done = True
else:
raise CommandExecutionError("Cannot edit marked block. Markers were not found in file.")
if done:
diff = ''.join(difflib.unified_diff(orig_file, new_file))
has_changes = diff is not ''
if has_changes and not dry_run:
# changes detected
# backup old content
if backup is not False:
shutil.copy2(path, '{0}{1}'.format(path, backup))
# backup file attrs
perms = {}
perms['user'] = get_user(path)
perms['group'] = get_group(path)
perms['mode'] = __salt__['config.manage_mode'](get_mode(path))
# write new content in the file while avoiding partial reads
f = salt.utils.atomicfile.atomic_open(path, 'wb')
for line in new_file:
f.write(line)
f.close()
# this may have overwritten file attrs
check_perms(path,
None,
perms['user'],
perms['group'],
perms['mode'])
if show_changes:
return diff
return has_changes
def search(path, def search(path,
pattern, pattern,
flags=0, flags=0,

View File

@ -34,13 +34,11 @@ import time
import logging import logging
import re import re
import sys import sys
import shlex
# Import salt libs # Import salt libs
import salt.utils import salt.utils
#import shlex which should be distributed with Python
import shlex
# Import third party libs # Import third party libs
try: try:
import MySQLdb import MySQLdb

View File

@ -428,7 +428,7 @@ def list_(profile=None):
def server_list(profile=None): def server_list(profile=None):
''' '''
Return detailed information for an active server Return list of active servers
CLI Example: CLI Example:
@ -443,6 +443,12 @@ def server_list(profile=None):
'id': item.id, 'id': item.id,
'name': item.name, 'name': item.name,
'status': item.status, 'status': item.status,
'accessIPv4': item.accessIPv4,
'accessIPv6': item.accessIPv6,
'flavor': {'id': item.flavor['id'],
'links': item.flavor['links']},
'image': {'id': item.image['id'],
'links': item.image['links']},
} }
return ret return ret
@ -455,20 +461,19 @@ def show(server_id, profile=None):
return server_show(server_id, profile) return server_show(server_id, profile)
def server_show(server_id, profile=None): def server_list_detailed(profile=None):
''' '''
Return detailed information for an active server Return detailed list of active servers
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
salt '*' nova.show salt '*' nova.server_list_detailed
''' '''
nt_ks = _auth(profile) nt_ks = _auth(profile)
ret = {} ret = {}
for item in nt_ks.servers.list(): for item in nt_ks.servers.list():
if item.id == server_id:
ret[item.name] = { ret[item.name] = {
'OS-EXT-SRV-ATTR': {}, 'OS-EXT-SRV-ATTR': {},
'OS-EXT-STS': {}, 'OS-EXT-STS': {},
@ -488,7 +493,6 @@ def server_show(server_id, profile=None):
'metadata': item.metadata, 'metadata': item.metadata,
'name': item.name, 'name': item.name,
'progress': item.progress, 'progress': item.progress,
'security_groups': item.security_groups,
'status': item.status, 'status': item.status,
'tenant_id': item.tenant_id, 'tenant_id': item.tenant_id,
'updated': item.updated, 'updated': item.updated,
@ -510,6 +514,27 @@ def server_show(server_id, profile=None):
ret[item.name]['OS-EXT-STS']['task_state'] = item.__dict__['OS-EXT-STS:task_state'] ret[item.name]['OS-EXT-STS']['task_state'] = item.__dict__['OS-EXT-STS:task_state']
if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'): if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'):
ret[item.name]['OS-EXT-STS']['vm_state'] = item.__dict__['OS-EXT-STS:vm_state'] ret[item.name]['OS-EXT-STS']['vm_state'] = item.__dict__['OS-EXT-STS:vm_state']
if hasattr(item.__dict__, 'security_groups'):
ret[item.name]['security_groups'] = item.__dict__['security_groups']
return ret
def server_show(server_id, profile=None):
'''
Return detailed information for an active server
CLI Example:
.. code-block:: bash
salt '*' nova.server_show <server_id>
'''
ret = {}
server_list = server_list_detailed(profile)
for item in server_list:
id_ = server_list[item]['id']
if str(id_) == server_id:
ret[server_list[item]['name']] = server_list[item]
return ret return ret

View File

@ -267,13 +267,13 @@ def do(cmdline=None, runas=None):
.. code-block:: bash .. code-block:: bash
salt '*' rbenv.do "gem list bundler" salt '*' rbenv.do 'gem list bundler'
salt '*' rbenv.do "gem list bundler" deploy salt '*' rbenv.do 'gem list bundler' deploy
''' '''
path = _rbenv_path(runas) path = _rbenv_path(runas)
result = __salt__['cmd.run_all']( result = __salt__['cmd.run_all'](
"env PATH={0}/shims:$PATH {1}".format(path, cmdline), 'env PATH={0}/shims:$PATH {1}'.format(path, cmdline),
runas=runas runas=runas
) )

View File

@ -60,6 +60,12 @@ def __virtual__():
# Disable on these platforms, specific service modules exist: # Disable on these platforms, specific service modules exist:
if __grains__['os'] in ('Ubuntu', 'Linaro', 'elementary OS'): if __grains__['os'] in ('Ubuntu', 'Linaro', 'elementary OS'):
return __virtualname__ return __virtualname__
elif __grains__['os'] in ('Debian', 'Raspbian'):
debian_initctl = '/sbin/initctl'
if os.path.isfile(debian_initctl):
initctl_version = salt.modules.cmdmod._run_quiet(debian_initctl + ' version')
if 'upstart' in initctl_version:
return 'service'
return False return False

View File

@ -129,8 +129,11 @@ def ext_pillar(pillar,
if not os.path.isdir(path): if not os.path.isdir(path):
log.error('Virtualenv {} not a directory!'.format(path)) log.error('Virtualenv {} not a directory!'.format(path))
return {} return {}
# load the virtualenv # load the virtualenv first
sys.path[0:0] = (virtualenv.path_locations(env)[1] + '/site-packages/',) sys.path.insert(0,
os.path.join(
virtualenv.path_locations(env)[1],
'site-packages'))
# load the django project # load the django project
sys.path.append(project_path) sys.path.append(project_path)

View File

@ -18,7 +18,7 @@ import salt.key
import salt.client import salt.client
import salt.output import salt.output
FINGERPRINT_REGEX = re.compile(r"[a-f0-9]{2}:"*15 + r"[a-f0-9]{2}") FINGERPRINT_REGEX = re.compile(r'^([a-f0-9]{2}:){15}([a-f0-9]{2})$')
def status(output=True): def status(output=True):

View File

@ -1776,6 +1776,121 @@ def replace(name,
return ret return ret
def blockreplace(name,
marker_start='#-- start managed zone --',
marker_end='#-- end managed zone --',
content='',
append_if_not_found=False,
backup='.bak',
show_changes=True):
'''
Maintain an edit in a file in a zone delimited by two line markers
.. versionadded:: 0.18.0
A block of content delimited by comments can help you manage several lines
entries without worrying about old entries removal. This can help you maintaining
an unmanaged file containing manual edits.
Note: this function will store two copies of the file in-memory
(the original version and the edited version) in order to detect changes
and only edit the targeted file if necessary.
:param name: Filesystem path to the file to be edited
:param marker_start: The line content identifying a line as the start of
the content block. Note that the whole line containing this marker will
be considered, so whitespaces or extra content before or after the
marker is included in final output
:param marker_end: The line content identifying a line as the end of
the content block. Note that the whole line containing this marker will
be considered, so whitespaces or extra content before or after the
marker is included in final output.
Note: you can use file.accumulated and target this state. All accumulated
datas dictionnaries content will be added as new lines in the content.
:param content: The content to be used between the two lines identified by
marker_start and marker_stop.
:param append_if_not_found: False by default, if markers are not found and
set to True then the markers and content will be appended to the file
:param backup: The file extension to use for a backup of the file if any
edit is made. Set to ``False`` to skip making a backup.
:param dry_run: Don't make any edits to the file
:param show_changes: Output a unified diff of the old file and the new
file. If ``False`` return a boolean if any changes were made.
:rtype: bool or str
Exemple of usage with an accumulator and with a variable::
{% set myvar = 42 %}
hosts-config-block-{{ myvar }}:
file.blockreplace:
- name: /etc/hosts
- marker_start: "# START managed zone {{ myvar }} -DO-NOT-EDIT-"
- marker_end: "# END managed zone {{ myvar }} --"
- content: 'First line of content'
- append_if_not_found: True
- backup: '.bak'
- show_changes: True
hosts-config-block-{{ myvar }}-accumulated1:
file.accumulated:
- filename: /etc/hosts
- name: my-accumulator-{{ myvar }}
- text: "text 2"
- require_in:
- file: foobar-config-block-{{ myvar }}
hosts-config-block-{{ myvar }}-accumulated2:
file.accumulated:
- filename: /etc/hosts
- name: my-accumulator-{{ myvar }}
- text: |
text 3
text 4
- require_in:
- file: foobar-config-block-{{ myvar }}
will generate and maintain a block of content in ``/etc/hosts``::
# START managed zone 42 -DO-NOT-EDIT-
First line of content
text 2
text 3
text 4
# END managed zone 42 --
'''
ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
check_res, check_msg = _check_file(name)
if not check_res:
return _error(ret, check_msg)
if name in _ACCUMULATORS:
accumulator = _ACCUMULATORS[name]
for acc, acc_content in accumulator.iteritems():
for line in acc_content:
if content == '':
content = line
else:
content += "\n" + line
changes = __salt__['file.blockreplace'](name,
marker_start,
marker_end,
content=content,
append_if_not_found=append_if_not_found,
backup=backup,
dry_run=__opts__['test'],
show_changes=show_changes)
if changes:
ret['changes'] = changes
ret['comment'] = 'Changes were made'
else:
ret['comment'] = 'No changes were made'
ret['result'] = True
return ret
def sed(name, def sed(name,
before, before,
after, after,
@ -2548,7 +2663,8 @@ def rename(name, source, force=False, makedirs=False):
def accumulated(name, filename, text, **kwargs): def accumulated(name, filename, text, **kwargs):
''' '''
Prepare accumulator which can be used in template in file.managed state. Prepare accumulator which can be used in template in file.managed state.
Accumulator dictionary becomes available in template. Accumulator dictionary becomes available in template. It can also be used
in file.blockreplace.
name name
Accumulator name Accumulator name

View File

@ -3,7 +3,7 @@
Control of entries in SSH authorized_key files. Control of entries in SSH authorized_key files.
=============================================== ===============================================
The information stored in a user's ssh authorized key file can be easily The information stored in a user's SSH authorized key file can be easily
controlled via the ssh_auth state. Defaults can be set by the enc, options, controlled via the ssh_auth state. Defaults can be set by the enc, options,
and comment keys. These defaults can be overridden by including them in the and comment keys. These defaults can be overridden by including them in the
name. name.
@ -116,19 +116,19 @@ def present(
config='.ssh/authorized_keys', config='.ssh/authorized_keys',
**kwargs): **kwargs):
''' '''
Verifies that the specified ssh key is present for the specified user Verifies that the specified SSH key is present for the specified user
name name
The ssh key to manage The SSH key to manage
user user
The user who owns the ssh authorized keys file to modify The user who owns the SSH authorized keys file to modify
enc enc
Defines what type of key is being used, can be ecdsa ssh-rsa, ssh-dss Defines what type of key is being used; can be ecdsa, ssh-rsa or ssh-dss
comment comment
The comment to be placed with the ssh public key The comment to be placed with the SSH public key
source source
The source file for the key(s). Can contain any number of public keys, The source file for the key(s). Can contain any number of public keys,
@ -237,15 +237,29 @@ def present(
return ret return ret
def absent(name, user, config='.ssh/authorized_keys'): def absent(name,
user,
enc='ssh-rsa',
comment='',
options=None,
config='.ssh/authorized_keys'):
''' '''
Verifies that the specified ssh key is absent Verifies that the specified SSH key is absent
name name
The ssh key to manage The SSH key to manage
user user
The user who owns the ssh authorized keys file to modify The user who owns the SSH authorized keys file to modify
enc
Defines what type of key is being used; can be ecdsa, ssh-rsa or ssh-dss
comment
The comment to be placed with the SSH public key
options
The options passed to the key, pass a list object
config config
The location of the authorized keys file relative to the user's home The location of the authorized keys file relative to the user's home
@ -263,9 +277,9 @@ def absent(name, user, config='.ssh/authorized_keys'):
check = __salt__['ssh.check_key']( check = __salt__['ssh.check_key'](
user, user,
name, name,
'', enc,
'', comment,
[], options or [],
config) config)
if check == 'update' or check == 'exists': if check == 'update' or check == 'exists':
ret['result'] = None ret['result'] = None

View File

@ -13,7 +13,8 @@ def mac(addr):
''' '''
valid = re.compile(r''' valid = re.compile(r'''
(^([0-9A-F]{1,2}[-]){5}([0-9A-F]{1,2})$ (^([0-9A-F]{1,2}[-]){5}([0-9A-F]{1,2})$
|^([0-9A-F]{1,2}[:]){5}([0-9A-F]{1,2})$) |^([0-9A-F]{1,2}[:]){5}([0-9A-F]{1,2})$
|^([0-9A-F]{1,2}[.]){5}([0-9A-F]{1,2})$)
''', ''',
re.VERBOSE | re.IGNORECASE) re.VERBOSE | re.IGNORECASE)
return valid.match(addr) is not None return valid.match(addr) is not None

View File

@ -6,17 +6,20 @@ import textwrap
# Import Salt Testing libs # Import Salt Testing libs
from salttesting import TestCase from salttesting import TestCase
from salttesting.helpers import ensure_in_syspath from salttesting.helpers import ensure_in_syspath
from salttesting.mock import MagicMock
ensure_in_syspath('../../') ensure_in_syspath('../../')
# Import Salt libs # Import Salt libs
from salt.modules import file as filemod from salt.modules import file as filemod
from salt.modules import cmdmod from salt.modules import cmdmod
from salt.exceptions import CommandExecutionError, SaltInvocationError
filemod.__salt__ = { filemod.__salt__ = {
'cmd.run': cmdmod.run, 'cmd.run': cmdmod.run,
'cmd.run_all': cmdmod.run_all 'cmd.run_all': cmdmod.run_all
} }
filemod.__opts__ = {'test': False}
SED_CONTENT = """test SED_CONTENT = """test
some some
@ -101,6 +104,140 @@ class FileReplaceTestCase(TestCase):
def test_re_int_flags(self): def test_re_int_flags(self):
filemod.replace(self.tfile.name, r'Etiam', 'Salticus', flags=10) filemod.replace(self.tfile.name, r'Etiam', 'Salticus', flags=10)
class FileBlockReplaceTestCase(TestCase):
MULTILINE_STRING = textwrap.dedent('''\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus
enim ac bibendum vulputate. Etiam nibh velit, placerat ac auctor in,
lacinia a turpis. Nulla elit elit, ornare in sodales eu, aliquam sit
amet nisl.
Fusce ac vehicula lectus. Vivamus justo nunc, pulvinar in ornare nec,
sollicitudin id sem. Pellentesque sed ipsum dapibus, dapibus elit id,
malesuada nisi.
first part of start line // START BLOCK : part of start line not removed
to be removed
first part of end line // END BLOCK : part of end line not removed
#-- START BLOCK UNFINISHED
#-- START BLOCK 1
old content part 1
old content part 2
#-- END BLOCK 1
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
venenatis tellus eget massa facilisis, in auctor ante aliquet. Sed nec
cursus metus. Curabitur massa urna, vehicula id porttitor sed, lobortis
quis leo.
''')
def setUp(self):
self.tfile = tempfile.NamedTemporaryFile(delete=False,prefix='blockrepltmp')
self.tfile.write(self.MULTILINE_STRING)
self.tfile.close()
manage_mode_mock = MagicMock()
filemod.__salt__['config.manage_mode'] = manage_mode_mock
def tearDown(self):
os.remove(self.tfile.name)
def test_replace_multiline(self):
new_multiline_content = "Who's that then?\nWell, how'd you become king, then?\nWe found them. I'm not a witch.\nWe shall say 'Ni' again to you, if you do not appease us."
filemod.blockreplace(self.tfile.name, '#-- START BLOCK 1', '#-- END BLOCK 1', new_multiline_content, backup=False)
with open(self.tfile.name, 'rb') as fp:
filecontent=fp.read()
self.assertIn('#-- START BLOCK 1'+"\n"+new_multiline_content+"\n"+'#-- END BLOCK 1', filecontent)
self.assertNotIn('old content part 1', filecontent)
self.assertNotIn('old content part 2', filecontent)
def test_replace_append(self):
new_content = "Well, I didn't vote for you."
self.assertRaises(
CommandExecutionError,
filemod.blockreplace,
self.tfile.name,
'#-- START BLOCK 2',
'#-- END BLOCK 2',
new_content,
append_if_not_found=False,
backup=False
)
with open(self.tfile.name, 'rb') as fp:
self.assertNotIn('#-- START BLOCK 2'+"\n"+new_content+"\n"+'#-- END BLOCK 2', fp.read())
filemod.blockreplace(self.tfile.name, '#-- START BLOCK 2', '#-- END BLOCK 2', new_content, backup=False,append_if_not_found=True)
with open(self.tfile.name, 'rb') as fp:
self.assertIn('#-- START BLOCK 2'+"\n"+new_content+"\n"+'#-- END BLOCK 2', fp.read())
def test_replace_partial_marked_lines(self):
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 1', backup=False)
with open(self.tfile.name, 'rb') as fp:
filecontent=fp.read()
self.assertIn('new content 1', filecontent)
self.assertNotIn('to be removed', filecontent)
self.assertIn('first part of start line', filecontent)
self.assertIn('first part of end line', filecontent)
self.assertIn('part of start line not removed', filecontent)
self.assertIn('part of end line not removed', filecontent)
def test_backup(self):
fext = '.bak'
bak_file = '{0}{1}'.format(self.tfile.name, fext)
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 2', backup=fext)
self.assertTrue(os.path.exists(bak_file))
os.unlink(bak_file)
self.assertFalse(os.path.exists(bak_file))
fext = '.bak'
bak_file = '{0}{1}'.format(self.tfile.name, fext)
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 3', backup=False)
self.assertFalse(os.path.exists(bak_file))
def test_no_modifications(self):
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 4', backup=False)
before_ctime = os.stat(self.tfile.name).st_mtime
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 4', backup=False)
after_ctime = os.stat(self.tfile.name).st_mtime
self.assertEqual(before_ctime, after_ctime)
def test_dry_run(self):
before_ctime = os.stat(self.tfile.name).st_mtime
filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 5', dry_run=True)
after_ctime = os.stat(self.tfile.name).st_mtime
self.assertEqual(before_ctime, after_ctime)
def test_show_changes(self):
ret = filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 6', backup=False, show_changes=True)
self.assertTrue(ret.startswith('---')) # looks like a diff
ret = filemod.blockreplace(self.tfile.name, '// START BLOCK', '// END BLOCK', 'new content 7', backup=False, show_changes=False)
self.assertIsInstance(ret, bool)
def test_unfinished_block_exception(self):
self.assertRaises(
CommandExecutionError,
filemod.blockreplace,
self.tfile.name,
'#-- START BLOCK UNFINISHED',
'#-- END BLOCK UNFINISHED',
'foobar',
backup=False
)
class FileModuleTestCase(TestCase): class FileModuleTestCase(TestCase):
def test_sed_limit_escaped(self): def test_sed_limit_escaped(self):
@ -149,4 +286,4 @@ class FileModuleTestCase(TestCase):
if __name__ == '__main__': if __name__ == '__main__':
from integration import run_tests from integration import run_tests
run_tests(FileModuleTestCase, FileReplaceTestCase, needs_daemon=False) run_tests(FileModuleTestCase, FileReplaceTestCase, FileBlockReplaceTestCase, needs_daemon=False)