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
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

View File

@ -193,6 +193,8 @@ and run NSIS.
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
=======================

View File

@ -2,6 +2,13 @@
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
bugfixes, and a number of security updates.

View File

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

View File

@ -14,7 +14,8 @@
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# 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
@ -26,16 +27,6 @@
# 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
SUSE_RELEASE=/etc/SuSE-release
# Source function library.
@ -47,22 +38,30 @@ else
. /etc/rc.d/init.d/functions
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
PROCESS=salt-minion
CONFIG_ARGS=" "
RETVAL=0
start() {
echo -n $"Starting salt-minion daemon: "
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
elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then
echo -n "already started, lock file found"
RETVAL=1
elif $PYTHON $SALTMINION -d >& /dev/null; then
elif $PYTHON $SALTMINION -d $MINION_ARGS >& /dev/null; then
echo -n "OK"
RETVAL=0
fi
@ -71,7 +70,7 @@ start() {
RETVAL=$?
echo -n "already running"
else
daemon --check $SERVICE $SALTMINION -d $CONFIG_ARGS
daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
RETVAL=$?
fi
fi
@ -133,11 +132,7 @@ case "$1" in
;;
reload)
echo "can't reload configuration, you have to restart it"
if [ -f $SUSE_RELEASE ]; then
rc_status -v
else
RETVAL=$?
fi
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"

View File

@ -14,28 +14,19 @@
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# 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
# 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.
#
# 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
SUSE_RELEASE=/etc/SuSE-release
# Source function library.
@ -47,27 +38,35 @@ else
. /etc/rc.d/init.d/functions
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
PROCESS=salt-syndic
CONFIG_ARGS=" "
RETVAL=0
start() {
echo -n $"Starting salt-syndic daemon: "
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
elif [ -e $DEBIAN_VERSION ]; then
if [ -f $LOCKFILE ]; then
echo -n "already started, lock file found"
RETVAL=1
elif $PYTHON $SALTSYNDIC -d >& /dev/null; then
elif $PYTHON $SALTSYNDIC -d $SYNDIC_ARGS >& /dev/null; then
echo -n "OK"
RETVAL=0
fi
else
daemon --check $SERVICE $SALTSYNDIC -d $CONFIG_ARGS
daemon --check $SERVICE $SALTSYNDIC -d $SYNDIC_ARGS
fi
RETVAL=$?
echo
@ -123,18 +122,16 @@ case "$1" in
RETVAL=$?
fi
;;
condrestart)
[ -f $LOCKFILE ] && restart || :
;;
reload)
echo "can't reload configuration, you have to restart it"
if [ -f $SUSE_RELEASE ]; then
rc_status -v
else
RETVAL=$?
fi
;;
*)
echo $"Usage: $0 {start|stop|status|restart|reload}"
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
exit 1
;;
esac
exit $RETVAL

View File

@ -16,7 +16,7 @@
#
Name: salt
Version: 0.17.0
Version: 0.17.1
Release: 0
Summary: A parallel remote execution system
License: Apache-2.0
@ -31,10 +31,8 @@ Source5: %{name}-syndic.service
Source6: %{name}-minion.service
Source7: %{name}.logrotate
Source8: %{name}.SuSEfirewall2
%if 0%{?sles_version}
BuildRequires: python
Requires: python
%endif
#for building
BuildRequires: python-devel
BuildRequires: logrotate
BuildRequires: python-Jinja2
@ -42,18 +40,32 @@ BuildRequires: python-M2Crypto
BuildRequires: python-PyYAML
BuildRequires: python-msgpack-python
BuildRequires: python-pycrypto
BuildRequires: python-pyzmq >= 2.1.9
BuildRequires: unzip
Requires: logrotate
Requires: python-Jinja2
Requires: python-PyYAML
Requires: python-Sphinx
Requires(pre): %fillup_prereq
Requires(pre): %insserv_prereq
BuildRequires: python-pyzmq
%if 0%{?sles_version}
BuildRequires: python
Requires: python
%endif
%if 0%{?suse_version} >= 1210
BuildRequires: systemd
%{?systemd_requires}
%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
%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()")}
@ -61,16 +73,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildArch: noarch
%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
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
@ -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
Group: System/Monitoring
Requires: %{name} = %{version}
Requires: zeromq >= 3.2
Requires: python-pyzmq >= 2.10
Requires: python-pyzmq
Requires: python-M2Crypto
Requires: python-msgpack-python
Requires: python-pycrypto
@ -96,8 +97,11 @@ Requires: dmidecode
%endif
%endif
Recommends: python-halite
Requires(pre): %fillup_prereq
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description master
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
Group: System/Monitoring
Requires: %{name} = %{version}
Requires: zeromq >= 3.2
Requires: python-pyzmq >= 2.10
Requires: python-pyzmq
Requires: python-M2Crypto
Requires: python-msgpack-python
Requires: python-pycrypto
Requires(pre): %fillup_prereq
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description minion
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
Requires: %{name} = %{version}
Requires: %{name}-master = %{version}
Requires(pre): %fillup_prereq
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description syndic
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
Group: System/Monitoring
Requires: %{name} = %{version}
Requires: sshpass
Requires(pre): %fillup_prereq
BuildRequires: python-markupsafe
Requires: python-markupsafe
Recommends: sshpass
%if 0%{?suse_version} < 1210
Requires(pre): %insserv_prereq
%endif
Requires(pre): %fillup_prereq
%description ssh
Salt ssh is a master running without zmq.
@ -154,10 +165,12 @@ python setup.py build
%install
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/minion.d
%if 0%{?suse_version} < 1210
mkdir -p %{buildroot}%{_sysconfdir}/init.d
%endif
mkdir -p %{buildroot}%{_localstatedir}/log/salt
mkdir -p %{buildroot}/%{_sysconfdir}/logrotate.d/
mkdir -p %{buildroot}/%{_sbindir}
@ -165,92 +178,102 @@ mkdir -p %{buildroot}/var/log/salt
mkdir -p %{buildroot}/srv/salt
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 %{SOURCE2} %{buildroot}%{_initddir}/salt-syndic
install -Dpm 0755 %{SOURCE3} %{buildroot}%{_initddir}/salt-minion
ln -sf %{_initddir}/salt-master %{buildroot}%{_sbindir}/rcsalt-master
ln -sf %{_initddir}/salt-syndic %{buildroot}%{_sbindir}/rcsalt-syndic
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
#
##config files
## install config files
install -Dpm 0644 conf/minion %{buildroot}%{_sysconfdir}/salt/minion
install -Dpm 0644 conf/master %{buildroot}%{_sysconfdir}/salt/master
#
##logrotate file
## install logrotate file
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
%if 0%{?suse_version} != 1220 && 0%{?suse_version} != 1230
%check
#export only_local_network=False
%{__python} setup.py test --runtests-opts=-u
%endif
%preun -n salt-syndic
%stop_on_removal salt-syndic
%if 0%{?_unitdir:1}
%service_del_preun salt-syndic.service
%else
%stop_on_removal salt-syndic
%endif
%post -n salt-syndic
%fillup_and_insserv
%if 0%{?_unitdir:1}
%service_add_post salt-syndic.service
%fillup_only
%else
%fillup_and_insserv
%endif
%postun -n salt-syndic
%restart_on_update salt-syndic
%if 0%{?_unitdir:1}
%service_del_postun salt-syndic.service
%endif
%else
%insserv_cleanup
%restart_on_update salt-syndic
%endif
%preun -n salt-master
%stop_on_removal salt-master
%if 0%{?_unitdir:1}
%service_del_preun salt-master.service
%else
%stop_on_removal salt-master
%endif
%post -n salt-master
%fillup_and_insserv
%if 0%{?_unitdir:1}
%service_add_post salt-master.service
%fillup_only
%else
%fillup_and_insserv
%endif
%postun -n salt-master
%restart_on_update salt-master
%if 0%{?_unitdir:1}
%service_del_postun salt-master.service
%endif
%else
%restart_on_update salt-master
%insserv_cleanup
%endif
%preun -n salt-minion
%stop_on_removal salt-minion
%if 0%{?_unitdir:1}
%service_del_preun salt-minion.service
%else
%stop_on_removal salt-minion
%endif
%post -n salt-minion
%fillup_and_insserv
%if 0%{?_unitdir:1}
%service_add_post salt-minion.service
%fillup_only
%else
%fillup_and_insserv
%endif
%postun -n salt-minion
%restart_on_update salt-minion
%if 0%{?_unitdir:1}
%service_del_postun salt-minion.service
%endif
%else
%insserv_cleanup
%restart_on_update salt-minion
%endif
%files -n salt-ssh
%defattr(-,root,root)
@ -261,22 +284,24 @@ install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2
%defattr(-,root,root)
%{_bindir}/salt-syndic
%{_mandir}/man1/salt-syndic.1.gz
%{_sbindir}/rcsalt-syndic
%{_sysconfdir}/init.d/salt-syndic
%if 0%{?_unitdir:1}
%_unitdir/salt-syndic.service
%else
%{_sbindir}/rcsalt-syndic
%{_sysconfdir}/init.d/salt-syndic
%endif
%files -n salt-minion
%defattr(-,root,root)
%{_bindir}/salt-minion
%{_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
%{_sysconfdir}/salt/minion.d
%if 0%{?_unitdir:1}
%_unitdir/salt-minion.service
%else
%{_sbindir}/rcsalt-minion
%config(noreplace) %{_sysconfdir}/init.d/salt-minion
%endif
%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-key.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
%attr(0644, root, root) %config(noreplace) %{_sysconfdir}/salt/master
%{_sysconfdir}/salt/master.d
@ -300,6 +323,9 @@ install -Dpm 0644 %{SOURCE8} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2
%dir /srv/pillar
%if 0%{?_unitdir:1}
%_unitdir/salt-master.service
%else
%{_sbindir}/rcsalt-master
%config(noreplace) %{_sysconfdir}/init.d/salt-master
%endif
%files

View File

@ -170,7 +170,7 @@ class SSH(object):
if deploy.startswith(('n', 'N')):
return ret
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 ret

View File

@ -664,7 +664,7 @@ def syndic_config(master_config_path,
return opts
def get_id(root_dir=None):
def get_id(root_dir=None, minion_id=False):
'''
Guess the id of the minion.
@ -704,6 +704,7 @@ def get_id(root_dir=None):
fqdn = socket.getfqdn()
if fqdn != 'localhost':
log.info('Found minion id from getfqdn(): {0}'.format(fqdn))
if minion_id:
try:
with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(fqdn)
@ -720,6 +721,7 @@ def get_id(root_dir=None):
'This file should not contain any whitespace.')
else:
if name != 'localhost':
if minion_id:
try:
with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name)
@ -740,6 +742,7 @@ def get_id(root_dir=None):
if name != 'localhost':
log.info('Found minion id in hosts file: {0}'
.format(name))
if minion_id:
try:
with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name)
@ -766,6 +769,7 @@ def get_id(root_dir=None):
if name != 'localhost':
log.info('Found minion id in hosts file: {0}'
.format(name))
if minion_id:
try:
with salt.utils.fopen(id_cache, 'w') as idf:
idf.write(name)
@ -830,8 +834,9 @@ def apply_minion_config(overrides=None,
# No ID provided. Will getfqdn save us?
using_ip_for_id = False
if opts['id'] is None and minion_id:
opts['id'], using_ip_for_id = get_id(opts['root_dir'])
if opts['id'] is None:
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
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")
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.find
import salt.utils.filebuffer
import salt.utils.atomicfile
from salt.exceptions import CommandExecutionError, SaltInvocationError
import salt._compat
@ -989,6 +990,154 @@ def replace(path,
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,
pattern,
flags=0,

View File

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

View File

@ -428,7 +428,7 @@ def list_(profile=None):
def server_list(profile=None):
'''
Return detailed information for an active server
Return list of active servers
CLI Example:
@ -443,6 +443,12 @@ def server_list(profile=None):
'id': item.id,
'name': item.name,
'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
@ -455,20 +461,19 @@ def show(server_id, profile=None):
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:
.. code-block:: bash
salt '*' nova.show
salt '*' nova.server_list_detailed
'''
nt_ks = _auth(profile)
ret = {}
for item in nt_ks.servers.list():
if item.id == server_id:
ret[item.name] = {
'OS-EXT-SRV-ATTR': {},
'OS-EXT-STS': {},
@ -488,7 +493,6 @@ def server_show(server_id, profile=None):
'metadata': item.metadata,
'name': item.name,
'progress': item.progress,
'security_groups': item.security_groups,
'status': item.status,
'tenant_id': item.tenant_id,
'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']
if hasattr(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

View File

@ -267,13 +267,13 @@ def do(cmdline=None, runas=None):
.. code-block:: bash
salt '*' rbenv.do "gem list bundler"
salt '*' rbenv.do "gem list bundler" deploy
salt '*' rbenv.do 'gem list bundler'
salt '*' rbenv.do 'gem list bundler' deploy
'''
path = _rbenv_path(runas)
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
)

View File

@ -60,6 +60,12 @@ def __virtual__():
# Disable on these platforms, specific service modules exist:
if __grains__['os'] in ('Ubuntu', 'Linaro', 'elementary OS'):
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

View File

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

View File

@ -18,7 +18,7 @@ import salt.key
import salt.client
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):

View File

@ -1776,6 +1776,121 @@ def replace(name,
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,
before,
after,
@ -2548,7 +2663,8 @@ def rename(name, source, force=False, makedirs=False):
def accumulated(name, filename, text, **kwargs):
'''
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
Accumulator name

View File

@ -3,7 +3,7 @@
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,
and comment keys. These defaults can be overridden by including them in the
name.
@ -116,19 +116,19 @@ def present(
config='.ssh/authorized_keys',
**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
The ssh key to manage
The SSH key to manage
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, ssh-dss
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
The comment to be placed with the SSH public key
source
The source file for the key(s). Can contain any number of public keys,
@ -237,15 +237,29 @@ def present(
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
The ssh key to manage
The SSH key to manage
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
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'](
user,
name,
'',
'',
[],
enc,
comment,
options or [],
config)
if check == 'update' or check == 'exists':
ret['result'] = None

View File

@ -13,7 +13,8 @@ def mac(addr):
'''
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})$)
''',
re.VERBOSE | re.IGNORECASE)
return valid.match(addr) is not None

View File

@ -6,17 +6,20 @@ import textwrap
# Import Salt Testing libs
from salttesting import TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import MagicMock
ensure_in_syspath('../../')
# Import Salt libs
from salt.modules import file as filemod
from salt.modules import cmdmod
from salt.exceptions import CommandExecutionError, SaltInvocationError
filemod.__salt__ = {
'cmd.run': cmdmod.run,
'cmd.run_all': cmdmod.run_all
}
filemod.__opts__ = {'test': False}
SED_CONTENT = """test
some
@ -101,6 +104,140 @@ class FileReplaceTestCase(TestCase):
def test_re_int_flags(self):
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):
def test_sed_limit_escaped(self):
@ -149,4 +286,4 @@ class FileModuleTestCase(TestCase):
if __name__ == '__main__':
from integration import run_tests
run_tests(FileModuleTestCase, FileReplaceTestCase, needs_daemon=False)
run_tests(FileModuleTestCase, FileReplaceTestCase, FileBlockReplaceTestCase, needs_daemon=False)