summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-02-20 10:38:38 -0500
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-02-20 10:38:38 -0500
commit69ebf49d54aac70a42142d0d04e562496bce58ea (patch)
treead0f346ff95a14ad49440128ff76d7e2b3f0816a
parent602ba6af6bd1c9b3910940dee766660ab8e81a19 (diff)
parente17e41dcff096ead7e129a0db063f75de44aaa2b (diff)
downloadbcfg2-69ebf49d54aac70a42142d0d04e562496bce58ea.tar.gz
bcfg2-69ebf49d54aac70a42142d0d04e562496bce58ea.tar.bz2
bcfg2-69ebf49d54aac70a42142d0d04e562496bce58ea.zip
Merge branch 'master' into 1.4.x
Conflicts: doc/appendix/contributors.txt schemas/bundle.xsd src/lib/Bcfg2/Client/Tools/__init__.py src/lib/Bcfg2/Server/Encryption.py src/lib/Bcfg2/Server/Lint/Genshi.py src/lib/Bcfg2/Server/Plugins/Bundler.py src/lib/Bcfg2/Server/Plugins/Decisions.py src/lib/Bcfg2/Server/Plugins/TemplateHelper.py src/sbin/bcfg2-test testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestProperties.py tools/bcfg2-profile-templates.py
-rw-r--r--AUTHORS64
-rw-r--r--COPYRIGHT169
-rw-r--r--LICENSE48
-rw-r--r--README14
-rw-r--r--debian/bcfg2-server.docs3
-rw-r--r--debian/bcfg2.docs4
-rw-r--r--debian/changelog6
-rw-r--r--debian/control12
-rw-r--r--debian/copyright70
-rw-r--r--doc/appendix/contributors.txt78
-rw-r--r--doc/appendix/index.txt1
-rw-r--r--doc/development/compat.txt2
-rw-r--r--doc/server/encryption.txt7
-rw-r--r--doc/server/plugins/generators/cfg.txt2
-rw-r--r--doc/server/plugins/generators/packages.txt18
-rw-r--r--doc/server/plugins/generators/rules.txt2
-rw-r--r--misc/apache/bcfg2.conf2
-rw-r--r--misc/bcfg2-selinux.spec11
-rw-r--r--misc/bcfg2.spec30
-rw-r--r--osx/Makefile2
-rw-r--r--osx/macports/Portfile2
-rw-r--r--redhat/RELEASE2
-rw-r--r--redhat/bcfg2.spec.in19
-rw-r--r--schemas/bundle.xsd69
-rw-r--r--schemas/privkey.xsd7
-rwxr-xr-xsetup.py2
-rw-r--r--solaris/Makefile2
-rw-r--r--solaris/pkginfo.bcfg22
-rw-r--r--solaris/pkginfo.bcfg2-server2
-rw-r--r--src/lib/Bcfg2/Client/Frame.py31
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py1
-rw-r--r--src/lib/Bcfg2/Client/Tools/SELinux.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM.py15
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py104
-rw-r--r--src/lib/Bcfg2/Client/__init__.py28
-rw-r--r--src/lib/Bcfg2/Compat.py7
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py6
-rw-r--r--src/lib/Bcfg2/Reporting/models.py8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base.html2
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/index.html5
-rw-r--r--src/lib/Bcfg2/Reporting/views.py7
-rw-r--r--src/lib/Bcfg2/Server/Core.py40
-rwxr-xr-xsrc/lib/Bcfg2/Server/Encryption.py6
-rw-r--r--src/lib/Bcfg2/Server/Lint/Comments.py2
-rwxr-xr-xsrc/lib/Bcfg2/Server/Lint/Genshi.py48
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Defaults.py29
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py48
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Collection.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py93
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSLCA.py5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py18
-rw-r--r--src/lib/Bcfg2/Server/Reports/updatefix.py5
-rw-r--r--src/lib/Bcfg2/settings.py2
-rw-r--r--src/lib/Bcfg2/version.py2
-rwxr-xr-xsrc/sbin/bcfg2-lint3
-rwxr-xr-xsrc/sbin/bcfg2-test59
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDevice.py56
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDirectory.py111
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestFile.py300
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestHardlink.py62
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestNonexistent.py28
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestPermissions.py1
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestSymlink.py70
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py129
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Testbase.py625
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py17
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py649
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgExternalCommandVerifier.py2
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py17
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestTemplateHelper.py15
-rw-r--r--tools/README4
-rwxr-xr-xtools/accounts2xml.py153
-rwxr-xr-xtools/bcfg2-profile-templates.py122
-rwxr-xr-xtools/upgrade/1.3/migrate_dbstats.py9
78 files changed, 2175 insertions, 1446 deletions
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index d2bebd02f..000000000
--- a/AUTHORS
+++ /dev/null
@@ -1,64 +0,0 @@
-In no particular order:
-
-- Narayan Desai <desai@mcs.anl.gov> has written most of Bcfg2,
- including all parts not explicitly mentioned in this file.
-
-- Sol Jerome <sol.jerome@gmail.com> squashes bugs, helps manage the
- project roadmap, and implements various interesting features.
-
-- Tim Laszlo <tim.laszlo@gmail.com> worked on the reporting system and
- made plugins.
-
-- Fabian Affolter <mail@fabian-affolter.ch> made some patches, added
- some new features and plugins, and restructured the manual for Bcfg2.
-
-- Andrew Brestick <brestick@mcs.anl.gov> fixed bugs and completed
- plugins.
-
-- James Yang <jjyang@mcs.anl.gov> worked on bcfg2-admin and
- bcfg2-reports.
-
-- Robert Gogolok <gogo@cs.uni-sb.de> fixed bugs and made the code more
- robust.
-
-- Jack Neely <jjneely@ncsu.edu> worked on YUMng.
-
-- Joey Hagedorn <hagedorn@mcs.anl.gov> has written the reporting
- subsystem, including StatReports, GenerateHostinfo, and the xslt,
- css and javascript associated with it.
-
-- Ed Smith <esmith4@inf.ed.ac.uk> has done substantial hardening of
- the Bcfg2 client and server and implemented a common logging
- infrastructure.
-
-- Rick Bradshaw <bradshaw@mcs.anl.gov> has written several of the
- tools included in the tools/ subdirectory.
-
-- Ken Raffenetti <raffenet@mcs.anl.gov> and Rick Bradshaw have written
- the Hostbase plugin.
-
-- Scott Behrens <behrens@mcs.anl.gov> and Rick Bradshaw have written
- the VHost plugin.
-
-- Cory Lueninghoener <cory@mcs.anl.gov> wrote the showentries function
- in bcfg2-info.
-
-- Chris Vuletich <vuletich@mcs.anl.gov> wrote some SSL code and the
- verification debugging code.
-
-- Daniel Clark <dclark@pobox.com> created encap packages for Bcfg2 and
- deps, wrote fossil-scm dvcs support, and helps with debian packaging
-
-- Jason Pepas <cell@ices.utexas.edu> has written a rpm package list
- creator has contributed patches to the Red Hat toolset.
-
-- Sami Haahtinen <ressu@ressukka.net> has writen debian packaging
- logic.
-
-- Brian Pellin and Andrew Lusk did substantial work on Bcfg1, some of
- which was used in the Bcfg2 client.
-
-- Michael Jinks <mjinks@uchicago.edu> wrote the gentoo tool drivers.
-
-- Chris St. Pierre <stpierreca@ornl.gov> (re)wrote bcfg2-lint and has
- made other miscellaneous contributions.
diff --git a/COPYRIGHT b/COPYRIGHT
index 39f0b0731..571fa6034 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -1,37 +1,154 @@
+- Narayan Desai <desai@mcs.anl.gov> has written most of Bcfg2,
+ including all parts not explicitly mentioned in this file.
- COPYRIGHT
+- Sol Jerome <sol.jerome@gmail.com> squashes bugs, helps manage the
+ project roadmap, and implements various interesting features.
-The following is a notice of limited availability of the code, and disclaimer
-which must be included in the prologue of the code and in all source listings
-of the code.
+- Tim Laszlo <tim.laszlo@gmail.com> worked on the reporting system and
+ made plugins.
-Copyright Notice
- + 2004 University of Chicago
+- Fabian Affolter <mail@fabian-affolter.ch> made some patches, added
+ some new features and plugins, and restructured the manual for Bcfg2.
-Permission is hereby granted to use, reproduce, prepare derivative works, and
-to redistribute to others. This software was authored by:
+- Andrew Brestick <brestick@mcs.anl.gov> fixed bugs and completed
+ plugins.
-Argonne National Laboratory
-N. Desai: (630) 252-7587; FAX: (630) 252-5986; e-mail: desai@mcs.anl.gov
-Mathematics and Computer Science Division
-Argonne National Laboratory, Argonne IL 60439
+- James Yang <jjyang@mcs.anl.gov> worked on bcfg2-admin and
+ bcfg2-reports.
+- Robert Gogolok <gogo@cs.uni-sb.de> fixed bugs and made the code more
+ robust.
- GOVERNMENT LICENSE
+- Jack Neely <jjneely@ncsu.edu> worked on the YUM driver.
-Portions of this material resulted from work developed under a U.S.
-Government Contract and are subject to the following license: the Government
-is granted for itself and others acting on its behalf a paid-up, nonexclusive,
-irrevocable worldwide license in this computer software to reproduce, prepare
-derivative works, and perform publicly and display publicly.
+- Joey Hagedorn <hagedorn@mcs.anl.gov> has written the reporting
+ subsystem, including StatReports, GenerateHostinfo, and the xslt,
+ css and javascript associated with it.
- DISCLAIMER
+- Ed Smith <esmith4@inf.ed.ac.uk> has done substantial hardening of
+ the Bcfg2 client and server and implemented a common logging
+ infrastructure.
-This computer code material was prepared, in part, as an account of work
-sponsored by an agency of the United States Government. Neither the United
-States, nor the University of Chicago, nor any of their employees, makes any
-warranty express or implied, or assumes any legal liability or responsibility
-for the accuracy, completeness, or usefulness of any information, apparatus,
-product, or process disclosed, or represents that its use would not infringe
-privately owned rights.
+- Rick Bradshaw <bradshaw@mcs.anl.gov> has written several of the
+ tools included in the tools/ subdirectory.
+- Ken Raffenetti <raffenet@mcs.anl.gov>, Rick Bradshaw, Rene Martin,
+ and David Dahl <dahl@mcs.anl.gov> have written the Hostbase plugin.
+
+- Scott Behrens <behrens@mcs.anl.gov> and Rick Bradshaw have written
+ the VHost plugin.
+
+- Cory Lueninghoener <cory@mcs.anl.gov> wrote the showentries function
+ in bcfg2-info.
+
+- Chris Vuletich <vuletich@mcs.anl.gov> wrote some SSL code and the
+ verification debugging code.
+
+- Daniel Clark <dclark@pobox.com> created encap packages for Bcfg2 and
+ deps, wrote fossil-scm dvcs support, and helps with debian packaging
+
+- Jason Pepas <cell@ices.utexas.edu> has written a rpm package list
+ creator has contributed patches to the Red Hat toolset.
+
+- Sami Haahtinen <ressu@ressukka.net> has writen debian packaging
+ logic.
+
+- Brian Pellin and Andrew Lusk did substantial work on Bcfg1, some of
+ which was used in the Bcfg2 client.
+
+- Michael Jinks <mjinks@uchicago.edu> wrote the gentoo tool drivers.
+
+- Chris St. Pierre <chris.a.st.pierre@gmail.com> has (re)written vast
+ swaths of more recent Bcfg2 releases.
+
+- Anatoly Techtonik <techtonik@gmail.com> has fixed various bugs.
+
+- Arto Jantunen <viiru@debian.org> maintains the Debian packages.
+
+- Asaf Ohaion <asaf@arch64.office.lingnu.com> added Pacman support.
+
+- Brent Bloxam <brent.bloxam@gmail.com> fixed bugs, particularly in
+ the documentation.
+
+- Calen Pennington <cpennington@wgen.net> write bcfg2-test and
+ contributed performance enhancements.
+
+- Calvin Cheng <calvin@calvinx.com> worked on Python packaging.
+
+- Carl Jackson <carl@avtok.com> fixed client-side bugs.
+
+- Chris Brinker <chris.brinker@gmail.com> added support for client
+ profile assertion to bcfg2.conf
+
+- Christopher 'm4z' Holm <them4z@googlemail.com> greatly improved the
+ RPM build logic.
+
+- Dan Foster <dan.foster@bristol.ac.uk> contributed Solaris 10 build
+ fixes.
+
+- David Strauss <david@fourkitchens.com> wrote the Bzr plugin and
+ contributed other various fixes.
+
+- Gordon Messmer <gordon@herald.private.dragonsdawn.net> contributed
+ documentation fixes.
+
+- Graham Hagger <g.hagger@gmail.com> wrote the SSLCA plugin.
+
+- Holger Weiß <holger@zedat.fu-berlin.de> has fixed a tremendous
+ number and variety of bugs, particularly with unicode handling,
+ SSHbase, and bcfg2-reports.
+
+- Jake Davis <jake@imapenguin.com> has fixed various bugs.
+
+- Jason Kincl <kincljc@ornl.gov> added conflict resolution to the Svn
+ plugin.
+
+- Jeffrey C. Ollie <jeff@ocjtech.us> wrote systemd support.
+
+- Jeroen Dekkers <jeroen@dekkers.ch> worked on the APT driver.
+
+- Joe Digilio <jgd-github@metajoe.com> worked on Cheetah support and
+ fixed other bugs.
+
+- John Morris <john@zultron.com> fixed bugs in the Chkconfig driver.
+
+- John 'Skip' Reddy <jreddy@alcf.anl.gov> worked on DBStats.
+
+- Jonathan Billings <jsbillin@umich.edu> worked on systemd support,
+ RPM builds, and fixed other bugs.
+
+- Kamil Kisiel <kamil@kamilkisiel.net> worked on documentation, Py3k
+ support, launchd support, and other bugs.
+
+- Kioob <ob.kioob@daevel.fr> fixed various bugs.
+
+- Luke Cyca <me@lukecyca.com> worked on MacPorts and launchd support.
+
+- Mike Brady <mike.brady@devnull.net.nz> worked on the YUM and RPM
+ drivers.
+
+- Mike McCallister <mike@mccllstr.com> worked on the Packages plugin.
+
+- Phillip Steinbachs <phil@steinbachs.org> wrote Solaris packaging
+ manifests.
+
+- Raul Cuza <raulcuza@gmail.com> worked on Python packaging,
+ documentation, and various bugs.
+
+- Remi Broemeling <remi@infinitewinter.com> worked on handling
+ restarts of Service entries.
+
+- Richard Connon <conan@irconan.co.uk> worked on handling of Apt
+ repositories.
+
+- Steve Tousignant <stousignant@revolutionlinux.com> worked on several
+ of the Debian package list tools and contributed bug fixes.
+
+- Ti Legget <leggett@mcs.anl.gov> worked on ebuild packaging and
+ bugfixes, RPM packaging.
+
+- Torsten Rehn <torsten@rehn.tel> wrote the Ldap plugin and fixed
+ bugs.
+
+- Zach Lowry <zach@mcs.anl.gov> wrote Solaris support and general
+ hardening.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..9059c2ba4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,48 @@
+Copyright (c) 2004, University of Chicago. See the COPYRIGHT file at
+ the top-level directory of this distribution and at
+ https://github.com/Bcfg2/bcfg2/blob/master/COPYRIGHT.
+
+All rights reserved.
+
+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 disclaimers.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers 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
+HOLDER 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.
+
+ GOVERNMENT LICENSE
+
+Portions of this material resulted from work developed under a U.S.
+Government Contract and are subject to the following license: the
+Government is granted for itself and others acting on its behalf a
+paid-up, nonexclusive, irrevocable worldwide license in this computer
+software to reproduce, prepare derivative works, and perform publicly
+and display publicly.
+
+ DISCLAIMER
+
+This computer code material was prepared, in part, as an account of
+work sponsored by an agency of the United States Government. Neither
+the United States, nor the University of Chicago, nor any of their
+employees, makes any warranty express or implied, or assumes any legal
+liability or responsibility for the accuracy, completeness, or
+usefulness of any information, apparatus, product, or process
+disclosed, or represents that its use would not infringe privately
+owned rights.
+
diff --git a/README b/README
index 1d55387ff..c836961f7 100644
--- a/README
+++ b/README
@@ -1,26 +1,26 @@
Bcfg2 - A Configuration Management System
-----------------------------------------
-Bcfg2 (bee-config two) helps system administrators produce a
+Bcfg2 (bee-config two) helps system administrators produce a
consistent, reproducible, and verifiable description of their
environment, and offers visualization and reporting tools to aid
in day-to-day administrative tasks. It is the fifth generation of
configuration management tools developed in the Mathematics and
-Computer Science Division of Argonne National Laboratory.
+Computer Science Division of Argonne National Laboratory.
* Homepage: http://bcfg2.org
Bcfg2 is fairly portable. It has been successfully run on:
* AIX, FreeBSD, OpenBSD Mac OS X, OpenSolaris, Solaris
-* Many GNU/Linux distributions, including ArchLinux, Blag, CentOS,
+* Many GNU/Linux distributions, including ArchLinux, Blag, CentOS,
Debian, Fedora, Gentoo, gNewSense, Mandriva, openSUSE, Red Hat/RHEL,
SuSE/SLES, Trisquel, and Ubuntu.
-
+
Installation
------------
-For details about the installation of Bcfg2 please refer to the
+For details about the installation of Bcfg2 please refer to the
following pages in the Bcfg2 wiki.
* Prerequisites: http://bcfg2.org/wiki/Prereqs
@@ -46,5 +46,5 @@ Want to help
* Wiki: http://bcfg2.org/wiki/Contribute
-Bcfg2 is licensed under a Simplified (2-clause) BSD license, for more
-details check COPYRIGHT.
+Bcfg2 is licensed under a Simplified (2-clause) BSD license. For more
+details check LICENSE.
diff --git a/debian/bcfg2-server.docs b/debian/bcfg2-server.docs
index 62deb0497..fea4b1e8f 100644
--- a/debian/bcfg2-server.docs
+++ b/debian/bcfg2-server.docs
@@ -1 +1,2 @@
-AUTHORS
+LICENSE
+COPYRIGHT
diff --git a/debian/bcfg2.docs b/debian/bcfg2.docs
index 62deb0497..57c7cb760 100644
--- a/debian/bcfg2.docs
+++ b/debian/bcfg2.docs
@@ -1 +1,3 @@
-AUTHORS
+LICENSE
+COPYRIGHT
+
diff --git a/debian/changelog b/debian/changelog
index 6871a2f65..3a38bf02a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+bcfg2 (1.3.0rc2-0.0) unstable; urgency=low
+
+ * New upstream release
+
+ -- Sol Jerome <sol.jerome@gmail.com> Tue, 29 Jan 2013 10:43:55 -0600
+
bcfg2 (1.3.0rc1-0.0) unstable; urgency=low
* New upstream release
diff --git a/debian/control b/debian/control
index e2826a057..6c7278e4e 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,17 @@ Section: admin
Priority: optional
Maintainer: Arto Jantunen <viiru@debian.org>
Uploaders: Sami Haahtinen <ressu@debian.org>
-Build-Depends: debhelper (>= 7.0.50~), python (>= 2.3.5-7), python-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx, python-lxml, python-daemon, python-cherrypy, python-pyinotify, python-mock, python-m2crypto, python-doc, python-mock-doc
+Build-Depends: debhelper (>= 7.0.50~),
+ python (>= 2.3.5-7),
+ python-setuptools,
+ python-sphinx (>= 1.0.7+dfsg) | python3-sphinx,
+ python-lxml,
+ python-daemon,
+ python-cherrypy,
+ python-pyinotify,
+ python-m2crypto,
+ python-doc,
+ python-mock-doc
Build-Depends-Indep: python-support (>= 0.5.3)
Standards-Version: 3.8.0.0
XS-Python-Version: >= 2.3
diff --git a/debian/copyright b/debian/copyright
index 224d3659a..85964ad86 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -4,44 +4,58 @@ It was downloaded from http://trac.mcs.anl.gov/projects/bcfg2/
Upstream Author: Narayan Desai <desai@mcs.anl.gov>
-Copyright:
- 2004 - 2006: University of Chicago
+Copyright:
+ 2004 - 2013: University of Chicago
License:
Unless otherwise specified, files are copyright by the following:
-The following is a notice of limited availability of the code, and disclaimer
-which must be included in the prologue of the code and in all source listings
-of the code.
-
-Copyright Notice
- + 2004 University of Chicago
-
-Permission is hereby granted to use, reproduce, prepare derivative works, and
-to redistribute to others. This software was authored by:
-
-Argonne National Laboratory
-N. Desai: (630) 252-7587; FAX: (630) 252-5986; e-mail: desai@mcs.anl.gov
-Mathematics and Computer Science Division
-Argonne National Laboratory, Argonne IL 60439
-
+Copyright (c) 2004, University of Chicago. See the COPYRIGHT file at
+ the top-level directory of this distribution and at
+ https://github.com/Bcfg2/bcfg2/blob/master/COPYRIGHT.
+
+All rights reserved.
+
+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 disclaimers.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers 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
+HOLDER 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.
GOVERNMENT LICENSE
Portions of this material resulted from work developed under a U.S.
-Government Contract and are subject to the following license: the Government
-is granted for itself and others acting on its behalf a paid-up, nonexclusive,
-irrevocable worldwide license in this computer software to reproduce, prepare
-derivative works, and perform publicly and display publicly.
+Government Contract and are subject to the following license: the
+Government is granted for itself and others acting on its behalf a
+paid-up, nonexclusive, irrevocable worldwide license in this computer
+software to reproduce, prepare derivative works, and perform publicly
+and display publicly.
DISCLAIMER
-This computer code material was prepared, in part, as an account of work
-sponsored by an agency of the United States Government. Neither the United
-States, nor the University of Chicago, nor any of their employees, makes any
-warranty express or implied, or assumes any legal liability or responsibility
-for the accuracy, completeness, or usefulness of any information, apparatus,
-product, or process disclosed, or represents that its use would not infringe
-privately owned rights.
+This computer code material was prepared, in part, as an account of
+work sponsored by an agency of the United States Government. Neither
+the United States, nor the University of Chicago, nor any of their
+employees, makes any warranty express or implied, or assumes any legal
+liability or responsibility for the accuracy, completeness, or
+usefulness of any information, apparatus, product, or process
+disclosed, or represents that its use would not infringe privately
+owned rights.
diff --git a/doc/appendix/contributors.txt b/doc/appendix/contributors.txt
deleted file mode 100644
index 9f99d25be..000000000
--- a/doc/appendix/contributors.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-.. -*- mode: rst -*-
-
-.. _AUTHORS: http://trac.mcs.anl.gov/projects/bcfg2/browser/AUTHORS
-.. _MCS: http://www.mcs.anl.gov/
-
-.. _appendix-contributors:
-
-============
-Contributors
-============
-
-..
- This is list is no longer in chronological order like the
- AUTHORS file because it's easier to maintain.
- Automatically sorted.
-
-In alphabetical order of the given name:
-
-- Andrew Brestick <brestick@mcs.anl.gov> fixed bugs and completed
- plugins.
-- Brian Pellin and Andrew Lusk did substantial work on Bcfg1, some of
- which was used in the Bcfg2 client.
-- Chris Vuletich <vuletich@mcs.anl.gov> wrote some SSL code and the
- verification debugging code.
-- Chris St. Pierre <stpierreca@ornl.gov> (re)wrote bcfg2-lint and has
- made other miscellaneous contributions.
-- Cory Lueninghoener <cory@mcs.anl.gov> wrote the showentries function
- in ``bcfg2-info``.
-- Daniel Clark <dclark@pobox.com> created encap packages for bcfg2 and
- deps, wrote fossil-scm dvcs support, and helps with Debian packaging.
-- Danny Clark enabled the Encap packaging.
-- David Dahl worked on Hostbase.
-- David Strauss worked on CentOS, RHEL, Yum, and Bazaar VCS support.
-- Ed Smith <esmith4@inf.ed.ac.uk> has done substantial hardening of the
- Bcfg client and server and implemented a common logging infrastructure.
-- Fabian Affolter <mail@fabian-affolter.ch> made some patches, added
- some new features and plugins, and restructured the manual for Bcfg2.
-- Jack Neely <jjneely@ncsu.edu> worked on YUMng (now YUM).
-- James Yang <jjyang@mcs.anl.gov> worked on ``bcfg2-admin`` and
- ``bcfg2-reports``.
-- Jason Pepas <cell@ices.utexas.edu> has written a RPM package list creator
- has contributed patches to the Red Hat toolset.
-- Joey Hagedorn <hagedorn@mcs.anl.gov> has written the reporting subsystem,
- including StatReports, GenerateHostinfo, and the xslt, css and javascript
- associated with it.
-- Jos Catnook fixed bugs.
-- Ken Raffenetti <raffenet@mcs.anl.gov> and Rick Bradshaw have written the
- Hostbase plugin.
-- Michael Jinks <mjinks@uchicago.edu> wrote the Gentoo tool drivers.
-- Narayan Desai <desai@mcs.anl.gov> has written most of Bcfg2, including all
- parts not explicitly mentioned in this file.
-- Patrick Ruckstuhl fixed bugs in the templating.
-- Pedro Flores made the Reporting system design help.
-- Raul Cuza <raulcuza@gmail.com> updated the documentation.
-- Rick Bradshaw <bradshaw@mcs.anl.gov> has written several of the tools
- included in the ``tools/`` subdirectory.
-- Robert Gogolok <gogo@cs.uni-sb.de> fixed bugs and made the code more
- robust.
-- Sami Haahtinen <ressu@ressukka.net> has written Debian packaging logic.
-- Scott Behrens <behrens@mcs.anl.gov> and Rick Bradshaw have written the
- VHost plugin.
-- Scott Matott
-- Sol Jerome <sol.jerome@gmail.com> squashes bugs, helps manage the
- project roadmap, and implements various interesting features.
-- Ti Leggett worked on ebuild packaging and bugfixes, RPM packaging.
-- Tim Laszlo <tim.laszlo@gmail.com> worked on the reporting system and made
- plugins.
-- Zach Lowry Solaris support and general hardening.
-
-
-The entire MCS_ systems team has provided invaluable help in the
-design process and refinement of the user interface. In particular,
-Gene Rackow and Sandra Bittner have provided great assistance
-throughout this project. Philip Steinbachs provided detailed
-feedback as an early external user.
-
-The most updated listing is available in the AUTHORS_ file in the
-git :term:`repository` for Bcfg2.
diff --git a/doc/appendix/index.txt b/doc/appendix/index.txt
index 407119e24..135d00bc8 100644
--- a/doc/appendix/index.txt
+++ b/doc/appendix/index.txt
@@ -11,7 +11,6 @@ Appendix
files
configuration
- contributors
books
papers
articles
diff --git a/doc/development/compat.txt b/doc/development/compat.txt
index b7bf87bec..90df45676 100644
--- a/doc/development/compat.txt
+++ b/doc/development/compat.txt
@@ -98,6 +98,8 @@ behavior (e.g., :func:`input`) do not cause unexpected side-effects.
+---------------------------------+--------------------------------------------------+---------------------------------------------------------+
| reduce | :func:`reduce` | :func:`functools.reduce` |
+---------------------------------+--------------------------------------------------+---------------------------------------------------------+
+| long | :func:`long` | :func:`int` |
++---------------------------------+--------------------------------------------------+---------------------------------------------------------+
Python 2.4 compatibility
------------------------
diff --git a/doc/server/encryption.txt b/doc/server/encryption.txt
index a701a42ca..28ca149f5 100644
--- a/doc/server/encryption.txt
+++ b/doc/server/encryption.txt
@@ -171,9 +171,8 @@ For instance::
.. note::
- The name of a passphrase **cannot** be "algorithm"; that
- configuration option is reserved for configuring the cipher
- algorithm.
+ The name of a passphrase **cannot** be ``algorithm`` or
+ ``decrypt``, which are reserved for other configuration options.
This would define two separate encryption passphrases, presumably for
use by two separate teams. The passphrase names are completely
@@ -221,7 +220,7 @@ though, that may not be possible. (For instance, if you use
encryption to protect data for your production environment from your
staging Bcfg2 server, then you would not expect the staging server to
be able to decrypt everything.) In this case, you want to enable lax
-decryption in the ``[encryption]`` section of ``bcfg2.conf``:
+decryption in the ``[encryption]`` section of ``bcfg2.conf``::
[encryption]
decrypt = lax
diff --git a/doc/server/plugins/generators/cfg.txt b/doc/server/plugins/generators/cfg.txt
index 8339c8080..4d35a5970 100644
--- a/doc/server/plugins/generators/cfg.txt
+++ b/doc/server/plugins/generators/cfg.txt
@@ -305,7 +305,7 @@ The Cheetah template is ignored. Exploiting this fact is probably a
pretty bad idea in practice.
You can mix Genshi and Cheetah when using different host-specific or
-group-specific files. For example:
+group-specific files. For example::
Cfg/etc/fstab/fstab.H_host.example.com.genshi
Cfg/etc/fstab/fstab.G50_server.cheetah
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index f178e1563..b11d1ebc5 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -476,9 +476,10 @@ Package Groups
--------------
Yum package groups are supported by the native Yum libraries. To
-include a package group, use the :xml:attribute:`PackageType:group`
-attribute of the :xml:element:`Package` tag. You can use either the
-short group ID or the long group name:
+include a package group, use the
+:xml:attribute:`PackageStructure:group` attribute of the
+:xml:element:`Package` tag. You can use either the short group ID or
+the long group name:
.. code-block:: xml
@@ -501,13 +502,22 @@ Valid values of "type" are:
* ``optional`` or ``all``: Install all packages in the group,
including mandatory, default, and optional packages.
-See :xml:element:`PackageStructure` for details.
+See :xml:type:`PackageStructure` for details.
You can view the packages in a group by category with the ``yum
groupinfo`` command. More information about the different levels can
be found at
http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation
+Abstract Package Tags
+---------------------
+
+If you are using the native Yum libraries, the abstract Package tag
+supports several attributes in addition to the standard
+:xml:attribute:`PackageStructure:name`:
+
+.. xml:type:: PackageStructure
+
.. _pulp-source-support:
Pulp Support
diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt
index adcb55d3e..bcd7342c9 100644
--- a/doc/server/plugins/generators/rules.txt
+++ b/doc/server/plugins/generators/rules.txt
@@ -375,7 +375,7 @@ For example:
<MemberOf>lp</MemberOf>
<MemberOf>adm</MemberOf>
<MemberOf>bin</MemberOf>
- </BoundPOSIXUser>
+ </POSIXUser>
The group specified will automatically be created if it does not
exist, even if there is no :xml:type:`POSIXGroup tag <POSIXGroupType>`
diff --git a/misc/apache/bcfg2.conf b/misc/apache/bcfg2.conf
index b9b4b0452..6cd5addf5 100644
--- a/misc/apache/bcfg2.conf
+++ b/misc/apache/bcfg2.conf
@@ -4,7 +4,7 @@
#
WSGIScriptAlias /bcfg2 "/usr/share/bcfg2/reports.wsgi"
- WSGISocketPrefix run
+ WSGISocketPrefix /var/run/httpd/wsgi
WSGIDaemonProcess Bcfg2.Server.Reports processes=1 threads=10
WSGIProcessGroup Bcfg2.Server.Reports
diff --git a/misc/bcfg2-selinux.spec b/misc/bcfg2-selinux.spec
index f8e6dbeb1..e5a0eed16 100644
--- a/misc/bcfg2-selinux.spec
+++ b/misc/bcfg2-selinux.spec
@@ -9,7 +9,7 @@
Name: bcfg2-selinux
Version: 1.3.0
-Release: 0.0rc1
+Release: 0.0rc2
Summary: Bcfg2 Client and Server SELinux policy
%if 0%{?suse_version}
@@ -24,8 +24,8 @@ Conflicts: selinux-policy = 3.11.1
License: BSD
URL: http://bcfg2.org
-Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}rc1.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}rc1-%{release}-root-%(%{__id_u} -n)
+Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}rc2.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}rc2-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
BuildRequires: checkpolicy, selinux-policy-devel, hardlink
@@ -65,7 +65,7 @@ deployment strategies.
This package includes the Bcfg2 server and client SELinux policy.
%prep
-%setup -q -n %{name}-%{version}rc1
+%setup -q -n %{name}-%{version}rc2
%build
cd redhat/selinux
@@ -120,6 +120,9 @@ if [ $1 -eq 0 ] ; then
fi
%changelog
+* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
+- New upstream release
+
* Wed Jan 09 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc1
- New upstream release
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index 1530682a9..4aaf15e1c 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -6,7 +6,7 @@
Name: bcfg2
Version: 1.3.0
-Release: 0.0rc1
+Release: 0.0rc2
Summary: Configuration management system
%if 0%{?suse_version}
@@ -17,8 +17,8 @@ Group: Applications/System
%endif
License: BSD
URL: http://bcfg2.org
-Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}rc1.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}rc1-%{release}-root-%(%{__id_u} -n)
+Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}rc2.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}rc2-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
BuildRequires: python-devel
@@ -267,7 +267,7 @@ deployment strategies.
This package includes the Bcfg2 reports web frontend.
%prep
-%setup -q -n %{name}-%{version}rc1
+%setup -q -n %{name}-%{version}rc2
%build
%{__python}%{pythonversion} setup.py build
@@ -318,7 +318,8 @@ cp -r build/sphinx/html/* %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}
# mandriva and RHEL 5 cannot handle %ghost without the file existing,
# so let's touch a bunch of empty config files
-touch %{buildroot}%{_sysconfdir}/bcfg2.conf %{buildroot}%{_sysconfdir}/bcfg2-web.conf
+touch %{buildroot}%{_sysconfdir}/bcfg2.conf \
+ %{buildroot}%{_sysconfdir}/bcfg2-web.conf
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} || exit 2
@@ -327,9 +328,13 @@ touch %{buildroot}%{_sysconfdir}/bcfg2.conf %{buildroot}%{_sysconfdir}/bcfg2-web
%defattr(-,root,root,-)
%{_sbindir}/bcfg2
%dir %{python_sitelib}/Bcfg2
-%{python_sitelib}/Bcfg2/*.py*
-%dir %{python_sitelib}/Bcfg2/Client
-%{python_sitelib}/Bcfg2/Client/*
+%{python_sitelib}/Bcfg2/Compat.py*
+%{python_sitelib}/Bcfg2/__init__.py*
+%{python_sitelib}/Bcfg2/Logger.py*
+%{python_sitelib}/Bcfg2/Options.py*
+%{python_sitelib}/Bcfg2/Proxy.py*
+%{python_sitelib}/Bcfg2/version.py*
+%{python_sitelib}/Bcfg2/Client
%{_mandir}/man1/bcfg2.1*
%{_mandir}/man5/bcfg2.conf.5*
%{_initrddir}/bcfg2
@@ -350,6 +355,12 @@ touch %{buildroot}%{_sysconfdir}/bcfg2.conf %{buildroot}%{_sysconfdir}/bcfg2-web
%{_initrddir}/bcfg2-server
%{_initrddir}/bcfg2-report-collector
%dir %{python_sitelib}/Bcfg2
+%{python_sitelib}/Bcfg2/Cache.py*
+%{python_sitelib}/Bcfg2/Encryption.py*
+%{python_sitelib}/Bcfg2/SSLServer.py*
+%{python_sitelib}/Bcfg2/Statistics.py*
+%{python_sitelib}/Bcfg2/manage.py*
+%{python_sitelib}/Bcfg2/settings.py*
%{python_sitelib}/Bcfg2/Server
%{python_sitelib}/Bcfg2/Reporting
%exclude %{python_sitelib}/Bcfg2/Server/CherryPyCore.py
@@ -442,6 +453,9 @@ fi
%endif
%changelog
+* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
+- New upstream release
+
* Wed Jan 09 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc1
- New upstream release
diff --git a/osx/Makefile b/osx/Makefile
index 3757b0ad7..f7c4b8ffa 100644
--- a/osx/Makefile
+++ b/osx/Makefile
@@ -28,7 +28,7 @@ SITELIBDIR = /Library/Python/${PYVERSION}/site-packages
# an Info.plist file for packagemaker to look at for package creation
# and substitute the version strings. Major/Minor versions can only be
# integers (e.g. "1" and "00" for bcfg2 version 1.0.0.
-BCFGVER = 1.3.0rc1
+BCFGVER = 1.3.0rc2
MAJOR = 1
MINOR = 30
diff --git a/osx/macports/Portfile b/osx/macports/Portfile
index 77e930c7f..f53974670 100644
--- a/osx/macports/Portfile
+++ b/osx/macports/Portfile
@@ -5,7 +5,7 @@ PortSystem 1.0
PortGroup python26 1.0
name bcfg2
-version 1.3.0rc1
+version 1.3.0rc2
categories sysutils python
maintainers gmail.com:sol.jerome
license BSD
diff --git a/redhat/RELEASE b/redhat/RELEASE
index 888c3a4b3..c39908ef3 100644
--- a/redhat/RELEASE
+++ b/redhat/RELEASE
@@ -1 +1 @@
-0.0rc1
+0.0rc2
diff --git a/redhat/bcfg2.spec.in b/redhat/bcfg2.spec.in
index 57c6221e6..871f3e86a 100644
--- a/redhat/bcfg2.spec.in
+++ b/redhat/bcfg2.spec.in
@@ -189,7 +189,7 @@ fi
%files
%defattr(-,root,root,-)
-%doc AUTHORS examples COPYRIGHT README
+%doc examples LICENSE COPYRIGHT README
%ghost %attr(600,root,root) %config(noreplace) %{_sysconfdir}/bcfg2.conf
@@ -201,7 +201,12 @@ fi
%{python_sitelib}/Bcfg2*.egg-info
%dir %{python_sitelib}/Bcfg2
-%{python_sitelib}/Bcfg2/*.py*
+%{python_sitelib}/Bcfg2/Compat.py*
+%{python_sitelib}/Bcfg2/__init__.py*
+%{python_sitelib}/Bcfg2/Logger.py*
+%{python_sitelib}/Bcfg2/Options.py*
+%{python_sitelib}/Bcfg2/Proxy.py*
+%{python_sitelib}/Bcfg2/version.py*
%{python_sitelib}/Bcfg2/Client
%{_sbindir}/bcfg2
@@ -219,6 +224,13 @@ fi
%{_initrddir}/bcfg2-server
+%dir %{python_sitelib}/Bcfg2
+%{python_sitelib}/Bcfg2/Cache.py*
+%{python_sitelib}/Bcfg2/Encryption.py*
+%{python_sitelib}/Bcfg2/SSLServer.py*
+%{python_sitelib}/Bcfg2/Statistics.py*
+%{python_sitelib}/Bcfg2/manage.py*
+%{python_sitelib}/Bcfg2/settings.py*
%{python_sitelib}/Bcfg2/Server
%{_datadir}/bcfg2
@@ -244,6 +256,9 @@ fi
%doc %{_defaultdocdir}/bcfg2-doc-%{version}
%changelog
+* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
+- New upstream release
+
* Wed Jan 09 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc1
- New upstream release
diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd
index e180d265a..d5abf2d94 100644
--- a/schemas/bundle.xsd
+++ b/schemas/bundle.xsd
@@ -55,25 +55,80 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
- <xsd:element name='SELinux' type='SELinuxStructure'>
+ <xsd:element name='POSIXUser' type='StructureEntry'>
<xsd:annotation>
<xsd:documentation>
- Abstract implementation of an SELinux entry. The
- full specification will be included in Rules.
+ Abstract description of a POSIXUser entry.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
- <xsd:element name='POSIXUser' type='StructureEntry'>
+ <xsd:element name='POSIXGroup' type='StructureEntry'>
<xsd:annotation>
<xsd:documentation>
- Abstract description of a POSIXUser entry.
+ Abstract description of a POSIXGroup entry.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
- <xsd:element name='POSIXGroup' type='StructureEntry'>
+ <xsd:element name='SEBoolean' type='SELinuxStructure'>
<xsd:annotation>
<xsd:documentation>
- Abstract description of a POSIXGroup entry.
+ Abstract SELinux boolean entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEPort' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux port entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEFcontext' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux file context ("fcontext") entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SENode' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux node entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SELogin' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux login entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEUser' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux user entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEInterface' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux interface entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEPermissive' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux permissive domain entry.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='SEModule' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract SELinux module entry.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
diff --git a/schemas/privkey.xsd b/schemas/privkey.xsd
index cf53904f0..0bb1b7184 100644
--- a/schemas/privkey.xsd
+++ b/schemas/privkey.xsd
@@ -144,13 +144,6 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
- <xsd:attribute name="priority" type="xsd:positiveInteger" default="50">
- <xsd:annotation>
- <xsd:documentation>
- Create group-specific keys with the given priority.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
<xsd:attribute name="decrypt" type="EncryptStrictnessEnum">
<xsd:annotation>
<xsd:documentation>
diff --git a/setup.py b/setup.py
index df6adba74..24a8c8d1b 100755
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ if need_m2crypto:
inst_reqs.append('M2Crypto')
setup(name="Bcfg2",
- version="1.3.0rc1",
+ version="1.3.0rc2",
description="Bcfg2 Server",
author="Narayan Desai",
author_email="desai@mcs.anl.gov",
diff --git a/solaris/Makefile b/solaris/Makefile
index 749e6ad79..d81dd0292 100644
--- a/solaris/Makefile
+++ b/solaris/Makefile
@@ -1,7 +1,7 @@
#!/usr/sfw/bin/gmake
PYTHON="/usr/local/bin/python"
-VERS=1.3.0rc1-1
+VERS=1.3.0rc2-1
PYVERSION := $(shell $(PYTHON) -c "import sys; print sys.version[0:3]")
default: clean package
diff --git a/solaris/pkginfo.bcfg2 b/solaris/pkginfo.bcfg2
index 09d91eb1f..b95b24f75 100644
--- a/solaris/pkginfo.bcfg2
+++ b/solaris/pkginfo.bcfg2
@@ -1,7 +1,7 @@
PKG="SCbcfg2"
NAME="bcfg2"
ARCH="sparc"
-VERSION="1.3.0rc1"
+VERSION="1.3.0rc2"
CATEGORY="application"
VENDOR="Argonne National Labratory"
EMAIL="bcfg-dev@mcs.anl.gov"
diff --git a/solaris/pkginfo.bcfg2-server b/solaris/pkginfo.bcfg2-server
index f7f6258d1..25b0bfa4b 100644
--- a/solaris/pkginfo.bcfg2-server
+++ b/solaris/pkginfo.bcfg2-server
@@ -1,7 +1,7 @@
PKG="SCbcfg2-server"
NAME="bcfg2-server"
ARCH="sparc"
-VERSION="1.3.0rc1"
+VERSION="1.3.0rc2"
CATEGORY="application"
VENDOR="Argonne National Labratory"
EMAIL="bcfg-dev@mcs.anl.gov"
diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py
index f230aacb7..baf8a14f2 100644
--- a/src/lib/Bcfg2/Client/Frame.py
+++ b/src/lib/Bcfg2/Client/Frame.py
@@ -1,14 +1,12 @@
""" Frame is the Client Framework that verifies and installs entries,
and generates statistics. """
-import os
-import sys
import time
-import select
import fnmatch
import logging
import Bcfg2.Client.Tools
-from Bcfg2.Compat import input, any, all # pylint: disable=W0622
+from Bcfg2.Client import prompt
+from Bcfg2.Compat import any, all # pylint: disable=W0622
def cmpent(ent1, ent2):
@@ -154,7 +152,7 @@ class Frame(object):
for entry in multi:
self.logger.debug(entry)
- def promptFilter(self, prompt, entries):
+ def promptFilter(self, msg, entries):
"""Filter a supplied list based on user input."""
ret = []
entries.sort(cmpent)
@@ -165,20 +163,9 @@ class Frame(object):
if 'qtext' in entry.attrib:
iprompt = entry.get('qtext')
else:
- iprompt = prompt % (entry.tag, entry.get('name'))
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- try:
- ans = input(iprompt.encode(sys.stdout.encoding, 'replace'))
- if ans in ['y', 'Y']:
- ret.append(entry)
- except EOFError:
- # python 2.4.3 on CentOS doesn't like ^C for some reason
- break
- except:
- print("Error while reading input")
- continue
+ iprompt = msg % (entry.tag, entry.get('name'))
+ if prompt(iprompt):
+ ret.append(entry)
return ret
def __getattr__(self, name):
@@ -281,7 +268,7 @@ class Frame(object):
def Decide(self): # pylint: disable=R0912
"""Set self.whitelist based on user interaction."""
- prompt = "Install %s: %s? (y/N): "
+ iprompt = "Install %s: %s? (y/N): "
rprompt = "Remove %s: %s? (y/N): "
if self.setup['remove']:
if self.setup['remove'] == 'all':
@@ -353,7 +340,7 @@ class Frame(object):
(bmodified or a.get('when') == 'always'))]
# now we process all "always actions"
if self.setup['interactive']:
- self.promptFilter(prompt, actions)
+ self.promptFilter(iprompt, actions)
self.DispatchInstallCalls(actions)
if bundle.tag != 'Bundle':
@@ -379,7 +366,7 @@ class Frame(object):
if b.get("name")))
if self.setup['interactive']:
- self.whitelist = self.promptFilter(prompt, self.whitelist)
+ self.whitelist = self.promptFilter(iprompt, self.whitelist)
self.removal = self.promptFilter(rprompt, self.removal)
for entry in candidates:
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py b/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
index 896ca5f49..64a0b1e15 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
@@ -12,5 +12,4 @@ class POSIXHardlink(POSIXLinkTool):
return os.path.samefile(entry.get('name'), entry.get('to'))
def _link(self, entry):
- ## TODO: set permissions
return os.link(entry.get('to'), entry.get('name'))
diff --git a/src/lib/Bcfg2/Client/Tools/SELinux.py b/src/lib/Bcfg2/Client/Tools/SELinux.py
index 08d943251..451495be2 100644
--- a/src/lib/Bcfg2/Client/Tools/SELinux.py
+++ b/src/lib/Bcfg2/Client/Tools/SELinux.py
@@ -360,7 +360,7 @@ class SELinuxEntryHandler(object):
""" find extra entries of this entry type """
specified = [self._key(e)
for e in self.tool.getSupportedEntries()
- if e.get("type") == self.etype]
+ if e.tag == "SE%s" % self.etype.title()]
try:
records = self.custom_records
except ValueError:
diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py
index a0dfe6dd9..4539a6a36 100644
--- a/src/lib/Bcfg2/Client/Tools/YUM.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM.py
@@ -603,17 +603,18 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
if stat['verify'] != {}:
stat['verify_fail'] = True
package_fail = True
- self.logger.debug("It is suggested that you either manage "
- "these files, revert the changes, or ignore "
- "false failures:")
- self.logger.debug(" Verify Problems:")
+ self.logger.info("It is suggested that you either manage "
+ "these files, revert the changes, or ignore "
+ "false failures:")
+ self.logger.info(" Verify Problems: %s" %
+ stat['pkg'].get('name'))
for fname, probs in list(stat['verify'].items()):
if len(probs) > 1:
- self.logger.debug(" %s" % fname)
+ self.logger.info(" %s" % fname)
for prob in probs:
- self.logger.debug(" %s" % prob)
+ self.logger.info(" %s" % prob[1])
else:
- self.logger.debug(" %s: %s" % (fname, probs[0]))
+ self.logger.info(" %s: %s" % (fname, probs[0]))
if len(all_pkg_objs) > 0:
# Is this an install only package? We just look at the first one
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index cd86a2a4b..41759655d 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -3,10 +3,10 @@
import os
import sys
import stat
-import select
+import Bcfg2.Client
import Bcfg2.Client.XML
from Bcfg2.Utils import Executor, ClassName
-from Bcfg2.Compat import input, walk_packages # pylint: disable=W0622
+from Bcfg2.Compat import walk_packages # pylint: disable=W0622
__all__ = [m[1] for m in walk_packages(path=__path__)]
@@ -113,25 +113,34 @@ class Tool(object):
#: A list of all entries handled by this tool
self.handled = []
- for struct in config:
+ self._analyze_config()
+ self._check_execs()
+
+ def _analyze_config(self):
+ """ Analyze the config at tool initialization-time for
+ important and handled entries """
+ for struct in self.config:
for entry in struct:
if (entry.tag == 'Path' and
entry.get('important', 'false').lower() == 'true'):
self.__important__.append(entry.get('name'))
- if self.handlesEntry(entry):
- self.handled.append(entry)
+ self.handled = self.getSupportedEntries()
+
+ def _check_execs(self):
+ """ Check all executables used by this tool to ensure that
+ they exist and are executable """
for filename in self.__execs__:
try:
mode = stat.S_IMODE(os.stat(filename)[stat.ST_MODE])
- if mode & stat.S_IEXEC != stat.S_IEXEC:
- raise ToolInstantiationError("%s: %s not executable" %
- (self.name, filename))
except OSError:
raise ToolInstantiationError(sys.exc_info()[1])
except:
raise ToolInstantiationError("%s: Failed to stat %s" %
- (self.name, filename),
- exc_info=1)
+ (self.name, filename))
+ if not mode & stat.S_IEXEC:
+ raise ToolInstantiationError("%s: %s not executable" %
+ (self.name, filename))
+
def BundleUpdated(self, bundle, states): # pylint: disable=W0613
""" Callback that is invoked when a bundle has been updated.
@@ -185,11 +194,13 @@ class Tool(object):
if self.canVerify(entry):
try:
func = getattr(self, "Verify%s" % entry.tag)
- states[entry] = func(entry, mods)
except AttributeError:
self.logger.error("%s: Cannot verify %s entries" %
(self.name, entry.tag))
- except:
+ continue
+ try:
+ states[entry] = func(entry, mods)
+ except: # pylint: disable=W0702
self.logger.error("%s: Unexpected failure verifying %s"
% (self.name,
self.primarykey(entry)),
@@ -213,14 +224,16 @@ class Tool(object):
:returns: None """
for entry in entries:
try:
- func = getattr(self, "Install%s" % (entry.tag))
- states[entry] = func(entry)
- if states[entry]:
- self.modified.append(entry)
+ func = getattr(self, "Install%s" % entry.tag)
except AttributeError:
self.logger.error("%s: Cannot install %s entries" %
(self.name, entry.tag))
- except:
+ continue
+ try:
+ states[entry] = func(entry)
+ if states[entry]:
+ self.modified.append(entry)
+ except: # pylint: disable=W0702
self.logger.error("%s: Unexpected failure installing %s" %
(self.name, self.primarykey(entry)),
exc_info=1)
@@ -409,6 +422,19 @@ class PkgTool(Tool):
"""
raise NotImplementedError
+ def _get_package_command(self, packages):
+ """ Get the command to install the given list of packages.
+
+ :param packages: The Package entries to install
+ :type packages: list of lxml.etree._Element
+ :returns: string - the command to run
+ """
+ pkgargs = " ".join(self.pkgtool[1][0] %
+ tuple(pkg.get(field)
+ for field in self.pkgtool[1][1])
+ for pkg in packages)
+ return self.pkgtool[0] % pkgargs
+
def Install(self, packages, states):
""" Run a one-pass install where all required packages are
installed with a single command, followed by single package
@@ -422,11 +448,6 @@ class PkgTool(Tool):
self.logger.info("Trying single pass package install for pkgtype %s" %
self.pkgtype)
- data = [tuple([pkg.get(field) for field in self.pkgtool[1][1]])
- for pkg in packages]
- pkgargs = " ".join([self.pkgtool[1][0] % datum for datum in data])
-
- self.logger.debug("Installing packages: %s" % pkgargs)
if self.cmd.run(self.pkgtool[0] % pkgargs):
self.logger.info("Single Pass Succeded")
# set all package states to true and flush workqueues
@@ -436,7 +457,7 @@ class PkgTool(Tool):
and entry.get('type') == self.pkgtype
and entry.get('name') in pkgnames):
self.logger.debug('Setting state to true for pkg %s' %
- (entry.get('name')))
+ entry.get('name'))
states[entry] = True
self.RefreshPackages()
else:
@@ -452,18 +473,13 @@ class PkgTool(Tool):
else:
self.logger.info("Installing pkg %s version %s" %
(pkg.get('name'), pkg.get('version')))
- if self.cmd.run(
- self.pkgtool[0] %
- (self.pkgtool[1][0] %
- tuple([pkg.get(field)
- for field in self.pkgtool[1][1]]))):
+ if self.cmd.run(self._get_package_command([pkg])):
states[pkg] = True
else:
self.logger.error("Failed to install package %s" %
- (pkg.get('name')))
+ pkg.get('name'))
self.RefreshPackages()
- for entry in [ent for ent in packages if states[ent]]:
- self.modified.append(entry)
+ self.modified.extend(entry for entry in packages if states[entry])
def RefreshPackages(self):
""" Refresh the internal representation of the package
@@ -557,11 +573,13 @@ class SvcTool(Tool):
if self.setup['servicemode'] == 'disabled':
return
- for entry in [ent for ent in bundle if self.handlesEntry(ent)]:
- restart = entry.get("restart", "true")
- if (restart.lower() == "false" or
- (restart.lower() == "interactive" and
- not self.setup['interactive'])):
+ for entry in bundle:
+ if not self.handlesEntry(entry):
+ continue
+
+ restart = entry.get("restart", "true").lower()
+ if (restart == "false" or
+ (restart == "interactive" and not self.setup['interactive'])):
continue
success = False
@@ -570,14 +588,8 @@ class SvcTool(Tool):
success = self.stop_service(entry)
elif entry.get('name') not in self.restarted:
if self.setup['interactive']:
- prompt = ('Restart service %s?: (y/N): ' %
- entry.get('name'))
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [],
- 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- ans = input(prompt)
- if ans not in ['y', 'Y']:
+ if not Bcfg2.Client.prompt('Restart service %s? (y/N) '
+ % entry.get('name')):
continue
success = self.restart_service(entry)
if success:
@@ -593,8 +605,8 @@ class SvcTool(Tool):
install_entries = []
for entry in entries:
if entry.get('install', 'true').lower() == 'false':
- self.logger.info("Service %s installation is false. Skipping "
- "installation." % (entry.get('name')))
+ self.logger.info("Installation is false for %s:%s, skipping" %
+ (entry.tag, entry.get('name')))
else:
install_entries.append(entry)
return Tool.Install(self, install_entries, states)
diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py
index c03021f14..dd5ae1e83 100644
--- a/src/lib/Bcfg2/Client/__init__.py
+++ b/src/lib/Bcfg2/Client/__init__.py
@@ -1,3 +1,31 @@
"""This contains all Bcfg2 Client modules"""
__all__ = ["Frame", "Tools", "XML", "Client"]
+
+import os
+import sys
+import select
+from Bcfg2.Compat import input # pylint: disable=W0622
+
+
+def prompt(msg):
+ """ Helper to give a yes/no prompt to the user. Flushes input
+ buffers, handles exceptions, etc. Returns True if the user
+ answers in the affirmative, False otherwise.
+
+ :param msg: The message to show to the user. The message is not
+ altered in any way for display; i.e., it should
+ contain "[y/N]" if desired, etc.
+ :type msg: string
+ :returns: bool - True if yes, False if no """
+ while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
+ os.read(sys.stdin.fileno(), 4096)
+ try:
+ ans = input(msg.encode(sys.stdout.encoding, 'replace'))
+ return ans in ['y', 'Y']
+ except EOFError:
+ # python 2.4.3 on CentOS doesn't like ^C for some reason
+ return False
+ except:
+ print("Error while reading input: %s" % sys.exc_info()[1])
+ return False
diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py
index b0f0ef5cf..beb534791 100644
--- a/src/lib/Bcfg2/Compat.py
+++ b/src/lib/Bcfg2/Compat.py
@@ -260,3 +260,10 @@ def oct_mode(mode):
:type mode: int
:returns: string """
return oct(mode).replace('o', '')
+
+
+try:
+ long = long
+except NameError:
+ # longs are just ints in py3k
+ long = int
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index fb7af7465..bca4a9c1e 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -224,7 +224,11 @@ class DjangoORM(StorageBase):
inter.extra_count = counter_fields[TYPE_EXTRA]
inter.save()
for entry_type in updates.keys():
- getattr(inter, entry_type).add(*updates[entry_type])
+ # batch this for sqlite
+ i = 0
+ while(i < len(updates[entry_type])):
+ getattr(inter, entry_type).add(*updates[entry_type][i:i+100])
+ i += 100
# performance metrics
for times in stats.findall('OpStamps'):
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index c7850f4af..ab2dc8418 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -392,7 +392,13 @@ class BaseEntry(models.Model):
@classmethod
def prune_orphans(cls):
'''Remove unused entries'''
- cls.objects.filter(interaction__isnull=True).delete()
+ # yeat another sqlite hack
+ cls_orphans = [x['id'] \
+ for x in cls.objects.filter(interaction__isnull=True).values("id")]
+ i = 0
+ while i < len(cls_orphans):
+ cls.objects.filter(id__in=cls_orphans[i:i+100]).delete()
+ i += 100
class SuccessEntry(BaseEntry):
diff --git a/src/lib/Bcfg2/Reporting/templates/base.html b/src/lib/Bcfg2/Reporting/templates/base.html
index 1ec6b8c24..533dcc79e 100644
--- a/src/lib/Bcfg2/Reporting/templates/base.html
+++ b/src/lib/Bcfg2/Reporting/templates/base.html
@@ -88,7 +88,7 @@
<div style='clear:both'></div>
</div><!-- document -->
<div id="footer">
- <span>Bcfg2 Version 1.3.0rc1</span>
+ <span>Bcfg2 Version 1.3.0rc2</span>
</div>
<div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/index.html b/src/lib/Bcfg2/Reporting/templates/clients/index.html
index 45ba20b86..d9c415c20 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/index.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/index.html
@@ -30,6 +30,9 @@
{% endif %}
{% endfor %}
</table>
-{% else %}<p>No client records are available.</p>
+{% else %}
+ <div class='client_list_box'>
+ <p>No client records are available.</p>
+ </div>
{% endif %}
{% endblock %}
diff --git a/src/lib/Bcfg2/Reporting/views.py b/src/lib/Bcfg2/Reporting/views.py
index 8ab3f8e59..0341a18af 100644
--- a/src/lib/Bcfg2/Reporting/views.py
+++ b/src/lib/Bcfg2/Reporting/views.py
@@ -213,8 +213,11 @@ def entry_status(request, entry_type, pk, timestamp=None, **kwargs):
# There is no good way to do this...
items = []
- for it in cls.objects.filter(interaction__in=current_clients, name=item.name).distinct("id").select_related():
- items.append((it, it.interaction_set.filter(pk__in=current_clients).order_by('client__name').select_related('client')))
+ seen = []
+ for it in cls.objects.filter(interaction__in=current_clients, name=item.name).select_related():
+ if it.pk not in seen:
+ items.append((it, it.interaction_set.filter(pk__in=current_clients).order_by('client__name').select_related('client')))
+ seen.append(it.pk)
return render_to_response('config_items/entry_status.html',
{'entry': item,
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index f93ca0a7b..1b0ecadf5 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -332,9 +332,23 @@ class BaseCore(object):
self.fam.handle_event_set(self.lock)
except:
continue
- # VCS plugin periodic updates
- for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Version):
- self.revision = plugin.get_revision()
+ self._update_vcs_revision()
+
+ @track_statistics()
+ def _update_vcs_revision(self):
+ """ Update the revision of the current configuration on-disk
+ from the VCS plugin """
+ for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Version):
+ try:
+ newrev = plugin.get_revision()
+ if newrev != self.revision:
+ self.logger.debug("Updated to revision %s" % newrev)
+ self.revision = newrev
+ break
+ except:
+ self.logger.warning("Error getting revision from %s: %s" %
+ (plugin.name, sys.exc_info()[1]))
+ self.revision = '-1'
def init_plugin(self, plugin):
""" Import and instantiate a single plugin. The plugin is
@@ -360,8 +374,8 @@ class BaseCore(object):
try:
plug = getattr(mod, plugin.split('.')[-1])
except AttributeError:
- self.logger.error("Failed to load plugin %s (AttributeError)" %
- plugin)
+ self.logger.error("Failed to load plugin %s: %s" %
+ (plugin, sys.exc_info()[1]))
return
# Blacklist conflicting plugins
cplugs = [conflict for conflict in plug.conflicts
@@ -536,18 +550,16 @@ class BaseCore(object):
continue
try:
self.Bind(entry, metadata)
- except PluginExecutionError:
- exc = sys.exc_info()[1]
- if 'failure' not in entry.attrib:
- entry.set('failure', 'bind error: %s' % exc)
- self.logger.error("Failed to bind entry %s:%s: %s" %
- (entry.tag, entry.get('name'), exc))
- except Exception:
+ except:
exc = sys.exc_info()[1]
if 'failure' not in entry.attrib:
entry.set('failure', 'bind error: %s' % exc)
- self.logger.error("Unexpected failure in BindStructure: %s %s"
- % (entry.tag, entry.get('name')), exc_info=1)
+ if isinstance(exc, PluginExecutionError):
+ msg = "Failed to bind entry"
+ else:
+ msg = "Unexpected failure binding entry"
+ self.logger.error("%s %s:%s: %s" %
+ (msg, entry.tag, entry.get('name'), exc))
def Bind(self, entry, metadata):
""" Bind a single entry using the appropriate generator.
diff --git a/src/lib/Bcfg2/Server/Encryption.py b/src/lib/Bcfg2/Server/Encryption.py
index c931ed2a7..b46337eb0 100755
--- a/src/lib/Bcfg2/Server/Encryption.py
+++ b/src/lib/Bcfg2/Server/Encryption.py
@@ -30,6 +30,9 @@ CFG_SECTION = "encryption"
#: The config option used to store the algorithm
CFG_ALGORITHM = "algorithm"
+#: The config option used to store the decryption strictness
+CFG_DECRYPT = "decrypt"
+
#: Default cipher algorithm. To get a full list of valid algorithms,
#: you can run::
#:
@@ -40,6 +43,7 @@ ALGORITHM = Bcfg2.Options.get_option_parser().cfp.get( # pylint: disable=E1103
CFG_ALGORITHM,
default="aes_256_cbc").lower().replace("-", "_")
+
Rand.rand_seed(os.urandom(1024))
@@ -165,7 +169,7 @@ def get_passphrases():
if setup.cfp.has_section(CFG_SECTION):
return dict([(o, setup.cfp.get(CFG_SECTION, o))
for o in setup.cfp.options(CFG_SECTION)
- if o != CFG_ALGORITHM])
+ if o not in [CFG_ALGORITHM, CFG_DECRYPT]])
else:
return dict()
diff --git a/src/lib/Bcfg2/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py
index ecea1ad1b..8bfb76461 100644
--- a/src/lib/Bcfg2/Server/Lint/Comments.py
+++ b/src/lib/Bcfg2/Server/Lint/Comments.py
@@ -130,7 +130,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
rtype)
def check_plaintext(self, filename, data, rtype):
- """ check generic plaintex files for required headers """
+ """ check generic plaintext files for required headers """
self.check_lines(filename, data.splitlines(), rtype)
def check_lines(self, filename, lines, rtype):
diff --git a/src/lib/Bcfg2/Server/Lint/Genshi.py b/src/lib/Bcfg2/Server/Lint/Genshi.py
index 437e69d82..ed0d9930f 100755
--- a/src/lib/Bcfg2/Server/Lint/Genshi.py
+++ b/src/lib/Bcfg2/Server/Lint/Genshi.py
@@ -1,8 +1,11 @@
""" Check Genshi templates for syntax errors """
import sys
-import genshi.template
import Bcfg2.Server.Lint
+from genshi.template import TemplateLoader, NewTextTemplate, MarkupTemplate, \
+ TemplateSyntaxError
+from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile
+from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator
class Genshi(Bcfg2.Server.Lint.ServerPlugin):
@@ -10,29 +13,40 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
def Run(self):
""" run plugin """
- loader = genshi.template.TemplateLoader()
if 'Cfg' in self.core.plugins:
- self.check_files(self.core.plugins['Cfg'].entries,
- loader=loader)
+ self.check_cfg()
+ if 'Bundler' in self.core.plugins:
+ self.check_bundler()
@classmethod
def Errors(cls):
return {"genshi-syntax-error": "error"}
- def check_files(self, entries, loader=None):
- """ Check genshi templates in a list of entries for syntax
- errors """
- if loader is None:
- loader = genshi.template.TemplateLoader()
-
- for eset in entries.values():
- for fname, sdata in list(eset.entries.items()):
- if (self.HandlesFile(fname) and
- (fname.endswith(".genshi") or fname.endswith(".newtxt"))):
+ def check_cfg(self):
+ """ Check genshi templates in Cfg for syntax errors """
+ for entryset in self.core.plugins['Cfg'].entries.values():
+ for entry in entryset.entries.values():
+ if (self.HandlesFile(entry.name) and
+ isinstance(entry, CfgGenshiGenerator) and
+ not entry.template):
try:
- loader.load(sdata.name,
- cls=genshi.template.NewTextTemplate)
- except genshi.template.TemplateSyntaxError:
+ entry.loader.load(entry.name,
+ cls=NewTextTemplate)
+ except TemplateSyntaxError:
err = sys.exc_info()[1]
self.LintError("genshi-syntax-error",
"Genshi syntax error: %s" % err)
+
+ def check_bundler(self):
+ """ Check templates in Bundler for syntax errors """
+ loader = TemplateLoader()
+
+ for entry in self.core.plugins['Bundler'].entries.values():
+ if (self.HandlesFile(entry.name) and
+ isinstance(entry, BundleTemplateFile)):
+ try:
+ loader.load(entry.name, cls=MarkupTemplate)
+ except TemplateSyntaxError:
+ err = sys.exc_info()[1]
+ self.LintError("genshi-syntax-error",
+ "Genshi syntax error: %s" % err)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
index b702ac899..313e53ee9 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
@@ -23,10 +23,15 @@ class CfgExternalCommandVerifier(CfgVerifier):
def verify_entry(self, entry, metadata, data):
try:
proc = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- err = proc.communicate(input=data)[1]
+ out, err = proc.communicate(input=data)
rv = proc.wait()
if rv != 0:
- raise CfgVerificationError(err)
+ # pylint: disable=E1103
+ raise CfgVerificationError(err.strip() or out.strip() or
+ "Non-zero return value %s" % rv)
+ # pylint: enable=E1103
+ except CfgVerificationError:
+ raise
except:
err = sys.exc_info()[1]
raise CfgVerificationError("Error running external command "
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index c11939bd5..5f10879be 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -166,6 +166,7 @@ class CfgGenshiGenerator(CfgGenerator):
raise
def handle_event(self, event):
+ CfgGenerator.handle_event(self, event)
try:
self.template = self.loader.load(self.name, cls=NewTextTemplate,
encoding=self.encoding)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 81adea42f..2301de725 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -553,11 +553,10 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
try:
self._validate_data(entry, metadata, data)
except CfgVerificationError:
- msg = "Data for %s for %s failed to verify: %s" % \
- (entry.get('name'), metadata.hostname,
- sys.exc_info()[1])
- self.logger.error(msg)
- raise PluginExecutionError(msg)
+ raise PluginExecutionError("Failed to verify %s for %s: %s" %
+ (entry.get('name'),
+ metadata.hostname,
+ sys.exc_info()[1]))
if entry.get('encoding') == 'base64':
data = b64encode(data)
diff --git a/src/lib/Bcfg2/Server/Plugins/Defaults.py b/src/lib/Bcfg2/Server/Plugins/Defaults.py
index f4d86a64f..04c14aa96 100644
--- a/src/lib/Bcfg2/Server/Plugins/Defaults.py
+++ b/src/lib/Bcfg2/Server/Plugins/Defaults.py
@@ -5,7 +5,7 @@ import Bcfg2.Server.Plugins.Rules
class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
- Bcfg2.Server.Plugin.StructureValidator):
+ Bcfg2.Server.Plugin.GoalValidator):
"""Set default attributes on bound entries"""
__author__ = 'bcfg-dev@mcs.anl.gov'
@@ -22,27 +22,18 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
def HandleEvent(self, event):
Bcfg2.Server.Plugin.XMLDirectoryBacked.HandleEvent(self, event)
- def validate_structures(self, metadata, structures):
+ def validate_goals(self, metadata, config):
""" Apply defaults """
- for struct in structures:
+ for struct in config.getchildren():
for entry in struct.getchildren():
- if entry.tag.startswith("Bound"):
- is_bound = True
- entry.tag = entry.tag[5:]
- else:
- is_bound = False
try:
- try:
- self.BindEntry(entry, metadata)
- except Bcfg2.Server.Plugin.PluginExecutionError:
- # either no matching defaults (which is okay),
- # or multiple matching defaults (which is not
- # okay, but is logged). either way, we don't
- # care about the error.
- pass
- finally:
- if is_bound:
- entry.tag = "Bound" + entry.tag
+ self.BindEntry(entry, metadata)
+ except Bcfg2.Server.Plugin.PluginExecutionError:
+ # either no matching defaults (which is okay),
+ # or multiple matching defaults (which is not
+ # okay, but is logged). either way, we don't
+ # care about the error.
+ pass
@property
def _regex_enabled(self):
diff --git a/src/lib/Bcfg2/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index 8cc63a46f..c8362db41 100644
--- a/src/lib/Bcfg2/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -2,7 +2,7 @@
git. """
import sys
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import Version, PluginExecutionError
from subprocess import Popen, PIPE
try:
@@ -12,16 +12,16 @@ except ImportError:
HAS_GITPYTHON = False
-class Git(Bcfg2.Server.Plugin.Version):
+class Git(Version):
""" The Git plugin provides a revision interface for Bcfg2 repos
using git. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".git"
if HAS_GITPYTHON:
- __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
+ __rmi__ = Version.__rmi__ + ['Update']
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
+ Version.__init__(self, core, datastore)
if HAS_GITPYTHON:
self.repo = git.Repo(self.vcs_root)
else:
@@ -31,6 +31,11 @@ class Git(Bcfg2.Server.Plugin.Version):
self.logger.debug("Initialized git plugin with git directory %s" %
self.vcs_path)
+ def _log_git_cmd(self, output):
+ """ Send output from a GitPython command to the debug log """
+ for line in output.strip().splitlines():
+ self.debug_log("Git: %s" % line)
+
def get_revision(self):
"""Read git revision information for the Bcfg2 repository."""
try:
@@ -46,11 +51,9 @@ class Git(Bcfg2.Server.Plugin.Version):
raise Exception(err)
return rv
except:
- err = sys.exc_info()[1]
- msg = "Git: Error getting revision from %s: %s" % (self.vcs_root,
- err)
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Git: Error getting revision from %s: "
+ "%s" % (self.vcs_root,
+ sys.exc_info()[1]))
def Update(self, ref=None):
""" Git.Update() => True|False
@@ -60,20 +63,25 @@ class Git(Bcfg2.Server.Plugin.Version):
self.debug_log("Git: Performing garbage collection on repo at %s" %
self.vcs_root)
try:
- self.repo.git.gc('--auto')
+ self._log_git_cmd(self.repo.git.gc('--auto'))
except git.GitCommandError:
self.logger.warning("Git: Failed to perform garbage collection: %s"
% sys.exc_info()[1])
+ self.debug_log("Git: Fetching all refs for repo at %s" % self.vcs_root)
+ try:
+ self._log_git_cmd(self.repo.git.fetch('--all'))
+ except git.GitCommandError:
+ self.logger.warning("Git: Failed to fetch refs: %s" %
+ sys.exc_info()[1])
+
if ref:
self.debug_log("Git: Checking out %s" % ref)
try:
- self.repo.git.checkout('-f', ref)
+ self._log_git_cmd(self.repo.git.checkout('-f', ref))
except git.GitCommandError:
- err = sys.exc_info()[1]
- msg = "Git: Failed to checkout %s: %s" % (ref, err)
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Git: Failed to checkout %s: %s" %
+ (ref, sys.exc_info()[1]))
# determine if we should try to pull to get the latest commit
# on this head
@@ -87,12 +95,10 @@ class Git(Bcfg2.Server.Plugin.Version):
self.debug_log("Git: %s is a tracking branch, pulling from %s" %
(self.repo.head.ref.name, tracking))
try:
- self.repo.git.pull("--rebase")
- except: # pylint: disable=W0702
- err = sys.exc_info()[1]
- msg = "Git: Failed to pull from upstream: %s" % err
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ self._log_git_cmd(self.repo.git.pull("--rebase"))
+ except git.GitCommandError:
+ raise PluginExecutionError("Git: Failed to pull from "
+ "upstream: %s" % sys.exc_info()[1])
self.logger.info("Git: Repo at %s updated to %s" %
(self.vcs_root, self.get_revision()))
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index e568c5c65..b053e65d3 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -1452,7 +1452,7 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
defaults = []
for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \
self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"):
- if grp.get("default"):
+ if grp.get("default", "false").lower() == "true":
defaults.append(self.RenderXML(grp))
if len(defaults) > 1:
self.LintError("multiple-default-groups",
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
index 3ad64b242..59eefe143 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
@@ -533,7 +533,7 @@ class Collection(list, Bcfg2.Server.Plugin.Debuggable):
# should be resolved
current = pkgs.pop()
self.debug_log("Packages: handling package requirement %s" %
- current)
+ (current,))
packages.add(current)
deps = self.get_deps(current)
newdeps = set(deps).difference(examined)
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index 4057ed230..775caaa08 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -684,6 +684,21 @@ class YumCollection(Collection):
return self.call_helper("get_groups", inputdata=gdicts)
+ def _element_to_pkg(self, el, name):
+ """ Convert a Package or Instance element to a package tuple """
+ rv = (name, el.get("arch"), el.get("epoch"),
+ el.get("version"), el.get("release"))
+ if rv[3] in ['any', 'auto']:
+ rv = (rv[0], rv[1], rv[2], None, None)
+ # if a package requires no specific version, we just use
+ # the name, not the tuple. this limits the amount of JSON
+ # encoding/decoding that has to be done to pass the
+ # package list to bcfg2-yum-helper.
+ if rv[1:] == (None, None, None, None):
+ return name
+ else:
+ return rv
+
def packages_from_entry(self, entry):
""" When using the Python yum libraries, convert a Package
entry to a list of package tuples. See :ref:`yum-pkg-objects`
@@ -693,32 +708,42 @@ class YumCollection(Collection):
:type entry: lxml.etree._Element
:returns: list of tuples
"""
+ if not self.use_yum:
+ return Collection.packages_from_entry(self, entry)
+
rv = set()
name = entry.get("name")
- def _tag_to_pkg(tag):
- """ Convert a Package or Instance tag to a package tuple """
- rv = (name, tag.get("arch"), tag.get("epoch"),
- tag.get("version"), tag.get("release"))
- if rv[3] in ['any', 'auto']:
- rv = (rv[0], rv[1], rv[2], None, None)
- # if a package requires no specific version, we just use
- # the name, not the tuple. this limits the amount of JSON
- # encoding/decoding that has to be done to pass the
- # package list to bcfg2-yum-helper.
- if rv[1:] == (None, None, None, None):
- return name
- else:
- return rv
-
for inst in entry.getchildren():
if inst.tag != "Instance":
continue
- rv.add(_tag_to_pkg(inst))
+ rv.add(self._element_to_pkg(inst, name))
if not rv:
- rv.add(_tag_to_pkg(entry))
+ rv.add(self._element_to_pkg(entry, name))
return list(rv)
+ def _get_entry_attrs(self, pkgtup):
+ """ Given a package tuple, return a dict of attributes
+ suitable for applying to either a Package or an Instance
+ tag """
+ attrs = dict(version=self.setup.cfp.get("packages", "version",
+ default="auto"))
+ if attrs['version'] == 'any' or not isinstance(pkgtup, tuple):
+ return attrs
+
+ try:
+ if pkgtup[1]:
+ attrs['arch'] = pkgtup[1]
+ if pkgtup[2]:
+ attrs['epoch'] = pkgtup[2]
+ if pkgtup[3]:
+ attrs['version'] = pkgtup[3]
+ if pkgtup[4]:
+ attrs['release'] = pkgtup[4]
+ except IndexError:
+ self.logger.warning("Malformed package tuple: %s" % pkgtup)
+ return attrs
+
def packages_to_entry(self, pkglist, entry):
""" When using the Python yum libraries, convert a list of
package tuples to a Package entry. See :ref:`yum-pkg-objects`
@@ -738,28 +763,8 @@ class YumCollection(Collection):
:type entry: lxml.etree._Element
:returns: None
"""
- def _get_entry_attrs(pkgtup):
- """ Given a package tuple, return a dict of attributes
- suitable for applying to either a Package or an Instance
- tag """
- attrs = dict(version=self.setup.cfp.get("packages",
- "version",
- default="auto"))
- if attrs['version'] == 'any' or not isinstance(pkgtup, tuple):
- return attrs
-
- try:
- if pkgtup[1]:
- attrs['arch'] = pkgtup[1]
- if pkgtup[2]:
- attrs['epoch'] = pkgtup[2]
- if pkgtup[3]:
- attrs['version'] = pkgtup[3]
- if pkgtup[4]:
- attrs['release'] = pkgtup[4]
- except IndexError:
- self.logger.warning("Malformed package tuple: %s" % pkgtup)
- return attrs
+ if not self.use_yum:
+ return Collection.packages_to_entry(self, pkglist, entry)
packages = dict()
for pkg in pkglist:
@@ -776,9 +781,9 @@ class YumCollection(Collection):
**pkgattrs)
for inst in instances:
lxml.etree.SubElement(pkg_el, "Instance",
- _get_entry_attrs(inst))
+ self._get_entry_attrs(inst))
else:
- attrs = _get_entry_attrs(instances[0])
+ attrs = self._get_entry_attrs(instances[0])
attrs.update(pkgattrs)
lxml.etree.SubElement(entry, 'BoundPackage', **attrs)
@@ -803,7 +808,11 @@ class YumCollection(Collection):
initial_names.append(pkg)
new = []
for pkg in complete:
- if pkg[0] not in initial_names:
+ if isinstance(pkg, tuple):
+ name = pkg[0]
+ else:
+ name = pkg
+ if name not in initial_names:
new.append(pkg)
return new
diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
index cc1a2ceac..ab2f80552 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
@@ -180,7 +180,7 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet):
self.logger.error("SSLCA: Failed to unlink temporary files: %s"
% sys.exc_info()[1])
if cert_spec['append_chain'] and 'chaincert' in ca:
- cert += open(self.parent.get_ca(ca)['chaincert']).read()
+ cert += open(ca['chaincert']).read()
open(os.path.join(self.path, filename), 'w').write(cert)
return cert
@@ -363,7 +363,8 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
""" The SSLCA generator handles the creation and management of ssl
certificates and their keys. """
__author__ = 'g.hagger@gmail.com'
- es_cls = lambda self, *args: SSLCAEntrySet(*args, parent=self)
+ # python 2.5 doesn't support mixing *magic and keyword arguments
+ es_cls = lambda self, *args: SSLCAEntrySet(*args, **dict(parent=self))
es_child_cls = SSLCADataFile
def get_ca(self, name):
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
index e3f8ed749..ad3eb65bc 100644
--- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -12,12 +12,25 @@ LOGGER = logging.getLogger(__name__)
MODULE_RE = re.compile(r'(?P<filename>(?P<module>[^\/]+)\.py)$')
+def safe_module_name(module):
+ """ Munge the name of a TemplateHelper module to avoid collisions
+ with other Python modules. E.g., if someone has a helper named
+ 'ldap.py', it should not be added to ``sys.modules`` as ``ldap``,
+ but rather as something more obscure. """
+ return '__TemplateHelper_%s' % module
+
+
class HelperModule(object):
""" Representation of a TemplateHelper module """
def __init__(self, name):
self.name = name
+
+ #: The name of the module as used by get_additional_data().
+ #: the name of the file with .py stripped off.
self._module_name = MODULE_RE.search(self.name).group('module')
+
+ #: The attributes exported by this module
self._attrs = []
def HandleEvent(self, event=None):
@@ -31,7 +44,8 @@ class HelperModule(object):
return
try:
- module = imp.load_source(self._module_name, self.name)
+ module = imp.load_source(safe_module_name(self._module_name),
+ self.name)
except: # pylint: disable=W0702
err = sys.exc_info()[1]
LOGGER.error("TemplateHelper: Failed to import %s: %s" %
@@ -97,7 +111,7 @@ class TemplateHelperLint(Bcfg2.Server.Lint.ServerPlugin):
module_name = MODULE_RE.search(helper).group(1)
try:
- module = imp.load_source(module_name, helper)
+ module = imp.load_source(safe_module_name(module_name), helper)
except: # pylint: disable=W0702
err = sys.exc_info()[1]
self.LintError("templatehelper-import-error",
diff --git a/src/lib/Bcfg2/Server/Reports/updatefix.py b/src/lib/Bcfg2/Server/Reports/updatefix.py
index b377806ab..cb131c29d 100644
--- a/src/lib/Bcfg2/Server/Reports/updatefix.py
+++ b/src/lib/Bcfg2/Server/Reports/updatefix.py
@@ -80,6 +80,9 @@ def _populate_interaction_entry_counts():
cursor.close()
+def update_noop():
+ return True
+
# be sure to test your upgrade query before reflecting the change in the models
# the list of function and sql command to do should go here
_fixes = [_merge_database_table_entries,
@@ -103,6 +106,8 @@ _fixes = [_merge_database_table_entries,
_interactions_constraint_or_idx,
'alter table reports_reason add is_binary bool NOT NULL default False;',
'alter table reports_reason add is_sensitive bool NOT NULL default False;',
+ update_noop, #_remove_table_column('reports_interaction', 'client_version'),
+ "alter table reports_reason add unpruned varchar(1280) not null default 'N/A';",
]
# this will calculate the last possible version of the database
diff --git a/src/lib/Bcfg2/settings.py b/src/lib/Bcfg2/settings.py
index da6e2036e..d819ee534 100644
--- a/src/lib/Bcfg2/settings.py
+++ b/src/lib/Bcfg2/settings.py
@@ -136,6 +136,8 @@ if HAS_SOUTH:
'south',
'Bcfg2.Reporting',
)
+if 'BCFG2_LEGACY_MODELS' in os.environ:
+ INSTALLED_APPS += ('Bcfg2.Server.Reports.reports',)
# Imported from Bcfg2.Server.Reports
MEDIA_ROOT = ''
diff --git a/src/lib/Bcfg2/version.py b/src/lib/Bcfg2/version.py
index 86803afcb..8223d7543 100644
--- a/src/lib/Bcfg2/version.py
+++ b/src/lib/Bcfg2/version.py
@@ -2,7 +2,7 @@
import re
-__version__ = "1.3.0rc1"
+__version__ = "1.3.0rc2"
class Bcfg2VersionInfo(tuple):
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 557a03405..4f81df89c 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -50,7 +50,8 @@ def run_plugin(plugin, plugin_name, errorhandler=None, args=None, files=None):
else:
args.append(setup)
- return plugin(*args, files=files, errorhandler=errorhandler).Run()
+ # python 2.5 doesn't support mixing *magic and keyword arguments
+ return plugin(*args, **dict(files=files, errorhandler=errorhandler)).Run()
def get_errorhandler():
diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test
index 28023a0f2..75079ec3f 100755
--- a/src/sbin/bcfg2-test
+++ b/src/sbin/bcfg2-test
@@ -3,7 +3,9 @@
"""This tool verifies that all clients known to the server build
without failures"""
+import os
import sys
+import signal
import fnmatch
import logging
import Bcfg2.Logger
@@ -12,6 +14,7 @@ from nose.core import TestProgram
from nose.suite import LazySuite
from unittest import TestCase
+
class ClientTest(TestCase):
"""
A test case representing the build of all of the configuration for
@@ -20,7 +23,7 @@ class ClientTest(TestCase):
files that we know will cause errors (because they are private
files we don't have access to, for instance)
"""
- __test__ = False # Do not collect
+ __test__ = False # Do not collect
def __init__(self, bcfg2_core, client, ignore=None):
TestCase.__init__(self)
@@ -44,10 +47,16 @@ class ClientTest(TestCase):
return True
return False
+ def shortDescription(self):
+ return "Building configuration for %s" % self.client
+
def runTest(self):
""" run this individual test """
config = self.bcfg2_core.BuildConfiguration(self.client)
+ assert len(config.findall("Bundle")) > 0, \
+ "%s has no content" % self.client
+
failures = []
msg = ["Failures:"]
for failure in config.xpath('//*[@failure]'):
@@ -63,6 +72,20 @@ class ClientTest(TestCase):
id = __str__
+
+def get_sigint_handler(core):
+ """ Get a function that handles SIGINT/Ctrl-C by shutting down the
+ core and exiting properly."""
+
+ def hdlr(sig, frame): # pylint: disable=W0613
+ """ Handle SIGINT/Ctrl-C by shutting down the core and exiting
+ properly. """
+ core.shutdown()
+ os._exit(1) # pylint: disable=W0212
+
+ return hdlr
+
+
def main():
optinfo = dict(noseopts=Bcfg2.Options.TEST_NOSEOPTS,
test_ignore=Bcfg2.Options.TEST_IGNORE,
@@ -75,10 +98,22 @@ def main():
setup.buildHelpMessage()
setup.parse(sys.argv[1:])
- if setup['verbose']:
- Bcfg2.Logger.setup_logging("bcfg2-test", to_syslog=setup['syslog'])
+ if setup['debug']:
+ level = logging.DEBUG
+ elif setup['verbose']:
+ level = logging.INFO
+ else:
+ level = logging.WARNING
+ Bcfg2.Logger.setup_logging("bcfg2-test",
+ to_console=setup['verbose'] or setup['debug'],
+ to_syslog=False,
+ to_file=setup['logging'],
+ level=level)
+ if (setup['debug'] or setup['verbose']) and "-v" not in setup['noseopts']:
+ setup['noseopts'].append("-v")
core = Bcfg2.Server.Core.BaseCore()
+ signal.signal(signal.SIGINT, get_sigint_handler(core))
ignore = dict()
for entry in setup['test_ignore']:
@@ -88,21 +123,23 @@ def main():
except KeyError:
ignore[tag] = [name]
- def run_tests():
- """ Run the test suite """
- core.fam.handle_events_in_interval(0.1)
+ core.fam.handle_events_in_interval(0.1)
- if setup['args']:
- clients = setup['args']
- else:
- clients = core.metadata.clients
+ if setup['args']:
+ clients = setup['args']
+ else:
+ clients = core.metadata.clients
+ def run_tests():
+ """ Run the test suite """
for client in clients:
- logging.info("Building %s" % client)
yield ClientTest(core, client, ignore)
TestProgram(argv=sys.argv[0:1] + setup['noseopts'],
suite=LazySuite(run_tests))
+ core.shutdown()
+ os._exit(0) # pylint: disable=W0212
+
if __name__ == "__main__":
sys.exit(main())
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDevice.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDevice.py
index 9f396b0b6..2f5eeb166 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDevice.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDevice.py
@@ -14,10 +14,10 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
from Testbase import TestPOSIXTool
from common import *
+
class TestPOSIXDevice(TestPOSIXTool):
test_obj = POSIXDevice
@@ -37,48 +37,48 @@ class TestPOSIXDevice(TestPOSIXTool):
entry.set("major", "0")
entry.set("minor", "0")
self.assertTrue(ptool.fully_specified(entry))
-
+
@patch("os.major")
@patch("os.minor")
- @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool._exists")
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
- def test_verify(self, mock_verify, mock_exists, mock_minor, mock_major):
+ def test_verify(self, mock_verify, mock_minor, mock_major):
entry = lxml.etree.Element("Path", name="/test", type="device",
mode='0644', owner='root', group='root',
dev_type="block", major="0", minor="10")
ptool = self.get_obj()
+ ptool._exists = Mock()
def reset():
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_verify.reset_mock()
mock_minor.reset_mock()
mock_major.reset_mock()
- mock_exists.return_value = False
+ ptool._exists.return_value = False
self.assertFalse(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
reset()
- mock_exists.return_value = MagicMock()
+ ptool._exists.return_value = MagicMock()
mock_major.return_value = 0
mock_minor.return_value = 10
mock_verify.return_value = True
self.assertTrue(ptool.verify(entry, []))
mock_verify.assert_called_with(ptool, entry, [])
- mock_exists.assert_called_with(entry)
- mock_major.assert_called_with(mock_exists.return_value.st_rdev)
- mock_minor.assert_called_with(mock_exists.return_value.st_rdev)
+ ptool._exists.assert_called_with(entry)
+ mock_major.assert_called_with(ptool._exists.return_value.st_rdev)
+ mock_minor.assert_called_with(ptool._exists.return_value.st_rdev)
reset()
- mock_exists.return_value = MagicMock()
+ ptool._exists.return_value = MagicMock()
mock_major.return_value = 0
mock_minor.return_value = 10
mock_verify.return_value = False
self.assertFalse(ptool.verify(entry, []))
mock_verify.assert_called_with(ptool, entry, [])
- mock_exists.assert_called_with(entry)
- mock_major.assert_called_with(mock_exists.return_value.st_rdev)
- mock_minor.assert_called_with(mock_exists.return_value.st_rdev)
+ ptool._exists.assert_called_with(entry)
+ mock_major.assert_called_with(ptool._exists.return_value.st_rdev)
+ mock_minor.assert_called_with(ptool._exists.return_value.st_rdev)
reset()
mock_verify.return_value = True
@@ -86,26 +86,26 @@ class TestPOSIXDevice(TestPOSIXTool):
mode='0644', owner='root', group='root',
dev_type="fifo")
self.assertTrue(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_verify.assert_called_with(ptool, entry, [])
self.assertFalse(mock_major.called)
self.assertFalse(mock_minor.called)
-
+
@patch("os.makedev")
@patch("os.mknod")
- @patch("Bcfg2.Client.Tools.POSIX.Device.%s._exists" % test_obj.__name__)
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
- def test_install(self, mock_install, mock_exists, mock_mknod, mock_makedev):
+ def test_install(self, mock_install, mock_mknod, mock_makedev):
entry = lxml.etree.Element("Path", name="/test", type="device",
mode='0644', owner='root', group='root',
dev_type="block", major="0", minor="10")
ptool = self.get_obj()
+ ptool._exists = Mock()
- mock_exists.return_value = False
+ ptool._exists.return_value = False
mock_makedev.return_value = Mock()
mock_install.return_value = True
self.assertTrue(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
+ ptool._exists.assert_called_with(entry, remove=True)
mock_makedev.assert_called_with(0, 10)
mock_mknod.assert_called_with(entry.get("name"), # 0o644
device_map[entry.get("dev_type")] | 420,
@@ -114,29 +114,29 @@ class TestPOSIXDevice(TestPOSIXTool):
mock_makedev.reset_mock()
mock_mknod.reset_mock()
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_install.reset_mock()
mock_makedev.side_effect = OSError
self.assertFalse(ptool.install(entry))
mock_makedev.reset_mock()
mock_mknod.reset_mock()
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_install.reset_mock()
mock_mknod.side_effect = OSError
self.assertFalse(ptool.install(entry))
-
+
mock_makedev.reset_mock()
mock_mknod.reset_mock()
- mock_exists.reset_mock()
- mock_install.reset_mock()
+ ptool._exists.reset_mock()
+ mock_install.reset_mock()
mock_mknod.side_effect = None
entry = lxml.etree.Element("Path", name="/test", type="device",
mode='0644', owner='root', group='root',
dev_type="fifo")
-
+
self.assertTrue(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
+ ptool._exists.assert_called_with(entry, remove=True)
mock_mknod.assert_called_with(entry.get("name"), # 0o644
device_map[entry.get("dev_type")] | 420)
mock_install.assert_called_with(ptool, entry)
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDirectory.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDirectory.py
index 16490808e..d9431dc63 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDirectory.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestDirectory.py
@@ -15,46 +15,47 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
from Testbase import TestPOSIXTool
from common import *
+
class TestPOSIXDirectory(TestPOSIXTool):
test_obj = POSIXDirectory
@patch("os.listdir")
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
- @patch("Bcfg2.Client.Tools.POSIX.Directory.%s._exists" % test_obj.__name__)
- def test_verify(self, mock_exists, mock_verify, mock_listdir):
+ def test_verify(self, mock_verify, mock_listdir):
+ ptool = self.get_obj()
+ ptool._exists = Mock()
entry = lxml.etree.Element("Path", name="/test", type="directory",
mode='0644', owner='root', group='root')
- mock_exists.return_value = False
- self.assertFalse(self.ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.return_value = False
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._exists.assert_called_with(entry)
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
exists_rv = MagicMock()
exists_rv.__getitem__.return_value = stat.S_IFREG | 420 # 0o644
- mock_exists.return_value = exists_rv
- self.assertFalse(self.ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.return_value = exists_rv
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._exists.assert_called_with(entry)
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_verify.return_value = False
exists_rv.__getitem__.return_value = stat.S_IFDIR | 420 # 0o644
- self.assertFalse(self.ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
- mock_verify.assert_called_with(self.ptool, entry, [])
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._exists.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, [])
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_verify.reset_mock()
mock_verify.return_value = True
- self.assertTrue(self.ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
- mock_verify.assert_called_with(self.ptool, entry, [])
+ self.assertTrue(ptool.verify(entry, []))
+ ptool._exists.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, [])
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_verify.reset_mock()
entry.set("prune", "true")
orig_entry = copy.deepcopy(entry)
@@ -62,9 +63,9 @@ class TestPOSIXDirectory(TestPOSIXTool):
entries = ["foo", "bar", "bar/baz"]
mock_listdir.return_value = entries
modlist = [os.path.join(entry.get("name"), entries[0])]
- self.assertFalse(self.ptool.verify(entry, modlist))
- mock_exists.assert_called_with(entry)
- mock_verify.assert_called_with(self.ptool, entry, modlist)
+ self.assertFalse(ptool.verify(entry, modlist))
+ ptool._exists.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, modlist)
mock_listdir.assert_called_with(entry.get("name"))
expected = [os.path.join(entry.get("name"), e)
for e in entries
@@ -73,67 +74,65 @@ class TestPOSIXDirectory(TestPOSIXTool):
self.assertItemsEqual(expected, actual)
mock_verify.reset_mock()
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_listdir.reset_mock()
entry = copy.deepcopy(orig_entry)
modlist = [os.path.join(entry.get("name"), e)
for e in entries]
- self.assertTrue(self.ptool.verify(entry, modlist))
- mock_exists.assert_called_with(entry)
- mock_verify.assert_called_with(self.ptool, entry, modlist)
+ self.assertTrue(ptool.verify(entry, modlist))
+ ptool._exists.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, modlist)
mock_listdir.assert_called_with(entry.get("name"))
self.assertEqual(len(entry.findall("Prune")), 0)
@patch("os.unlink")
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
- @patch("Bcfg2.Client.Tools.POSIX.Directory.%s._exists" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.Directory.%s._makedirs" %
- test_obj.__name__)
- def test_install(self, mock_makedirs, mock_exists, mock_install,
- mock_unlink):
+ def test_install(self, mock_install, mock_unlink):
entry = lxml.etree.Element("Path", name="/test/foo/bar",
type="directory", mode='0644',
owner='root', group='root')
- self.ptool._makedirs = Mock()
- self.ptool._remove = Mock()
+ ptool = self.get_obj()
+ ptool._exists = Mock()
+ ptool._makedirs = Mock()
+ ptool._remove = Mock()
def reset():
- mock_exists.reset_mock()
+ ptool._exists.reset_mock()
mock_install.reset_mock()
mock_unlink.reset_mock()
- self.ptool._makedirs.reset_mock()
- self.ptool._remove.reset_mock()
+ ptool._makedirs.reset_mock()
+ ptool._remove.reset_mock()
- self.ptool._makedirs.return_value = True
- mock_exists.return_value = False
+ ptool._makedirs.return_value = True
+ ptool._exists.return_value = False
mock_install.return_value = True
- self.assertTrue(self.ptool.install(entry))
- mock_exists.assert_called_with(entry)
- mock_install.assert_called_with(self.ptool, entry)
- self.ptool._makedirs.assert_called_with(entry)
+ self.assertTrue(ptool.install(entry))
+ ptool._exists.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
+ ptool._makedirs.assert_called_with(entry)
reset()
exists_rv = MagicMock()
exists_rv.__getitem__.return_value = stat.S_IFREG | 420 # 0o644
- mock_exists.return_value = exists_rv
- self.assertTrue(self.ptool.install(entry))
+ ptool._exists.return_value = exists_rv
+ self.assertTrue(ptool.install(entry))
mock_unlink.assert_called_with(entry.get("name"))
- mock_exists.assert_called_with(entry)
- self.ptool._makedirs.assert_called_with(entry)
- mock_install.assert_called_with(self.ptool, entry)
+ ptool._exists.assert_called_with(entry)
+ ptool._makedirs.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
reset()
exists_rv.__getitem__.return_value = stat.S_IFDIR | 420 # 0o644
mock_install.return_value = True
- self.assertTrue(self.ptool.install(entry))
- mock_exists.assert_called_with(entry)
- mock_install.assert_called_with(self.ptool, entry)
+ self.assertTrue(ptool.install(entry))
+ ptool._exists.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
reset()
mock_install.return_value = False
- self.assertFalse(self.ptool.install(entry))
- mock_install.assert_called_with(self.ptool, entry)
+ self.assertFalse(ptool.install(entry))
+ mock_install.assert_called_with(ptool, entry)
entry.set("prune", "true")
prune = ["/test/foo/bar/prune1", "/test/foo/bar/prune2"]
@@ -143,9 +142,9 @@ class TestPOSIXDirectory(TestPOSIXTool):
reset()
mock_install.return_value = True
- self.assertTrue(self.ptool.install(entry))
- mock_exists.assert_called_with(entry)
- mock_install.assert_called_with(self.ptool, entry)
+ self.assertTrue(ptool.install(entry))
+ ptool._exists.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
self.assertItemsEqual([c[0][0].get("path")
- for c in self.ptool._remove.call_args_list],
+ for c in ptool._remove.call_args_list],
prune)
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestFile.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestFile.py
index 69b021421..662e0e1b6 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestFile.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestFile.py
@@ -3,9 +3,8 @@ import os
import sys
import copy
import difflib
-import binascii
import lxml.etree
-from Bcfg2.Compat import b64encode, b64decode, u_str
+from Bcfg2.Compat import b64encode, u_str
from mock import Mock, MagicMock, patch
from Bcfg2.Client.Tools.POSIX.File import *
@@ -18,47 +17,46 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
-from Testbase import TestPOSIXTool
+from TestPOSIX.Testbase import TestPOSIXTool
from common import *
-def get_file_object(posix=None):
- if posix is None:
- posix = get_posix_object()
- return POSIXFile(posix.logger, posix.setup, posix.config)
class TestPOSIXFile(TestPOSIXTool):
test_obj = POSIXFile
def test_fully_specified(self):
+ ptool = self.get_obj()
+
entry = lxml.etree.Element("Path", name="/test", type="file")
- self.assertFalse(self.ptool.fully_specified(entry))
+ self.assertFalse(ptool.fully_specified(entry))
entry.set("empty", "true")
- self.assertTrue(self.ptool.fully_specified(entry))
+ self.assertTrue(ptool.fully_specified(entry))
entry.set("empty", "false")
entry.text = "text"
- self.assertTrue(self.ptool.fully_specified(entry))
-
+ self.assertTrue(ptool.fully_specified(entry))
+
def test_is_string(self):
+ ptool = self.get_obj()
+
for char in list(range(8)) + list(range(14, 32)):
- self.assertFalse(self.ptool._is_string("foo" + chr(char) + "bar",
+ self.assertFalse(ptool._is_string("foo" + chr(char) + "bar",
'UTF-8'))
for char in list(range(9, 14)) + list(range(33, 128)):
- self.assertTrue(self.ptool._is_string("foo" + chr(char) + "bar",
+ self.assertTrue(ptool._is_string("foo" + chr(char) + "bar",
'UTF-8'))
ustr = 'é'
- self.assertTrue(self.ptool._is_string(ustr, 'UTF-8'))
+ self.assertTrue(ptool._is_string(ustr, 'UTF-8'))
if not inPy3k:
- self.assertFalse(self.ptool._is_string("foo" + chr(128) + "bar",
+ self.assertFalse(ptool._is_string("foo" + chr(128) + "bar",
'ascii'))
- self.assertFalse(self.ptool._is_string(ustr, 'ascii'))
+ self.assertFalse(ptool._is_string(ustr, 'ascii'))
def test_get_data(self):
orig_entry = lxml.etree.Element("Path", name="/test", type="file")
- setup = dict(encoding="ascii", ppath='/', max_copies=5)
- ptool = self.get_obj(posix=get_posix_object(setup=setup))
+ ptool = self.get_obj(setup=dict(encoding="ascii", ppath='/',
+ max_copies=5))
entry = copy.deepcopy(orig_entry)
entry.text = b64encode("test")
@@ -83,124 +81,122 @@ class TestPOSIXFile(TestPOSIXTool):
@patch("%s.open" % builtins)
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._exists" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._get_data" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._get_diffs" % test_obj.__name__)
- def test_verify(self, mock_get_diffs, mock_get_data, mock_exists,
- mock_verify, mock_open):
+ def test_verify(self, mock_verify, mock_open):
entry = lxml.etree.Element("Path", name="/test", type="file")
- setup = dict(interactive=False, ppath='/', max_copies=5)
- ptool = self.get_obj(posix=get_posix_object(setup=setup))
+ ptool = self.get_obj(setup=dict(interactive=False, ppath='/',
+ max_copies=5))
+ ptool._exists = Mock()
+ ptool._get_data = Mock()
+ ptool._get_diffs = Mock()
def reset():
- mock_get_diffs.reset_mock()
- mock_get_data.reset_mock()
- mock_exists.reset_mock()
+ ptool._get_diffs.reset_mock()
+ ptool._get_data.reset_mock()
+ ptool._exists.reset_mock()
mock_verify.reset_mock()
mock_open.reset_mock()
- mock_get_data.return_value = ("test", False)
- mock_exists.return_value = False
+ ptool._get_data.return_value = ("test", False)
+ ptool._exists.return_value = False
mock_verify.return_value = True
self.assertFalse(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_verify.assert_called_with(ptool, entry, [])
- mock_get_diffs.assert_called_with(entry, interactive=False,
- sensitive=False,
- is_binary=False,
+ ptool._get_diffs.assert_called_with(entry, interactive=False,
+ sensitive=False, is_binary=False,
content="")
reset()
exists_rv = MagicMock()
exists_rv.__getitem__.return_value = 5
- mock_exists.return_value = exists_rv
- mock_get_data.return_value = ("test", True)
+ ptool._exists.return_value = exists_rv
+ ptool._get_data.return_value = ("test", True)
self.assertFalse(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_verify.assert_called_with(ptool, entry, [])
- mock_get_diffs.assert_called_with(entry, interactive=False,
- sensitive=False,
- is_binary=True,
+ ptool._get_diffs.assert_called_with(entry, interactive=False,
+ sensitive=False, is_binary=True,
content=None)
-
+
reset()
- mock_get_data.return_value = ("test", False)
+ ptool._get_data.return_value = ("test", False)
exists_rv.__getitem__.return_value = 4
entry.set("sensitive", "true")
mock_open.return_value.read.return_value = "tart"
self.assertFalse(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_verify.assert_called_with(ptool, entry, [])
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_called_with()
- mock_get_diffs.assert_called_with(entry, interactive=False,
- sensitive=True,
- is_binary=False,
+ ptool._get_diffs.assert_called_with(entry, interactive=False,
+ sensitive=True, is_binary=False,
content="tart")
reset()
mock_open.return_value.read.return_value = "test"
self.assertTrue(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_verify.assert_called_with(ptool, entry, [])
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_called_with()
- self.assertFalse(mock_get_diffs.called)
+ self.assertFalse(ptool._get_diffs.called)
reset()
mock_open.side_effect = IOError
self.assertFalse(ptool.verify(entry, []))
- mock_exists.assert_called_with(entry)
+ ptool._exists.assert_called_with(entry)
mock_open.assert_called_with(entry.get("name"))
-
+
@patch("os.fdopen")
@patch("tempfile.mkstemp")
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._get_data" % test_obj.__name__)
- def test_write_tmpfile(self, mock_get_data, mock_mkstemp, mock_fdopen):
+ def test_write_tmpfile(self, mock_mkstemp, mock_fdopen):
+ ptool = self.get_obj()
+ ptool._get_data = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file",
mode='0644', owner='root', group='root')
newfile = "/foo/bar"
def reset():
- mock_get_data.reset_mock()
+ ptool._get_data.reset_mock()
mock_mkstemp.reset_mock()
mock_fdopen.reset_mock()
- mock_get_data.return_value = ("test", False)
+ ptool._get_data.return_value = ("test", False)
mock_mkstemp.return_value = (5, newfile)
- self.assertEqual(self.ptool._write_tmpfile(entry), newfile)
- mock_get_data.assert_called_with(entry)
+ self.assertEqual(ptool._write_tmpfile(entry), newfile)
+ ptool._get_data.assert_called_with(entry)
mock_mkstemp.assert_called_with(prefix='test', dir='/')
mock_fdopen.assert_called_with(5, 'w')
mock_fdopen.return_value.write.assert_called_with("test")
reset()
mock_mkstemp.side_effect = OSError
- self.assertFalse(self.ptool._write_tmpfile(entry))
+ self.assertFalse(ptool._write_tmpfile(entry))
mock_mkstemp.assert_called_with(prefix='test', dir='/')
reset()
mock_mkstemp.side_effect = None
mock_fdopen.side_effect = OSError
- self.assertFalse(self.ptool._write_tmpfile(entry))
+ self.assertFalse(ptool._write_tmpfile(entry))
mock_mkstemp.assert_called_with(prefix='test', dir='/')
- mock_get_data.assert_called_with(entry)
+ ptool._get_data.assert_called_with(entry)
mock_fdopen.assert_called_with(5, 'w')
-
+
@patch("os.rename")
@patch("os.unlink")
def test_rename_tmpfile(self, mock_unlink, mock_rename):
+ ptool = self.get_obj()
entry = lxml.etree.Element("Path", name="/test", type="file",
mode='0644', owner='root', group='root')
newfile = "/foo/bar"
- self.assertTrue(self.ptool._rename_tmpfile(newfile, entry))
+ self.assertTrue(ptool._rename_tmpfile(newfile, entry))
mock_rename.assert_called_with(newfile, entry.get("name"))
-
+
mock_rename.reset_mock()
mock_unlink.reset_mock()
mock_rename.side_effect = OSError
- self.assertFalse(self.ptool._rename_tmpfile(newfile, entry))
+ self.assertFalse(ptool._rename_tmpfile(newfile, entry))
mock_rename.assert_called_with(newfile, entry.get("name"))
mock_unlink.assert_called_with(newfile)
@@ -208,58 +204,57 @@ class TestPOSIXFile(TestPOSIXTool):
mock_rename.reset_mock()
mock_unlink.reset_mock()
mock_unlink.side_effect = OSError
- self.assertFalse(self.ptool._rename_tmpfile(newfile, entry))
+ self.assertFalse(ptool._rename_tmpfile(newfile, entry))
mock_rename.assert_called_with(newfile, entry.get("name"))
mock_unlink.assert_called_with(newfile)
@patch("%s.open" % builtins)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._diff" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._get_data" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._is_string" % test_obj.__name__)
- def test__get_diffs(self, mock_is_string, mock_get_data, mock_diff,
- mock_open):
+ def test__get_diffs(self, mock_open):
orig_entry = lxml.etree.Element("Path", name="/test", type="file",
mode='0644', owner='root',
group='root')
orig_entry.text = "test"
ondisk = "test2"
- setup = dict(encoding="utf-8", ppath='/', max_copies=5)
- ptool = self.get_obj(posix=get_posix_object(setup=setup))
+ ptool = self.get_obj(setup=dict(encoding="utf-8", ppath='/',
+ max_copies=5))
+ ptool._get_data = Mock()
+ ptool._diff = Mock()
+ ptool._is_string = Mock()
def reset():
- mock_is_string.reset_mock()
- mock_get_data.reset_mock()
- mock_diff.reset_mock()
+ ptool._is_string.reset_mock()
+ ptool._get_data.reset_mock()
+ ptool._diff.reset_mock()
mock_open.reset_mock()
return copy.deepcopy(orig_entry)
-
- mock_is_string.return_value = True
- mock_get_data.return_value = (orig_entry.text, False)
+
+ ptool._is_string.return_value = True
+ ptool._get_data.return_value = (orig_entry.text, False)
mock_open.return_value.read.return_value = ondisk
- mock_diff.return_value = ["-test2", "+test"]
+ ptool._diff.return_value = ["-test2", "+test"]
# binary data in the entry
entry = reset()
ptool._get_diffs(entry, is_binary=True)
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_any_call()
- self.assertFalse(mock_diff.called)
+ self.assertFalse(ptool._diff.called)
self.assertEqual(entry.get("current_bfile"), b64encode(ondisk))
# binary data on disk
entry = reset()
- mock_is_string.return_value = False
+ ptool._is_string.return_value = False
ptool._get_diffs(entry, content=ondisk)
self.assertFalse(mock_open.called)
- self.assertFalse(mock_diff.called)
+ self.assertFalse(ptool._diff.called)
self.assertEqual(entry.get("current_bfile"), b64encode(ondisk))
# sensitive, non-interactive -- do nothing
entry = reset()
- mock_is_string.return_value = True
+ ptool._is_string.return_value = True
ptool._get_diffs(entry, sensitive=True, interactive=False)
self.assertFalse(mock_open.called)
- self.assertFalse(mock_diff.called)
+ self.assertFalse(ptool._diff.called)
self.assertXMLEqual(entry, orig_entry)
# sensitive, interactive
@@ -267,8 +262,9 @@ class TestPOSIXFile(TestPOSIXTool):
ptool._get_diffs(entry, sensitive=True, interactive=True)
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_any_call()
- mock_diff.assert_called_with(ondisk, entry.text, difflib.unified_diff,
- filename=entry.get("name"))
+ ptool._diff.assert_called_with(ondisk, entry.text,
+ difflib.unified_diff,
+ filename=entry.get("name"))
self.assertIsNotNone(entry.get("qtext"))
del entry.attrib['qtext']
self.assertItemsEqual(orig_entry.attrib, entry.attrib)
@@ -277,11 +273,11 @@ class TestPOSIXFile(TestPOSIXTool):
entry = reset()
ptool._get_diffs(entry, content=ondisk)
self.assertFalse(mock_open.called)
- mock_diff.assert_called_with(ondisk, entry.text, difflib.ndiff,
+ ptool._diff.assert_called_with(ondisk, entry.text, difflib.ndiff,
filename=entry.get("name"))
self.assertIsNone(entry.get("qtext"))
self.assertEqual(entry.get("current_bdiff"),
- b64encode("\n".join(mock_diff.return_value)))
+ b64encode("\n".join(ptool._diff.return_value)))
del entry.attrib["current_bdiff"]
self.assertItemsEqual(orig_entry.attrib, entry.attrib)
@@ -292,7 +288,7 @@ class TestPOSIXFile(TestPOSIXTool):
ptool._get_diffs(entry, interactive=True)
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_any_call()
- self.assertItemsEqual(mock_diff.call_args_list,
+ self.assertItemsEqual(ptool._diff.call_args_list,
[call(ondisk, entry.text, difflib.unified_diff,
filename=entry.get("name")),
call(ondisk, entry.text, difflib.ndiff,
@@ -300,7 +296,7 @@ class TestPOSIXFile(TestPOSIXTool):
self.assertIsNotNone(entry.get("qtext"))
self.assertTrue(entry.get("qtext").startswith("test\n"))
self.assertEqual(entry.get("current_bdiff"),
- b64encode("\n".join(mock_diff.return_value)))
+ b64encode("\n".join(ptool._diff.return_value)))
del entry.attrib['qtext']
del entry.attrib["current_bdiff"]
self.assertItemsEqual(orig_entry.attrib, entry.attrib)
@@ -308,118 +304,118 @@ class TestPOSIXFile(TestPOSIXTool):
# non-sensitive, interactive with unicode data
entry = reset()
entry.text = u("tëst")
- encoded = entry.text.encode(setup['encoding'])
- mock_diff.return_value = ["-test2", "+tëst"]
- mock_get_data.return_value = (encoded, False)
+ encoded = entry.text.encode(ptool.setup['encoding'])
+ ptool._diff.return_value = ["-test2", "+tëst"]
+ ptool._get_data.return_value = (encoded, False)
ptool._get_diffs(entry, interactive=True)
mock_open.assert_called_with(entry.get("name"))
mock_open.return_value.read.assert_any_call()
- self.assertItemsEqual(mock_diff.call_args_list,
+ self.assertItemsEqual(ptool._diff.call_args_list,
[call(ondisk, encoded, difflib.unified_diff,
filename=entry.get("name")),
call(ondisk, encoded, difflib.ndiff,
filename=entry.get("name"))])
self.assertIsNotNone(entry.get("qtext"))
self.assertEqual(entry.get("current_bdiff"),
- b64encode("\n".join(mock_diff.return_value)))
+ b64encode("\n".join(ptool._diff.return_value)))
del entry.attrib['qtext']
del entry.attrib["current_bdiff"]
self.assertItemsEqual(orig_entry.attrib, entry.attrib)
@patch("os.path.exists")
@patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._makedirs" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._set_perms" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._write_tmpfile" %
- test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.File.%s._rename_tmpfile" %
- test_obj.__name__)
- def test_install(self, mock_rename, mock_write, mock_set_perms,
- mock_makedirs, mock_install, mock_exists):
+ def test_install(self, mock_install, mock_exists):
+ ptool = self.get_obj()
+ ptool._makedirs = Mock()
+ ptool._set_perms = Mock()
+ ptool._write_tmpfile = Mock()
+ ptool._rename_tmpfile = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file",
mode='0644', owner='root', group='root')
def reset():
- mock_rename.reset_mock()
- mock_write.reset_mock()
- mock_set_perms.reset_mock()
- mock_makedirs.reset_mock()
+ ptool._rename_tmpfile.reset_mock()
+ ptool._write_tmpfile.reset_mock()
+ ptool._set_perms.reset_mock()
+ ptool._makedirs.reset_mock()
mock_install.reset_mock()
mock_exists.reset_mock()
mock_exists.return_value = False
- mock_makedirs.return_value = False
- self.assertFalse(self.ptool.install(entry))
+ ptool._makedirs.return_value = False
+ self.assertFalse(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
-
+ ptool._makedirs.assert_called_with(entry, path="/")
+
reset()
- mock_makedirs.return_value = True
- mock_write.return_value = False
- self.assertFalse(self.ptool.install(entry))
+ ptool._makedirs.return_value = True
+ ptool._write_tmpfile.return_value = False
+ self.assertFalse(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
- mock_write.assert_called_with(entry)
+ ptool._makedirs.assert_called_with(entry, path="/")
+ ptool._write_tmpfile.assert_called_with(entry)
reset()
newfile = '/test.X987yS'
- mock_write.return_value = newfile
- mock_set_perms.return_value = False
- mock_rename.return_value = False
- self.assertFalse(self.ptool.install(entry))
+ ptool._write_tmpfile.return_value = newfile
+ ptool._set_perms.return_value = False
+ ptool._rename_tmpfile.return_value = False
+ self.assertFalse(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
- mock_write.assert_called_with(entry)
- mock_set_perms.assert_called_with(entry, path=newfile)
- mock_rename.assert_called_with(newfile, entry)
+ ptool._makedirs.assert_called_with(entry, path="/")
+ ptool._write_tmpfile.assert_called_with(entry)
+ ptool._set_perms.assert_called_with(entry, path=newfile)
+ ptool._rename_tmpfile.assert_called_with(newfile, entry)
reset()
- mock_rename.return_value = True
+ ptool._rename_tmpfile.return_value = True
mock_install.return_value = False
- self.assertFalse(self.ptool.install(entry))
+ self.assertFalse(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
- mock_write.assert_called_with(entry)
- mock_set_perms.assert_called_with(entry, path=newfile)
- mock_rename.assert_called_with(newfile, entry)
- mock_install.assert_called_with(self.ptool, entry)
+ ptool._makedirs.assert_called_with(entry, path="/")
+ ptool._write_tmpfile.assert_called_with(entry)
+ ptool._set_perms.assert_called_with(entry, path=newfile)
+ ptool._rename_tmpfile.assert_called_with(newfile, entry)
+ mock_install.assert_called_with(ptool, entry)
reset()
mock_install.return_value = True
- self.assertFalse(self.ptool.install(entry))
+ self.assertFalse(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
- mock_write.assert_called_with(entry)
- mock_set_perms.assert_called_with(entry, path=newfile)
- mock_rename.assert_called_with(newfile, entry)
- mock_install.assert_called_with(self.ptool, entry)
+ ptool._makedirs.assert_called_with(entry, path="/")
+ ptool._write_tmpfile.assert_called_with(entry)
+ ptool._set_perms.assert_called_with(entry, path=newfile)
+ ptool._rename_tmpfile.assert_called_with(newfile, entry)
+ mock_install.assert_called_with(ptool, entry)
reset()
- mock_set_perms.return_value = True
- self.assertTrue(self.ptool.install(entry))
+ ptool._set_perms.return_value = True
+ self.assertTrue(ptool.install(entry))
mock_exists.assert_called_with("/")
- mock_makedirs.assert_called_with(entry, path="/")
- mock_write.assert_called_with(entry)
- mock_set_perms.assert_called_with(entry, path=newfile)
- mock_rename.assert_called_with(newfile, entry)
- mock_install.assert_called_with(self.ptool, entry)
+ ptool._makedirs.assert_called_with(entry, path="/")
+ ptool._write_tmpfile.assert_called_with(entry)
+ ptool._set_perms.assert_called_with(entry, path=newfile)
+ ptool._rename_tmpfile.assert_called_with(newfile, entry)
+ mock_install.assert_called_with(ptool, entry)
reset()
mock_exists.return_value = True
- self.assertTrue(self.ptool.install(entry))
+ self.assertTrue(ptool.install(entry))
mock_exists.assert_called_with("/")
- self.assertFalse(mock_makedirs.called)
- mock_write.assert_called_with(entry)
- mock_set_perms.assert_called_with(entry, path=newfile)
- mock_rename.assert_called_with(newfile, entry)
- mock_install.assert_called_with(self.ptool, entry)
+ self.assertFalse(ptool._makedirs.called)
+ ptool._write_tmpfile.assert_called_with(entry)
+ ptool._set_perms.assert_called_with(entry, path=newfile)
+ ptool._rename_tmpfile.assert_called_with(newfile, entry)
+ mock_install.assert_called_with(ptool, entry)
@patch("time.time")
def test_diff(self, mock_time):
+ ptool = self.get_obj()
content1 = "line1\nline2"
content2 = "line3"
self.now = 1345640723
+
def time_rv():
self.now += 1
return self.now
@@ -428,7 +424,7 @@ class TestPOSIXFile(TestPOSIXTool):
rv = ["line1", "line2", "line3"]
func = Mock()
func.return_value = rv
- self.assertItemsEqual(self.ptool._diff(content1, content2, func), rv)
+ self.assertItemsEqual(ptool._diff(content1, content2, func), rv)
func.assert_called_with(["line1", "line2"], ["line3"])
func.reset_mock()
@@ -442,5 +438,5 @@ class TestPOSIXFile(TestPOSIXTool):
for i in range(1, 10):
yield "line%s" % i
func.side_effect = slow_diff
- self.assertFalse(self.ptool._diff(content1, content2, func), rv)
+ self.assertFalse(ptool._diff(content1, content2, func), rv)
func.assert_called_with(["line1", "line2"], ["line3"])
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestHardlink.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestHardlink.py
index c38e86aeb..3159b69df 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestHardlink.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestHardlink.py
@@ -14,70 +14,26 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
-from Testbase import TestPOSIXTool
+from Testbase import TestPOSIXLinkTool
from common import *
-class TestPOSIXHardlink(TestPOSIXTool):
+
+class TestPOSIXHardlink(TestPOSIXLinkTool):
test_obj = POSIXHardlink
@patch("os.path.samefile")
- @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
- def test_verify(self, mock_verify, mock_samefile):
+ def test__verify(self, mock_samefile):
entry = lxml.etree.Element("Path", name="/test", type="hardlink",
to="/dest")
ptool = self.get_obj()
-
- mock_samefile.return_value = True
- mock_verify.return_value = False
- self.assertFalse(ptool.verify(entry, []))
- mock_samefile.assert_called_with(entry.get("name"),
- entry.get("to"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- mock_samefile.reset_mock()
- mock_verify.reset_mock()
- mock_verify.return_value = True
- self.assertTrue(ptool.verify(entry, []))
- mock_samefile.assert_called_with(entry.get("name"),
- entry.get("to"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- mock_samefile.reset_mock()
- mock_verify.reset_mock()
- mock_samefile.return_value = False
- self.assertFalse(ptool.verify(entry, []))
- mock_samefile.assert_called_with(entry.get("name"),
- entry.get("to"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- mock_samefile.reset_mock()
- mock_verify.reset_mock()
- mock_samefile.side_effect = OSError
- self.assertFalse(ptool.verify(entry, []))
- mock_samefile.assert_called_with(entry.get("name"),
- entry.get("to"))
+ self.assertEqual(ptool._verify(entry), mock_samefile.return_value)
+ self.assertItemsEqual(mock_samefile.call_args[0],
+ [entry.get("name"), entry.get("to")])
@patch("os.link")
- @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
- @patch("Bcfg2.Client.Tools.POSIX.Hardlink.%s._exists" % test_obj.__name__)
- def test_install(self, mock_exists, mock_install, mock_link):
+ def test__link(self, mock_link):
entry = lxml.etree.Element("Path", name="/test", type="hardlink",
to="/dest")
ptool = self.get_obj()
-
- mock_exists.return_value = False
- mock_install.return_value = True
- self.assertTrue(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
- mock_link.assert_called_with(entry.get("to"), entry.get("name"))
- mock_install.assert_called_with(ptool, entry)
-
- mock_link.reset_mock()
- mock_exists.reset_mock()
- mock_install.reset_mock()
- mock_link.side_effect = OSError
- self.assertFalse(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
+ self.assertEqual(ptool._link(entry), mock_link.return_value)
mock_link.assert_called_with(entry.get("to"), entry.get("name"))
- mock_install.assert_called_with(ptool, entry)
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestNonexistent.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestNonexistent.py
index 583d17e32..3d1ddbf84 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestNonexistent.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestNonexistent.py
@@ -14,7 +14,7 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_config, get_posix_object
+from Test__init import get_config
from Testbase import TestPOSIXTool
from common import *
@@ -24,34 +24,36 @@ class TestPOSIXNonexistent(TestPOSIXTool):
@patch("os.path.lexists")
def test_verify(self, mock_lexists):
+ ptool = self.get_obj()
entry = lxml.etree.Element("Path", name="/test", type="nonexistent")
for val in [True, False]:
mock_lexists.reset_mock()
mock_lexists.return_value = val
- self.assertEqual(self.ptool.verify(entry, []), not val)
+ self.assertEqual(ptool.verify(entry, []), not val)
mock_lexists.assert_called_with(entry.get("name"))
def test_install(self):
entry = lxml.etree.Element("Path", name="/test", type="nonexistent")
- self.ptool._remove = Mock()
+ ptool = self.get_obj()
+ ptool._remove = Mock()
def reset():
- self.ptool._remove.reset_mock()
+ ptool._remove.reset_mock()
- self.assertTrue(self.ptool.install(entry))
- self.ptool._remove.assert_called_with(entry, recursive=False)
+ self.assertTrue(ptool.install(entry))
+ ptool._remove.assert_called_with(entry, recursive=False)
reset()
entry.set("recursive", "true")
- self.assertTrue(self.ptool.install(entry))
- self.ptool._remove.assert_called_with(entry, recursive=True)
+ self.assertTrue(ptool.install(entry))
+ ptool._remove.assert_called_with(entry, recursive=True)
reset()
child_entry = lxml.etree.Element("Path", name="/test/foo",
type="nonexistent")
- ptool = self.get_obj(posix=get_posix_object(config=get_config([child_entry])))
+ ptool = self.get_obj(config=get_config([child_entry]))
ptool._remove = Mock()
self.assertTrue(ptool.install(entry))
ptool._remove.assert_called_with(entry, recursive=True)
@@ -59,13 +61,13 @@ class TestPOSIXNonexistent(TestPOSIXTool):
reset()
child_entry = lxml.etree.Element("Path", name="/test/foo",
type="file")
- ptool = self.get_obj(posix=get_posix_object(config=get_config([child_entry])))
+ ptool = self.get_obj(config=get_config([child_entry]))
ptool._remove = Mock()
self.assertFalse(ptool.install(entry))
self.assertFalse(ptool._remove.called)
reset()
entry.set("recursive", "false")
- self.ptool._remove.side_effect = OSError
- self.assertFalse(self.ptool.install(entry))
- self.ptool._remove.assert_called_with(entry, recursive=False)
+ ptool._remove.side_effect = OSError
+ self.assertFalse(ptool.install(entry))
+ ptool._remove.assert_called_with(entry, recursive=False)
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestPermissions.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestPermissions.py
index 565857437..10194dbbf 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestPermissions.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestPermissions.py
@@ -1,5 +1,6 @@
from Bcfg2.Client.Tools.POSIX.Permissions import *
from Testbase import TestPOSIXTool
+
class TestPOSIXPermissions(TestPOSIXTool):
test_obj = POSIXPermissions
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestSymlink.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestSymlink.py
index 4c8ddfa3f..78bfd716f 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestSymlink.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/TestSymlink.py
@@ -1,6 +1,5 @@
import os
import sys
-import copy
import lxml.etree
from mock import Mock, MagicMock, patch
from Bcfg2.Client.Tools.POSIX.Symlink import *
@@ -14,84 +13,33 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
-from Testbase import TestPOSIXTool
+from Testbase import TestPOSIXLinkTool
from common import *
-class TestPOSIXSymlink(TestPOSIXTool):
+
+class TestPOSIXSymlink(TestPOSIXLinkTool):
test_obj = POSIXSymlink
@patch("os.readlink")
- @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
- def test_verify(self, mock_verify, mock_readlink):
+ def test__verify(self, mock_readlink):
entry = lxml.etree.Element("Path", name="/test", type="symlink",
to="/dest")
ptool = self.get_obj()
mock_readlink.return_value = entry.get("to")
- mock_verify.return_value = False
- self.assertFalse(ptool.verify(entry, []))
+ self.assertTrue(ptool._verify(entry))
mock_readlink.assert_called_with(entry.get("name"))
- mock_verify.assert_called_with(ptool, entry, [])
mock_readlink.reset_mock()
- mock_verify.reset_mock()
- mock_verify.return_value = True
- self.assertTrue(ptool.verify(entry, []))
- mock_readlink.assert_called_with(entry.get("name"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- mock_readlink.reset_mock()
- mock_verify.reset_mock()
mock_readlink.return_value = "/bogus"
- self.assertFalse(ptool.verify(entry, []))
- mock_readlink.assert_called_with(entry.get("name"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- # relative symlink
- mock_readlink.reset_mock()
- mock_verify.reset_mock()
- entry = lxml.etree.Element("Path", name="/test", type="symlink",
- to="dest")
- mock_readlink.return_value = entry.get("to")
- self.assertTrue(ptool.verify(entry, []))
- mock_readlink.assert_called_with(entry.get("name"))
- mock_verify.assert_called_with(ptool, entry, [])
-
- mock_readlink.reset_mock()
- mock_verify.reset_mock()
- mock_readlink.side_effect = OSError
- self.assertFalse(ptool.verify(entry, []))
+ self.assertFalse(ptool._verify(entry))
mock_readlink.assert_called_with(entry.get("name"))
@patch("os.symlink")
- @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
- @patch("Bcfg2.Client.Tools.POSIX.Symlink.%s._exists" % test_obj.__name__)
- def test_install(self, mock_exists, mock_install, mock_symlink):
+ def test__link(self, mock_symlink):
entry = lxml.etree.Element("Path", name="/test", type="symlink",
to="/dest")
ptool = self.get_obj()
-
- mock_exists.return_value = False
- mock_install.return_value = True
- self.assertTrue(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
- mock_symlink.assert_called_with(entry.get("to"), entry.get("name"))
- mock_install.assert_called_with(ptool, entry)
-
- # relative symlink
- entry = lxml.etree.Element("Path", name="/test", type="symlink",
- to="dest")
- self.assertTrue(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
- mock_symlink.assert_called_with(entry.get("to"), entry.get("name"))
- mock_install.assert_called_with(ptool, entry)
-
- mock_symlink.reset_mock()
- mock_exists.reset_mock()
- mock_install.reset_mock()
- mock_symlink.side_effect = OSError
- self.assertFalse(ptool.install(entry))
- mock_exists.assert_called_with(entry, remove=True)
+ self.assertEqual(ptool._link(entry),
+ mock_symlink.return_value)
mock_symlink.assert_called_with(entry.get("to"), entry.get("name"))
- mock_install.assert_called_with(ptool, entry)
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py
index bf7f1eecc..f01082e86 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Test__init.py
@@ -3,7 +3,7 @@ import sys
import lxml.etree
from mock import Mock, MagicMock, patch
import Bcfg2.Client.Tools
-import Bcfg2.Client.Tools.POSIX
+from Bcfg2.Client.Tools.POSIX import *
# add all parent testsuite directories to sys.path to allow (most)
# relative imports in python 2.4
@@ -15,6 +15,7 @@ while path != "/":
break
path = os.path.dirname(path)
from common import *
+from TestTools.Test_init import TestTool
def get_config(entries):
@@ -24,37 +25,14 @@ def get_config(entries):
return config
-def get_posix_object(logger=None, setup=None, config=None):
- if config is None:
- config = lxml.etree.Element("Configuration")
- if not logger:
- def print_msg(msg):
- print(msg)
- logger = Mock()
- logger.error = Mock(side_effect=print_msg)
- logger.warning = Mock(side_effect=print_msg)
- logger.info = Mock(side_effect=print_msg)
- logger.debug = Mock(side_effect=print_msg)
- if not setup:
- setup = MagicMock()
- if 'command_timeout' not in setup:
- setup['command_timeout'] = None
- return Bcfg2.Client.Tools.POSIX.POSIX(logger, setup, config)
-
-
-class TestPOSIX(Bcfg2TestCase):
- def setUp(self):
- self.posix = get_posix_object()
-
- def tearDown(self):
- # just to guarantee that we start fresh each time
- self.posix = None
+class TestPOSIX(TestTool):
+ test_obj = POSIX
def test__init(self):
entries = [lxml.etree.Element("Path", name="test", type="file")]
- posix = get_posix_object(config=get_config(entries))
+ posix = self.get_obj(config=get_config(entries))
self.assertIsInstance(posix, Bcfg2.Client.Tools.Tool)
- self.assertIsInstance(posix, Bcfg2.Client.Tools.POSIX.POSIX)
+ self.assertIsInstance(posix, POSIX)
self.assertIn('Path', posix.__req__)
self.assertGreater(len(posix.__req__['Path']), 0)
self.assertGreater(len(posix.__handles__), 0)
@@ -62,98 +40,102 @@ class TestPOSIX(Bcfg2TestCase):
@patch("Bcfg2.Client.Tools.Tool.canVerify")
def test_canVerify(self, mock_canVerify):
+ posix = self.get_obj()
entry = lxml.etree.Element("Path", name="test", type="file")
# first, test superclass canVerify failure
mock_canVerify.return_value = False
- self.assertFalse(self.posix.canVerify(entry))
- mock_canVerify.assert_called_with(self.posix, entry)
+ self.assertFalse(posix.canVerify(entry))
+ mock_canVerify.assert_called_with(posix, entry)
# next, test fully_specified failure
- self.posix.logger.error.reset_mock()
+ posix.logger.error.reset_mock()
mock_canVerify.reset_mock()
mock_canVerify.return_value = True
mock_fully_spec = Mock()
mock_fully_spec.return_value = False
- self.posix._handlers[entry.get("type")].fully_specified = \
+ posix._handlers[entry.get("type")].fully_specified = \
mock_fully_spec
- self.assertFalse(self.posix.canVerify(entry))
- mock_canVerify.assert_called_with(self.posix, entry)
+ self.assertFalse(posix.canVerify(entry))
+ mock_canVerify.assert_called_with(posix, entry)
mock_fully_spec.assert_called_with(entry)
- self.assertTrue(self.posix.logger.error.called)
+ self.assertTrue(posix.logger.error.called)
# finally, test success
- self.posix.logger.error.reset_mock()
+ posix.logger.error.reset_mock()
mock_canVerify.reset_mock()
mock_fully_spec.reset_mock()
mock_fully_spec.return_value = True
- self.assertTrue(self.posix.canVerify(entry))
- mock_canVerify.assert_called_with(self.posix, entry)
+ self.assertTrue(posix.canVerify(entry))
+ mock_canVerify.assert_called_with(posix, entry)
mock_fully_spec.assert_called_with(entry)
- self.assertFalse(self.posix.logger.error.called)
+ self.assertFalse(posix.logger.error.called)
@patch("Bcfg2.Client.Tools.Tool.canInstall")
def test_canInstall(self, mock_canInstall):
+ posix = self.get_obj()
entry = lxml.etree.Element("Path", name="test", type="file")
# first, test superclass canInstall failure
mock_canInstall.return_value = False
- self.assertFalse(self.posix.canInstall(entry))
- mock_canInstall.assert_called_with(self.posix, entry)
+ self.assertFalse(posix.canInstall(entry))
+ mock_canInstall.assert_called_with(posix, entry)
# next, test fully_specified failure
- self.posix.logger.error.reset_mock()
+ posix.logger.error.reset_mock()
mock_canInstall.reset_mock()
mock_canInstall.return_value = True
mock_fully_spec = Mock()
mock_fully_spec.return_value = False
- self.posix._handlers[entry.get("type")].fully_specified = \
+ posix._handlers[entry.get("type")].fully_specified = \
mock_fully_spec
- self.assertFalse(self.posix.canInstall(entry))
- mock_canInstall.assert_called_with(self.posix, entry)
+ self.assertFalse(posix.canInstall(entry))
+ mock_canInstall.assert_called_with(posix, entry)
mock_fully_spec.assert_called_with(entry)
- self.assertTrue(self.posix.logger.error.called)
+ self.assertTrue(posix.logger.error.called)
# finally, test success
- self.posix.logger.error.reset_mock()
+ posix.logger.error.reset_mock()
mock_canInstall.reset_mock()
mock_fully_spec.reset_mock()
mock_fully_spec.return_value = True
- self.assertTrue(self.posix.canInstall(entry))
- mock_canInstall.assert_called_with(self.posix, entry)
+ self.assertTrue(posix.canInstall(entry))
+ mock_canInstall.assert_called_with(posix, entry)
mock_fully_spec.assert_called_with(entry)
- self.assertFalse(self.posix.logger.error.called)
+ self.assertFalse(posix.logger.error.called)
def test_InstallPath(self):
+ posix = self.get_obj()
entry = lxml.etree.Element("Path", name="test", type="file")
mock_install = Mock()
mock_install.return_value = True
- self.posix._handlers[entry.get("type")].install = mock_install
- self.assertTrue(self.posix.InstallPath(entry))
+ posix._handlers[entry.get("type")].install = mock_install
+ self.assertTrue(posix.InstallPath(entry))
mock_install.assert_called_with(entry)
def test_VerifyPath(self):
+ posix = self.get_obj()
entry = lxml.etree.Element("Path", name="test", type="file")
modlist = []
mock_verify = Mock()
mock_verify.return_value = True
- self.posix._handlers[entry.get("type")].verify = mock_verify
- self.assertTrue(self.posix.VerifyPath(entry, modlist))
+ posix._handlers[entry.get("type")].verify = mock_verify
+ self.assertTrue(posix.VerifyPath(entry, modlist))
mock_verify.assert_called_with(entry, modlist)
mock_verify.reset_mock()
mock_verify.return_value = False
- self.posix.setup.__getitem__.return_value = True
- self.assertFalse(self.posix.VerifyPath(entry, modlist))
+ posix.setup.__getitem__.return_value = True
+ self.assertFalse(posix.VerifyPath(entry, modlist))
self.assertIsNotNone(entry.get('qtext'))
@patch('os.remove')
def test_prune_old_backups(self, mock_remove):
entry = lxml.etree.Element("Path", name="/etc/foo", type="file")
setup = dict(ppath='/', max_copies=5, paranoid=True)
- posix = get_posix_object(setup=setup)
+ posix = self.get_obj(setup=setup)
remove = ["_etc_foo_2012-07-20T04:13:22.364989",
"_etc_foo_2012-07-31T04:13:23.894958",
@@ -202,48 +184,55 @@ class TestPOSIX(Bcfg2TestCase):
@patch("shutil.copy")
@patch("os.path.isdir")
- @patch("Bcfg2.Client.Tools.POSIX.POSIX._prune_old_backups")
- def test_paranoid_backup(self, mock_prune, mock_isdir, mock_copy):
+ def test_paranoid_backup(self, mock_isdir, mock_copy):
entry = lxml.etree.Element("Path", name="/etc/foo", type="file")
setup = dict(ppath='/', max_copies=5, paranoid=False)
- posix = get_posix_object(setup=setup)
+ posix = self.get_obj(setup=setup)
+ posix._prune_old_backups = Mock()
# paranoid false globally
posix._paranoid_backup(entry)
- self.assertFalse(mock_prune.called)
+ self.assertFalse(posix._prune_old_backups.called)
self.assertFalse(mock_copy.called)
# paranoid false on the entry
- mock_prune.reset_mock()
setup['paranoid'] = True
- posix = get_posix_object(setup=setup)
+ posix = self.get_obj(setup=setup)
+ posix._prune_old_backups = Mock()
+
+ def reset():
+ mock_isdir.reset_mock()
+ mock_copy.reset_mock()
+ posix._prune_old_backups.reset_mock()
+
+ reset()
posix._paranoid_backup(entry)
- self.assertFalse(mock_prune.called)
+ self.assertFalse(posix._prune_old_backups.called)
self.assertFalse(mock_copy.called)
# entry does not exist on filesystem
- mock_prune.reset_mock()
+ reset()
entry.set("paranoid", "true")
entry.set("current_exists", "false")
posix._paranoid_backup(entry)
- self.assertFalse(mock_prune.called)
+ self.assertFalse(posix._prune_old_backups.called)
self.assertFalse(mock_copy.called)
# entry is a directory on the filesystem
- mock_prune.reset_mock()
+ reset()
entry.set("current_exists", "true")
mock_isdir.return_value = True
posix._paranoid_backup(entry)
- self.assertFalse(mock_prune.called)
+ self.assertFalse(posix._prune_old_backups.called)
self.assertFalse(mock_copy.called)
mock_isdir.assert_called_with(entry.get("name"))
# test the actual backup now
- mock_prune.reset_mock()
+ reset()
mock_isdir.return_value = False
posix._paranoid_backup(entry)
mock_isdir.assert_called_with(entry.get("name"))
- mock_prune.assert_called_with(entry)
+ posix._prune_old_backups.assert_called_with(entry)
# it's basically impossible to test the shutil.copy() call
# exactly because the destination includes microseconds, so we
# just test it good enough
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Testbase.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Testbase.py
index b3599db83..49e9be2ba 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Testbase.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIX/Testbase.py
@@ -16,7 +16,7 @@ while path != "/":
if os.path.basename(path) == "testsuite":
break
path = os.path.dirname(path)
-from Test__init import get_posix_object
+from TestTools.Test_init import TestTool
from common import *
try:
@@ -32,96 +32,86 @@ except ImportError:
HAS_ACLS = False
-class TestPOSIXTool(Bcfg2TestCase):
+class TestPOSIXTool(TestTool):
test_obj = POSIXTool
- def get_obj(self, posix=None):
- if posix is None:
- posix = get_posix_object()
- return self.test_obj(posix.logger, posix.setup, posix.config)
-
- def setUp(self):
- self.ptool = self.get_obj()
-
- def tearDown(self):
- # just to guarantee that we start fresh each time
- self.ptool = None
-
def test_fully_specified(self):
# fully_specified should do no checking on the abstract
# POSIXTool object
- self.assertTrue(self.ptool.fully_specified(Mock()))
+ ptool = self.get_obj()
+ self.assertTrue(ptool.fully_specified(Mock()))
@patch('os.stat')
@patch('os.walk')
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._verify_metadata" %
- test_obj.__name__)
- def test_verify(self, mock_verify, mock_walk, mock_stat):
+ def test_verify(self, mock_walk, mock_stat):
+ ptool = self.get_obj()
+ ptool._verify_metadata = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file")
mock_stat.return_value = MagicMock()
- mock_verify.return_value = False
- self.assertFalse(self.ptool.verify(entry, []))
- mock_verify.assert_called_with(entry)
+ ptool._verify_metadata.return_value = False
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._verify_metadata.assert_called_with(entry)
- mock_verify.reset_mock()
- mock_verify.return_value = True
- self.assertTrue(self.ptool.verify(entry, []))
- mock_verify.assert_called_with(entry)
+ ptool._verify_metadata.reset_mock()
+ ptool._verify_metadata.return_value = True
+ self.assertTrue(ptool.verify(entry, []))
+ ptool._verify_metadata.assert_called_with(entry)
- mock_verify.reset_mock()
+ ptool._verify_metadata.reset_mock()
entry.set("recursive", "true")
walk_rv = [("/", ["dir1", "dir2"], ["file1", "file2"]),
("/dir1", ["dir3"], []),
("/dir2", [], ["file3", "file4"])]
mock_walk.return_value = walk_rv
- self.assertTrue(self.ptool.verify(entry, []))
+ self.assertTrue(ptool.verify(entry, []))
mock_walk.assert_called_with(entry.get("name"))
all_verifies = [call(entry)]
for root, dirs, files in walk_rv:
all_verifies.extend([call(entry, path=os.path.join(root, p))
for p in dirs + files])
- self.assertItemsEqual(mock_verify.call_args_list, all_verifies)
+ self.assertItemsEqual(ptool._verify_metadata.call_args_list, all_verifies)
@patch('os.walk')
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._set_perms" % test_obj.__name__)
- def test_install(self, mock_set_perms, mock_walk):
+ def test_install(self, mock_walk):
+ ptool = self.get_obj()
+ ptool._set_perms = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file")
- mock_set_perms.return_value = True
- self.assertTrue(self.ptool.install(entry))
- mock_set_perms.assert_called_with(entry)
+ ptool._set_perms.return_value = True
+ self.assertTrue(ptool.install(entry))
+ ptool._set_perms.assert_called_with(entry)
- mock_set_perms.reset_mock()
+ ptool._set_perms.reset_mock()
entry.set("recursive", "true")
walk_rv = [("/", ["dir1", "dir2"], ["file1", "file2"]),
("/dir1", ["dir3"], []),
("/dir2", [], ["file3", "file4"])]
mock_walk.return_value = walk_rv
- mock_set_perms.return_value = True
- self.assertTrue(self.ptool.install(entry))
+ ptool._set_perms.return_value = True
+ self.assertTrue(ptool.install(entry))
mock_walk.assert_called_with(entry.get("name"))
all_set_perms = [call(entry)]
for root, dirs, files in walk_rv:
all_set_perms.extend([call(entry, path=os.path.join(root, p))
for p in dirs + files])
- self.assertItemsEqual(mock_set_perms.call_args_list,
+ self.assertItemsEqual(ptool._set_perms.call_args_list,
all_set_perms)
mock_walk.reset_mock()
- mock_set_perms.reset_mock()
+ ptool._set_perms.reset_mock()
def set_perms_rv(entry, path=None):
if path == '/dir2/file3':
return False
else:
return True
- mock_set_perms.side_effect = set_perms_rv
+ ptool._set_perms.side_effect = set_perms_rv
- self.assertFalse(self.ptool.install(entry))
+ self.assertFalse(ptool.install(entry))
mock_walk.assert_called_with(entry.get("name"))
- self.assertItemsEqual(mock_set_perms.call_args_list, all_set_perms)
+ self.assertItemsEqual(ptool._set_perms.call_args_list, all_set_perms)
@patch('os.rmdir')
@patch('os.unlink')
@@ -130,6 +120,7 @@ class TestPOSIXTool(Bcfg2TestCase):
@patch('os.path.islink')
def test_remove(self, mock_islink, mock_isdir, mock_rmtree, mock_unlink,
mock_rmdir):
+ ptool = self.get_obj()
entry = lxml.etree.Element("Path", name="/etc/foo")
def reset():
@@ -141,7 +132,7 @@ class TestPOSIXTool(Bcfg2TestCase):
mock_islink.return_value = True
mock_isdir.return_value = False
- self.ptool._remove(entry)
+ ptool._remove(entry)
mock_unlink.assert_called_with(entry.get('name'))
self.assertFalse(mock_rmtree.called)
self.assertFalse(mock_rmdir.called)
@@ -149,13 +140,13 @@ class TestPOSIXTool(Bcfg2TestCase):
reset()
mock_islink.return_value = False
mock_isdir.return_value = True
- self.ptool._remove(entry)
+ ptool._remove(entry)
mock_rmtree.assert_called_with(entry.get('name'))
self.assertFalse(mock_unlink.called)
self.assertFalse(mock_rmdir.called)
reset()
- self.ptool._remove(entry, recursive=False)
+ ptool._remove(entry, recursive=False)
mock_rmdir.assert_called_with(entry.get('name'))
self.assertFalse(mock_unlink.called)
self.assertFalse(mock_rmtree.called)
@@ -163,7 +154,7 @@ class TestPOSIXTool(Bcfg2TestCase):
reset()
mock_islink.return_value = False
mock_isdir.return_value = False
- self.ptool._remove(entry, recursive=False)
+ ptool._remove(entry, recursive=False)
mock_unlink.assert_called_with(entry.get('name'))
self.assertFalse(mock_rmtree.called)
self.assertFalse(mock_rmdir.called)
@@ -172,103 +163,101 @@ class TestPOSIXTool(Bcfg2TestCase):
def test_exists(self, mock_lstat):
entry = lxml.etree.Element("Path", name="/etc/foo", type="file")
- self.ptool._remove = Mock()
+ ptool = self.get_obj()
+ ptool._remove = Mock()
def reset():
mock_lstat.reset_mock()
- self.ptool._remove.reset_mock()
+ ptool._remove.reset_mock()
mock_lstat.side_effect = OSError
- self.assertFalse(self.ptool._exists(entry))
+ self.assertFalse(ptool._exists(entry))
mock_lstat.assert_called_with(entry.get('name'))
- self.assertFalse(self.ptool._remove.called)
+ self.assertFalse(ptool._remove.called)
reset()
rv = MagicMock()
mock_lstat.return_value = rv
mock_lstat.side_effect = None
- self.assertEqual(self.ptool._exists(entry), rv)
+ self.assertEqual(ptool._exists(entry), rv)
mock_lstat.assert_called_with(entry.get('name'))
- self.assertFalse(self.ptool._remove.called)
+ self.assertFalse(ptool._remove.called)
reset()
- self.assertEqual(self.ptool._exists(entry, remove=True), None)
+ self.assertEqual(ptool._exists(entry, remove=True), None)
mock_lstat.assert_called_with(entry.get('name'))
- self.ptool._remove.assert_called_with(entry)
+ ptool._remove.assert_called_with(entry)
reset()
- self.ptool._remove.side_effect = OSError
- self.assertEqual(self.ptool._exists(entry, remove=True), rv)
+ ptool._remove.side_effect = OSError
+ self.assertEqual(ptool._exists(entry, remove=True), rv)
mock_lstat.assert_called_with(entry.get('name'))
- self.ptool._remove.assert_called_with(entry)
+ ptool._remove.assert_called_with(entry)
@patch("os.chown")
@patch("os.chmod")
@patch("os.utime")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_entry_uid" %
- test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_entry_gid" %
- test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._set_acls" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._set_secontext" %
- test_obj.__name__)
- def test_set_perms(self, mock_set_secontext, mock_set_acls, mock_norm_gid,
- mock_norm_uid, mock_utime, mock_chmod, mock_chown):
+ def test_set_perms(self, mock_utime, mock_chmod, mock_chown):
+ ptool = self.get_obj()
+ ptool._norm_entry_uid = Mock()
+ ptool._norm_entry_gid = Mock()
+ ptool._set_acls = Mock()
+ ptool._set_secontext = Mock()
def reset():
- mock_set_secontext.reset_mock()
- mock_set_acls.reset_mock()
- mock_norm_gid.reset_mock()
- mock_norm_uid.reset_mock()
+ ptool._set_secontext.reset_mock()
+ ptool._set_acls.reset_mock()
+ ptool._norm_entry_gid.reset_mock()
+ ptool._norm_entry_uid.reset_mock()
mock_chmod.reset_mock()
mock_chown.reset_mock()
mock_utime.reset_mock()
entry = lxml.etree.Element("Path", name="/etc/foo", to="/etc/bar",
type="symlink")
- mock_set_acls.return_value = True
- mock_set_secontext.return_value = True
- self.assertTrue(self.ptool._set_perms(entry))
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.return_value = True
+ ptool._set_secontext.return_value = True
+ self.assertTrue(ptool._set_perms(entry))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
entry = lxml.etree.Element("Path", name="/etc/foo", owner="owner",
group="group", mode="644", type="file")
- mock_norm_uid.return_value = 10
- mock_norm_gid.return_value = 100
+ ptool._norm_entry_uid.return_value = 10
+ ptool._norm_entry_gid.return_value = 100
reset()
- self.assertTrue(self.ptool._set_perms(entry))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ self.assertTrue(ptool._set_perms(entry))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with(entry.get("name"), 10, 100)
mock_chmod.assert_called_with(entry.get("name"),
int(entry.get("mode"), 8))
self.assertFalse(mock_utime.called)
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
reset()
mtime = 1344459042
entry.set("mtime", str(mtime))
- self.assertTrue(self.ptool._set_perms(entry))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ self.assertTrue(ptool._set_perms(entry))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with(entry.get("name"), 10, 100)
mock_chmod.assert_called_with(entry.get("name"),
int(entry.get("mode"), 8))
mock_utime.assert_called_with(entry.get("name"), (mtime, mtime))
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
reset()
- self.assertTrue(self.ptool._set_perms(entry, path='/etc/bar'))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ self.assertTrue(ptool._set_perms(entry, path='/etc/bar'))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with('/etc/bar', 10, 100)
mock_chmod.assert_called_with('/etc/bar', int(entry.get("mode"), 8))
mock_utime.assert_called_with(entry.get("name"), (mtime, mtime))
- mock_set_secontext.assert_called_with(entry, path='/etc/bar')
- mock_set_acls.assert_called_with(entry, path='/etc/bar')
+ ptool._set_secontext.assert_called_with(entry, path='/etc/bar')
+ ptool._set_acls.assert_called_with(entry, path='/etc/bar')
# test dev_type modification of perms, failure of chown
reset()
@@ -280,15 +269,15 @@ class TestPOSIXTool(Bcfg2TestCase):
os.chown.side_effect = chown_rv
entry.set("type", "device")
entry.set("dev_type", list(device_map.keys())[0])
- self.assertFalse(self.ptool._set_perms(entry))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ self.assertFalse(ptool._set_perms(entry))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with(entry.get("name"), 0, 0)
mock_chmod.assert_called_with(entry.get("name"),
int(entry.get("mode"), 8) | list(device_map.values())[0])
mock_utime.assert_called_with(entry.get("name"), (mtime, mtime))
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
# test failure of chmod
reset()
@@ -296,15 +285,15 @@ class TestPOSIXTool(Bcfg2TestCase):
os.chmod.side_effect = OSError
entry.set("type", "file")
del entry.attrib["dev_type"]
- self.assertFalse(self.ptool._set_perms(entry))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ self.assertFalse(ptool._set_perms(entry))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with(entry.get("name"), 10, 100)
mock_chmod.assert_called_with(entry.get("name"),
int(entry.get("mode"), 8))
mock_utime.assert_called_with(entry.get("name"), (mtime, mtime))
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
# test that even when everything fails, we try to do it all.
# e.g., when chmod fails, we still try to apply acls, set
@@ -312,33 +301,32 @@ class TestPOSIXTool(Bcfg2TestCase):
reset()
os.chown.side_effect = OSError
os.utime.side_effect = OSError
- mock_set_acls.return_value = False
- mock_set_secontext.return_value = False
- self.assertFalse(self.ptool._set_perms(entry))
- mock_norm_uid.assert_called_with(entry)
- mock_norm_gid.assert_called_with(entry)
+ ptool._set_acls.return_value = False
+ ptool._set_secontext.return_value = False
+ self.assertFalse(ptool._set_perms(entry))
+ ptool._norm_entry_uid.assert_called_with(entry)
+ ptool._norm_entry_gid.assert_called_with(entry)
mock_chown.assert_called_with(entry.get("name"), 10, 100)
mock_chmod.assert_called_with(entry.get("name"),
int(entry.get("mode"), 8))
mock_utime.assert_called_with(entry.get("name"), (mtime, mtime))
- mock_set_secontext.assert_called_with(entry, path=entry.get("name"))
- mock_set_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_secontext.assert_called_with(entry, path=entry.get("name"))
+ ptool._set_acls.assert_called_with(entry, path=entry.get("name"))
@skipUnless(HAS_ACLS, "ACLS not found, skipping")
@patchIf(HAS_ACLS, "posix1e.ACL")
@patchIf(HAS_ACLS, "posix1e.Entry")
@patch("os.path.isdir")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_uid" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_gid" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._list_entry_acls" %
- test_obj.__name__)
- def test_set_acls(self, mock_list_entry_acls, mock_norm_gid, mock_norm_uid,
- mock_isdir, mock_Entry, mock_ACL):
+ def test_set_acls(self, mock_isdir, mock_Entry, mock_ACL):
+ ptool = self.get_obj()
+ ptool._list_entry_acls = Mock()
+ ptool._norm_uid = Mock()
+ ptool._norm_gid = Mock()
entry = lxml.etree.Element("Path", name="/etc/foo", type="file")
# disable acls for the initial test
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = False
- self.assertTrue(self.ptool._set_acls(entry))
+ self.assertTrue(ptool._set_acls(entry))
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = True
# build a set of file ACLs to return from posix1e.ACL(file=...)
@@ -361,9 +349,9 @@ class TestPOSIXTool(Bcfg2TestCase):
# _list_entry_acls()
entry_acls = {("default", posix1e.ACL_USER, "user"): 7,
("access", posix1e.ACL_GROUP, "group"): 5}
- mock_list_entry_acls.return_value = entry_acls
- mock_norm_uid.return_value = 10
- mock_norm_gid.return_value = 100
+ ptool._list_entry_acls.return_value = entry_acls
+ ptool._norm_uid.return_value = 10
+ ptool._norm_gid.return_value = 100
# set up the unreasonably complex return value for
# posix1e.ACL(), which has three separate uses
@@ -403,17 +391,17 @@ class TestPOSIXTool(Bcfg2TestCase):
# test fs mounted noacl
mock_ACL.side_effect = IOError(95, "Operation not permitted")
- self.assertFalse(self.ptool._set_acls(entry))
+ self.assertFalse(ptool._set_acls(entry))
# test other error
reset()
mock_ACL.side_effect = IOError
- self.assertFalse(self.ptool._set_acls(entry))
+ self.assertFalse(ptool._set_acls(entry))
reset()
mock_ACL.side_effect = mock_acl_rv
mock_isdir.return_value = True
- self.assertTrue(self.ptool._set_acls(entry))
+ self.assertTrue(ptool._set_acls(entry))
self.assertItemsEqual(mock_ACL.call_args_list,
[call(file=entry.get("name")),
call(filedef=entry.get("name"))])
@@ -421,9 +409,9 @@ class TestPOSIXTool(Bcfg2TestCase):
[call(a) for a in remove_acls])
self.assertItemsEqual(filedef_rv.delete_entry.call_args_list,
[call(a) for a in remove_acls])
- mock_list_entry_acls.assert_called_with(entry)
- mock_norm_uid.assert_called_with("user")
- mock_norm_gid.assert_called_with("group")
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._norm_uid.assert_called_with("user")
+ ptool._norm_gid.assert_called_with("group")
fileacl_rv.calc_mask.assert_any_call()
fileacl_rv.applyto.assert_called_with(entry.get("name"),
posix1e.ACL_TYPE_ACCESS)
@@ -432,7 +420,7 @@ class TestPOSIXTool(Bcfg2TestCase):
posix1e.ACL_TYPE_DEFAULT)
# build tuples of the Entry objects that were added to acl
- # and defaacl so they're easier to compare for equality
+ # and defacl so they're easier to compare for equality
added_acls = []
for acl in acl_entries:
added_acls.append((acl.acl, acl.tag_type, acl.qualifier,
@@ -446,17 +434,17 @@ class TestPOSIXTool(Bcfg2TestCase):
# they've already been iterated over once
fileacl_rv.__iter__.return_value = iter(file_acls)
filedef_rv.__iter__.return_value = iter(file_acls)
- mock_list_entry_acls.reset_mock()
- mock_norm_uid.reset_mock()
- mock_norm_gid.reset_mock()
+ ptool._list_entry_acls.reset_mock()
+ ptool._norm_uid.reset_mock()
+ ptool._norm_gid.reset_mock()
mock_isdir.return_value = False
acl_entries = []
- self.assertTrue(self.ptool._set_acls(entry, path="/bin/bar"))
+ self.assertTrue(ptool._set_acls(entry, path="/bin/bar"))
mock_ACL.assert_called_with(file="/bin/bar")
self.assertItemsEqual(fileacl_rv.delete_entry.call_args_list,
[call(a) for a in remove_acls])
- mock_list_entry_acls.assert_called_with(entry)
- mock_norm_gid.assert_called_with("group")
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._norm_gid.assert_called_with("group")
fileacl_rv.calc_mask.assert_any_call()
fileacl_rv.applyto.assert_called_with("/bin/bar",
posix1e.ACL_TYPE_ACCESS)
@@ -472,22 +460,23 @@ class TestPOSIXTool(Bcfg2TestCase):
@patchIf(HAS_SELINUX, "selinux.restorecon")
@patchIf(HAS_SELINUX, "selinux.lsetfilecon")
def test_set_secontext(self, mock_lsetfilecon, mock_restorecon):
+ ptool = self.get_obj()
entry = lxml.etree.Element("Path", name="/etc/foo", type="file")
# disable selinux for the initial test
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = False
- self.assertTrue(self.ptool._set_secontext(entry))
+ self.assertTrue(ptool._set_secontext(entry))
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = True
# no context given
- self.assertTrue(self.ptool._set_secontext(entry))
+ self.assertTrue(ptool._set_secontext(entry))
self.assertFalse(mock_restorecon.called)
self.assertFalse(mock_lsetfilecon.called)
mock_restorecon.reset_mock()
mock_lsetfilecon.reset_mock()
entry.set("secontext", "__default__")
- self.assertTrue(self.ptool._set_secontext(entry))
+ self.assertTrue(ptool._set_secontext(entry))
mock_restorecon.assert_called_with(entry.get("name"))
self.assertFalse(mock_lsetfilecon.called)
@@ -495,85 +484,91 @@ class TestPOSIXTool(Bcfg2TestCase):
mock_lsetfilecon.reset_mock()
mock_lsetfilecon.return_value = 0
entry.set("secontext", "foo_t")
- self.assertTrue(self.ptool._set_secontext(entry))
+ self.assertTrue(ptool._set_secontext(entry))
self.assertFalse(mock_restorecon.called)
mock_lsetfilecon.assert_called_with(entry.get("name"), "foo_t")
mock_restorecon.reset_mock()
mock_lsetfilecon.reset_mock()
mock_lsetfilecon.return_value = 1
- self.assertFalse(self.ptool._set_secontext(entry))
+ self.assertFalse(ptool._set_secontext(entry))
self.assertFalse(mock_restorecon.called)
mock_lsetfilecon.assert_called_with(entry.get("name"), "foo_t")
@patch("grp.getgrnam")
def test_norm_gid(self, mock_getgrnam):
- self.assertEqual(5, self.ptool._norm_gid("5"))
+ ptool = self.get_obj()
+ self.assertEqual(5, ptool._norm_gid("5"))
self.assertFalse(mock_getgrnam.called)
mock_getgrnam.reset_mock()
mock_getgrnam.return_value = ("group", "x", 5, [])
- self.assertEqual(5, self.ptool._norm_gid("group"))
+ self.assertEqual(5, ptool._norm_gid("group"))
mock_getgrnam.assert_called_with("group")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_gid" % test_obj.__name__)
- def test_norm_entry_gid(self, mock_norm_gid):
+ def test_norm_entry_gid(self):
+ ptool = self.get_obj()
+ ptool._norm_gid = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file",
group="group", owner="user")
- mock_norm_gid.return_value = 10
- self.assertEqual(10, self.ptool._norm_entry_gid(entry))
- mock_norm_gid.assert_called_with(entry.get("group"))
+ self.assertEqual(ptool._norm_entry_gid(entry),
+ ptool._norm_gid.return_value)
+ ptool._norm_gid.assert_called_with(entry.get("group"))
- mock_norm_gid.reset_mock()
- mock_norm_gid.side_effect = KeyError
- self.assertEqual(0, self.ptool._norm_entry_gid(entry))
- mock_norm_gid.assert_called_with(entry.get("group"))
+ ptool._norm_gid.reset_mock()
+ ptool._norm_gid.side_effect = KeyError
+ self.assertEqual(ptool._norm_entry_gid(entry), 0)
+ ptool._norm_gid.assert_called_with(entry.get("group"))
@patch("pwd.getpwnam")
def test_norm_uid(self, mock_getpwnam):
- self.assertEqual(5, self.ptool._norm_uid("5"))
+ ptool = self.get_obj()
+ self.assertEqual(5, ptool._norm_uid("5"))
self.assertFalse(mock_getpwnam.called)
mock_getpwnam.reset_mock()
mock_getpwnam.return_value = ("user", "x", 5, 5, "User", "/home/user",
"/bin/zsh")
- self.assertEqual(5, self.ptool._norm_uid("user"))
+ self.assertEqual(5, ptool._norm_uid("user"))
mock_getpwnam.assert_called_with("user")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_uid" % test_obj.__name__)
- def test_norm_entry_uid(self, mock_norm_uid):
+ def test_norm_entry_uid(self):
+ ptool = self.get_obj()
+ ptool._norm_uid = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file",
group="group", owner="user")
- mock_norm_uid.return_value = 10
- self.assertEqual(10, self.ptool._norm_entry_uid(entry))
- mock_norm_uid.assert_called_with(entry.get("owner"))
+ self.assertEqual(ptool._norm_entry_uid(entry),
+ ptool._norm_uid.return_value)
+ ptool._norm_uid.assert_called_with(entry.get("owner"))
- mock_norm_uid.reset_mock()
- mock_norm_uid.side_effect = KeyError
- self.assertEqual(0, self.ptool._norm_entry_uid(entry))
- mock_norm_uid.assert_called_with(entry.get("owner"))
+ ptool._norm_uid.reset_mock()
+ ptool._norm_uid.side_effect = KeyError
+ self.assertEqual(ptool._norm_entry_uid(entry), 0)
+ ptool._norm_uid.assert_called_with(entry.get("owner"))
def test_norm_acl_perms(self):
- # there's basically no reasonably way to test the Permset
+ # there's basically no reasonable way to test the Permset
# object parsing feature without writing our own Mock object
# that re-implements Permset.test(). silly pylibacl won't let
# us create standalone Entry or Permset objects.
- self.assertEqual(5, self.ptool._norm_acl_perms("5"))
- self.assertEqual(0, self.ptool._norm_acl_perms("55"))
- self.assertEqual(5, self.ptool._norm_acl_perms("rx"))
- self.assertEqual(5, self.ptool._norm_acl_perms("r-x"))
- self.assertEqual(6, self.ptool._norm_acl_perms("wr-"))
- self.assertEqual(0, self.ptool._norm_acl_perms("rwrw"))
- self.assertEqual(0, self.ptool._norm_acl_perms("-"))
- self.assertEqual(0, self.ptool._norm_acl_perms("a"))
- self.assertEqual(6, self.ptool._norm_acl_perms("rwa"))
- self.assertEqual(4, self.ptool._norm_acl_perms("rr"))
+ ptool = self.get_obj()
+ self.assertEqual(5, ptool._norm_acl_perms("5"))
+ self.assertEqual(0, ptool._norm_acl_perms("55"))
+ self.assertEqual(5, ptool._norm_acl_perms("rx"))
+ self.assertEqual(5, ptool._norm_acl_perms("r-x"))
+ self.assertEqual(6, ptool._norm_acl_perms("wr-"))
+ self.assertEqual(0, ptool._norm_acl_perms("rwrw"))
+ self.assertEqual(0, ptool._norm_acl_perms("-"))
+ self.assertEqual(0, ptool._norm_acl_perms("a"))
+ self.assertEqual(6, ptool._norm_acl_perms("rwa"))
+ self.assertEqual(4, ptool._norm_acl_perms("rr"))
@patch('os.stat')
def test__gather_data(self, mock_stat):
+ ptool = self.get_obj()
path = '/test'
mock_stat.side_effect = OSError
- self.assertFalse(self.ptool._gather_data(path)[0])
+ self.assertFalse(ptool._gather_data(path)[0])
mock_stat.assert_called_with(path)
mock_stat.reset_mock()
@@ -598,7 +593,7 @@ class TestPOSIXTool(Bcfg2TestCase):
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS)
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = False
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = False
- self.assertEqual(self.ptool._gather_data(path),
+ self.assertEqual(ptool._gather_data(path),
(stat_rv, '0', '10', '0660', None, None))
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX, \
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = states
@@ -606,6 +601,7 @@ class TestPOSIXTool(Bcfg2TestCase):
@skipUnless(HAS_SELINUX, "SELinux not found, skipping")
def test__gather_data_selinux(self):
+ ptool = self.get_obj()
context = 'system_u:object_r:root_t:s0'
path = '/test'
@@ -620,7 +616,7 @@ class TestPOSIXTool(Bcfg2TestCase):
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX)
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = False
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = True
- self.assertEqual(self.ptool._gather_data(path)[4], 'root_t')
+ self.assertEqual(ptool._gather_data(path)[4], 'root_t')
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS, \
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = state
mock_getfilecon.assert_called_with(path)
@@ -629,12 +625,12 @@ class TestPOSIXTool(Bcfg2TestCase):
@skipUnless(HAS_ACLS, "ACLS not found, skipping")
@patch('os.stat')
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._list_file_acls" %
- test_obj.__name__)
- def test__gather_data_acls(self, mock_list_file_acls, mock_stat):
+ def test__gather_data_acls(self, mock_stat):
+ ptool = self.get_obj()
+ ptool._list_file_acls = Mock()
acls = {("default", posix1e.ACL_USER, "testuser"): "rwx",
("access", posix1e.ACL_GROUP, "testgroup"): "rx"}
- mock_list_file_acls.return_value = acls
+ ptool._list_file_acls.return_value = acls
path = '/test'
mock_stat.return_value = MagicMock()
mock_stat.return_value.__getitem__.return_value = MagicMock()
@@ -643,21 +639,18 @@ class TestPOSIXTool(Bcfg2TestCase):
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX)
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS = True
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = False
- self.assertItemsEqual(self.ptool._gather_data(path)[5], acls)
+ self.assertItemsEqual(ptool._gather_data(path)[5], acls)
Bcfg2.Client.Tools.POSIX.base.HAS_ACLS, \
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = state
- mock_list_file_acls.assert_called_with(path)
+ ptool._list_file_acls.assert_called_with(path)
@patchIf(HAS_SELINUX, "selinux.matchpathcon")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._verify_acls" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._gather_data" % test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_entry_uid" %
- test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._norm_entry_gid" %
- test_obj.__name__)
- def test_verify_metadata(self, mock_norm_gid, mock_norm_uid,
- mock_gather_data, mock_verify_acls,
- mock_matchpathcon):
+ def test_verify_metadata(self, mock_matchpathcon):
+ ptool = self.get_obj()
+ ptool._norm_entry_uid = Mock()
+ ptool._norm_entry_gid = Mock()
+ ptool._verify_acls = Mock()
+ ptool._gather_data = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file",
group="group", owner="user", mode="664",
secontext='etc_t')
@@ -666,34 +659,34 @@ class TestPOSIXTool(Bcfg2TestCase):
orig_entry = copy.deepcopy(entry)
def reset():
- mock_gather_data.reset_mock()
- mock_verify_acls.reset_mock()
- mock_norm_uid.reset_mock()
- mock_norm_gid.reset_mock()
+ ptool._gather_data.reset_mock()
+ ptool._verify_acls.reset_mock()
+ ptool._norm_entry_uid.reset_mock()
+ ptool._norm_entry_gid.reset_mock()
return copy.deepcopy(orig_entry)
# test nonexistent file
- mock_gather_data.return_value = (False, None, None, None, None, None)
- self.assertFalse(self.ptool._verify_metadata(entry))
+ ptool._gather_data.return_value = (False, None, None, None, None, None)
+ self.assertFalse(ptool._verify_metadata(entry))
self.assertEqual(entry.get("current_exists", "").lower(), "false")
- mock_gather_data.assert_called_with(entry.get("name"))
+ ptool._gather_data.assert_called_with(entry.get("name"))
# expected data. tuple of attr, return value index, value
expected = [('current_owner', 1, '0'),
('current_group', 2, '10'),
('current_mode', 3, '0664'),
('current_secontext', 4, 'etc_t')]
- mock_norm_uid.return_value = 0
- mock_norm_gid.return_value = 10
+ ptool._norm_entry_uid.return_value = 0
+ ptool._norm_entry_gid.return_value = 10
gather_data_rv = [MagicMock(), None, None, None, None, []]
for attr, idx, val in expected:
gather_data_rv[idx] = val
entry = reset()
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertTrue(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertTrue(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry, path=entry.get("name"))
self.assertEqual(entry.get("current_exists", 'true'), 'true')
for attr, idx, val in expected:
self.assertEqual(entry.get(attr), val)
@@ -703,10 +696,10 @@ class TestPOSIXTool(Bcfg2TestCase):
gather_data_rv[4] = None
sestate = Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX
Bcfg2.Client.Tools.POSIX.base.HAS_SELINUX = False
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertTrue(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertTrue(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry, path=entry.get("name"))
self.assertEqual(entry.get("current_exists", 'true'), 'true')
for attr, idx, val in expected:
if attr != 'current_secontext':
@@ -716,7 +709,7 @@ class TestPOSIXTool(Bcfg2TestCase):
gather_data_rv = [MagicMock(), None, None, None, None, []]
for attr, idx, val in expected:
gather_data_rv[idx] = val
- mock_gather_data.return_value = tuple(gather_data_rv)
+ ptool._gather_data.return_value = tuple(gather_data_rv)
mtime = 1344430414
entry = reset()
@@ -724,10 +717,10 @@ class TestPOSIXTool(Bcfg2TestCase):
stat_rv = MagicMock()
stat_rv.__getitem__.return_value = mtime
gather_data_rv[0] = stat_rv
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertTrue(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertTrue(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry, path=entry.get("name"))
self.assertEqual(entry.get("current_exists", 'true'), 'true')
for attr, idx, val in expected:
self.assertEqual(entry.get(attr), val)
@@ -748,10 +741,10 @@ class TestPOSIXTool(Bcfg2TestCase):
for attr, idx, val in expected:
gather_data_rv[idx] = val
gather_data_rv[fail_idx] = fail_val
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertFalse(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertFalse(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry, path=entry.get("name"))
self.assertEqual(entry.get("current_exists", 'true'), 'true')
self.assertEqual(entry.get(fail_attr), fail_val)
for attr, idx, val in expected:
@@ -768,10 +761,10 @@ class TestPOSIXTool(Bcfg2TestCase):
gather_data_rv = [fail_stat_rv, None, None, None, None, []]
for attr, idx, val in expected:
gather_data_rv[idx] = val
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertFalse(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry, path=entry.get("name"))
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertFalse(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry, path=entry.get("name"))
self.assertEqual(entry.get("current_exists", 'true'), 'true')
for attr, idx, val in expected:
self.assertEqual(entry.get(attr), val)
@@ -790,10 +783,10 @@ class TestPOSIXTool(Bcfg2TestCase):
gather_data_rv = [stat_rv, None, None, None, None, []]
for attr, idx, val in expected:
gather_data_rv[idx] = val
- mock_gather_data.return_value = tuple(gather_data_rv)
- self.assertTrue(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry,
+ ptool._gather_data.return_value = tuple(gather_data_rv)
+ self.assertTrue(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry,
path=entry.get("name"))
mock_matchpathcon.assert_called_with(entry.get("name"), 0)
self.assertEqual(entry.get("current_exists", 'true'), 'true')
@@ -806,9 +799,9 @@ class TestPOSIXTool(Bcfg2TestCase):
entry.set("secontext", "__default__")
mock_matchpathcon.return_value = [1 + len(context2),
context2]
- self.assertFalse(self.ptool._verify_metadata(entry))
- mock_gather_data.assert_called_with(entry.get("name"))
- mock_verify_acls.assert_called_with(entry,
+ self.assertFalse(ptool._verify_metadata(entry))
+ ptool._gather_data.assert_called_with(entry.get("name"))
+ ptool._verify_acls.assert_called_with(entry,
path=entry.get("name"))
mock_matchpathcon.assert_called_with(entry.get("name"), 0)
self.assertEqual(entry.get("current_exists", 'true'), 'true')
@@ -818,21 +811,24 @@ class TestPOSIXTool(Bcfg2TestCase):
@skipUnless(HAS_ACLS, "ACLS not found, skipping")
def test_list_entry_acls(self):
+ ptool = self.get_obj()
entry = lxml.etree.Element("Path", name="/test", type="file")
lxml.etree.SubElement(entry, "ACL", scope="user", type="default",
user="user", perms="rwx")
lxml.etree.SubElement(entry, "ACL", scope="group", type="access",
group="group", perms="5")
- self.assertItemsEqual(self.ptool._list_entry_acls(entry),
+ self.assertItemsEqual(ptool._list_entry_acls(entry),
{("default", posix1e.ACL_USER, "user"): 7,
("access", posix1e.ACL_GROUP, "group"): 5})
@skipUnless(HAS_ACLS, "ACLS not found, skipping")
+ @patchIf(HAS_ACLS, "posix1e.ACL")
@patch("pwd.getpwuid")
@patch("grp.getgrgid")
@patch("os.path.isdir")
def test_list_file_acls(self, mock_isdir, mock_getgrgid, mock_getpwuid,
mock_ACL):
+ ptool = self.get_obj()
path = '/test'
# build a set of file ACLs to return from posix1e.ACL(file=...)
@@ -881,15 +877,15 @@ class TestPOSIXTool(Bcfg2TestCase):
mock_ACL.reset_mock()
mock_ACL.side_effect = IOError(95, "Operation not supported")
- self.assertItemsEqual(self.ptool._list_file_acls(path), dict())
+ self.assertItemsEqual(ptool._list_file_acls(path), dict())
reset()
mock_ACL.side_effect = IOError
- self.assertItemsEqual(self.ptool._list_file_acls(path), dict())
+ self.assertItemsEqual(ptool._list_file_acls(path), dict())
reset()
mock_ACL.side_effect = mock_acl_rv
- self.assertItemsEqual(self.ptool._list_file_acls(path), acls)
+ self.assertItemsEqual(ptool._list_file_acls(path), acls)
mock_isdir.assert_called_with(path)
mock_getgrgid.assert_called_with(100)
mock_getpwuid.assert_called_with(10)
@@ -903,7 +899,7 @@ class TestPOSIXTool(Bcfg2TestCase):
defacls = acls
for akey, perms in acls.items():
defacls[('default', akey[1], akey[2])] = perms
- self.assertItemsEqual(self.ptool._list_file_acls(path), defacls)
+ self.assertItemsEqual(ptool._list_file_acls(path), defacls)
mock_isdir.assert_called_with(path)
self.assertItemsEqual(mock_getgrgid.call_args_list,
[call(100), call(100)])
@@ -912,20 +908,11 @@ class TestPOSIXTool(Bcfg2TestCase):
self.assertItemsEqual(mock_ACL.call_args_list,
[call(file=path), call(filedef=path)])
- if HAS_ACLS:
- # python 2.6 applies decorators at compile-time, not at
- # run-time, so we can't do these as decorators because
- # pylibacl might not be installed. (If it's not, this test
- # will be skipped, so as long as this is done at run-time
- # we're safe.)
- test_list_file_acls = patch("posix1e.ACL")(test_list_file_acls)
-
@skipUnless(HAS_ACLS, "ACLS not found, skipping")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._list_file_acls" %
- test_obj.__name__)
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._list_entry_acls" %
- test_obj.__name__)
- def test_verify_acls(self, mock_list_entry_acls, mock_list_file_acls):
+ def test_verify_acls(self):
+ ptool = self.get_obj()
+ ptool._list_file_acls = Mock()
+ ptool._list_entry_acls = Mock()
entry = lxml.etree.Element("Path", name="/test", type="file")
# we can't test to make sure that errors get properly sorted
# into (missing, extra, wrong) without refactoring the
@@ -938,44 +925,45 @@ class TestPOSIXTool(Bcfg2TestCase):
extra_acls = copy.deepcopy(acls)
extra_acls[("access", posix1e.ACL_USER, "user2")] = 4
- mock_list_entry_acls.return_value = acls
- mock_list_file_acls.return_value = acls
- self.assertTrue(self.ptool._verify_acls(entry))
- mock_list_entry_acls.assert_called_with(entry)
- mock_list_file_acls.assert_called_with(entry.get("name"))
+ ptool._list_entry_acls.return_value = acls
+ ptool._list_file_acls.return_value = acls
+ self.assertTrue(ptool._verify_acls(entry))
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._list_file_acls.assert_called_with(entry.get("name"))
# test missing
- mock_list_entry_acls.reset_mock()
- mock_list_file_acls.reset_mock()
- mock_list_file_acls.return_value = extra_acls
- self.assertFalse(self.ptool._verify_acls(entry))
- mock_list_entry_acls.assert_called_with(entry)
- mock_list_file_acls.assert_called_with(entry.get("name"))
+ ptool._list_entry_acls.reset_mock()
+ ptool._list_file_acls.reset_mock()
+ ptool._list_file_acls.return_value = extra_acls
+ self.assertFalse(ptool._verify_acls(entry))
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._list_file_acls.assert_called_with(entry.get("name"))
# test extra
- mock_list_entry_acls.reset_mock()
- mock_list_file_acls.reset_mock()
- mock_list_entry_acls.return_value = extra_acls
- mock_list_file_acls.return_value = acls
- self.assertFalse(self.ptool._verify_acls(entry))
- mock_list_entry_acls.assert_called_with(entry)
- mock_list_file_acls.assert_called_with(entry.get("name"))
+ ptool._list_entry_acls.reset_mock()
+ ptool._list_file_acls.reset_mock()
+ ptool._list_entry_acls.return_value = extra_acls
+ ptool._list_file_acls.return_value = acls
+ self.assertFalse(ptool._verify_acls(entry))
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._list_file_acls.assert_called_with(entry.get("name"))
# test wrong
wrong_acls = copy.deepcopy(extra_acls)
wrong_acls[("access", posix1e.ACL_USER, "user2")] = 5
- mock_list_entry_acls.reset_mock()
- mock_list_file_acls.reset_mock()
- mock_list_entry_acls.return_value = extra_acls
- mock_list_file_acls.return_value = wrong_acls
- self.assertFalse(self.ptool._verify_acls(entry))
- mock_list_entry_acls.assert_called_with(entry)
- mock_list_file_acls.assert_called_with(entry.get("name"))
+ ptool._list_entry_acls.reset_mock()
+ ptool._list_file_acls.reset_mock()
+ ptool._list_entry_acls.return_value = extra_acls
+ ptool._list_file_acls.return_value = wrong_acls
+ self.assertFalse(ptool._verify_acls(entry))
+ ptool._list_entry_acls.assert_called_with(entry)
+ ptool._list_file_acls.assert_called_with(entry.get("name"))
@patch("os.makedirs")
@patch("os.path.exists")
- @patch("Bcfg2.Client.Tools.POSIX.base.%s._set_perms" % test_obj.__name__)
- def test_makedirs(self, mock_set_perms, mock_exists, mock_makedirs):
+ def test_makedirs(self, mock_exists, mock_makedirs):
+ ptool = self.get_obj()
+ ptool._set_perms = Mock()
entry = lxml.etree.Element("Path", name="/test/foo/bar",
type="directory", mode="0644")
parent_entry = lxml.etree.Element("Path", name="/test/foo/bar",
@@ -983,33 +971,33 @@ class TestPOSIXTool(Bcfg2TestCase):
def reset():
mock_exists.reset_mock()
- mock_set_perms.reset_mock()
+ ptool._set_perms.reset_mock()
mock_makedirs.reset_mock()
- mock_set_perms.return_value = True
+ ptool._set_perms.return_value = True
def path_exists_rv(path):
if path == "/test":
return True
else:
return False
mock_exists.side_effect = path_exists_rv
- self.assertTrue(self.ptool._makedirs(entry))
+ self.assertTrue(ptool._makedirs(entry))
self.assertItemsEqual(mock_exists.call_args_list,
[call("/test"), call("/test/foo"),
call("/test/foo/bar")])
- for args in mock_set_perms.call_args_list:
+ for args in ptool._set_perms.call_args_list:
self.assertXMLEqual(args[0][0], parent_entry)
- self.assertItemsEqual([a[1] for a in mock_set_perms.call_args_list],
+ self.assertItemsEqual([a[1] for a in ptool._set_perms.call_args_list],
[dict(path="/test/foo"),
dict(path="/test/foo/bar")])
mock_makedirs.assert_called_with(entry.get("name"))
reset()
mock_makedirs.side_effect = OSError
- self.assertFalse(self.ptool._makedirs(entry))
- for args in mock_set_perms.call_args_list:
+ self.assertFalse(ptool._makedirs(entry))
+ for args in ptool._set_perms.call_args_list:
self.assertXMLEqual(args[0][0], parent_entry)
- self.assertItemsEqual([a[1] for a in mock_set_perms.call_args_list],
+ self.assertItemsEqual([a[1] for a in ptool._set_perms.call_args_list],
[dict(path="/test/foo"),
dict(path="/test/foo/bar")])
@@ -1020,14 +1008,89 @@ class TestPOSIXTool(Bcfg2TestCase):
return False
else:
return True
- mock_set_perms.side_effect = set_perms_rv
- self.assertFalse(self.ptool._makedirs(entry))
+ ptool._set_perms.side_effect = set_perms_rv
+ self.assertFalse(ptool._makedirs(entry))
self.assertItemsEqual(mock_exists.call_args_list,
[call("/test"), call("/test/foo"),
call("/test/foo/bar")])
- for args in mock_set_perms.call_args_list:
+ for args in ptool._set_perms.call_args_list:
self.assertXMLEqual(args[0][0], parent_entry)
- self.assertItemsEqual([a[1] for a in mock_set_perms.call_args_list],
+ self.assertItemsEqual([a[1] for a in ptool._set_perms.call_args_list],
[dict(path="/test/foo"),
dict(path="/test/foo/bar")])
mock_makedirs.assert_called_with(entry.get("name"))
+
+
+class TestPOSIXLinkTool(TestPOSIXTool):
+ test_obj = POSIXLinkTool
+
+ @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.verify")
+ def test_verify(self, mock_verify):
+ entry = lxml.etree.Element("Path", name="/test", type="testlink",
+ to="/dest")
+ ptool = self.get_obj()
+ linktype = ptool.__linktype__
+ ptool.__linktype__ = "test"
+ ptool._verify = Mock()
+
+ ptool._verify.return_value = True
+ mock_verify.return_value = False
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._verify.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, [])
+
+ ptool._verify.reset_mock()
+ mock_verify.reset_mock()
+ mock_verify.return_value = True
+ self.assertTrue(ptool.verify(entry, []))
+ ptool._verify.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, [])
+
+ ptool._verify.reset_mock()
+ mock_verify.reset_mock()
+ ptool._verify.return_value = False
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._verify.assert_called_with(entry)
+ mock_verify.assert_called_with(ptool, entry, [])
+
+ ptool._verify.reset_mock()
+ mock_verify.reset_mock()
+ ptool._verify.side_effect = OSError
+ self.assertFalse(ptool.verify(entry, []))
+ ptool._verify.assert_called_with(entry)
+ ptool.__linktype__ = linktype
+
+ def test__verify(self):
+ ptool = self.get_obj()
+ self.assertRaises(NotImplementedError, ptool._verify, Mock())
+
+ @patch("Bcfg2.Client.Tools.POSIX.base.POSIXTool.install")
+ def test_install(self, mock_install):
+ entry = lxml.etree.Element("Path", name="/test", type="symlink",
+ to="/dest")
+ ptool = self.get_obj()
+ linktype = ptool.__linktype__
+ ptool.__linktype__ = "test"
+ ptool._exists = Mock()
+ ptool._link = Mock()
+
+ ptool._exists.return_value = False
+ mock_install.return_value = True
+ self.assertTrue(ptool.install(entry))
+ ptool._exists.assert_called_with(entry, remove=True)
+ ptool._link.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
+
+ ptool._link.reset_mock()
+ ptool._exists.reset_mock()
+ mock_install.reset_mock()
+ ptool._link.side_effect = OSError
+ self.assertFalse(ptool.install(entry))
+ ptool._exists.assert_called_with(entry, remove=True)
+ ptool._link.assert_called_with(entry)
+ mock_install.assert_called_with(ptool, entry)
+ ptool.__linktype__ = linktype
+
+ def test__link(self):
+ ptool = self.get_obj()
+ self.assertRaises(NotImplementedError, ptool._link, Mock())
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py
index 582f0ac99..4fcd63a60 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py
@@ -18,28 +18,17 @@ while path != "/":
break
path = os.path.dirname(path)
from common import *
+from TestTools.Test_init import TestTool
-class TestPOSIXUsers(Bcfg2TestCase):
+class TestPOSIXUsers(TestTool):
test_obj = POSIXUsers
def get_obj(self, logger=None, setup=None, config=None):
- if config is None:
- config = lxml.etree.Element("Configuration")
-
- if logger is None:
- def print_msg(msg):
- print(msg)
- logger = Mock()
- logger.error = Mock(side_effect=print_msg)
- logger.warning = Mock(side_effect=print_msg)
- logger.info = Mock(side_effect=print_msg)
- logger.debug = Mock(side_effect=print_msg)
-
if setup is None:
setup = MagicMock()
setup.__getitem__.return_value = []
- return self.test_obj(logger, setup, config)
+ return TestTool.get_obj(self, logger, setup, config)
@patch("pwd.getpwall")
@patch("grp.getgrall")
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py
new file mode 100644
index 000000000..19f76f2f1
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py
@@ -0,0 +1,649 @@
+import os
+import sys
+import lxml.etree
+from mock import Mock, MagicMock, patch
+from Bcfg2.Compat import long
+from Bcfg2.Client.Tools import Tool, SvcTool, PkgTool, \
+ ToolInstantiationError
+
+# add all parent testsuite directories to sys.path to allow (most)
+# relative imports in python 2.4
+path = os.path.dirname(__file__)
+while path != "/":
+ if os.path.basename(path).lower().startswith("test"):
+ sys.path.append(path)
+ if os.path.basename(path) == "testsuite":
+ break
+ path = os.path.dirname(path)
+from common import *
+
+
+class TestTool(Bcfg2TestCase):
+ test_obj = Tool
+
+ def get_obj(self, logger=None, setup=None, config=None):
+ if config is None:
+ config = lxml.etree.Element("Configuration")
+ if not logger:
+ def print_msg(msg):
+ print(msg)
+ logger = Mock()
+ logger.error = Mock(side_effect=print_msg)
+ logger.warning = Mock(side_effect=print_msg)
+ logger.info = Mock(side_effect=print_msg)
+ logger.debug = Mock(side_effect=print_msg)
+ if not setup:
+ setup = MagicMock()
+ if 'command_timeout' not in setup:
+ setup['command_timeout'] = None
+ execs = self.test_obj.__execs__
+ self.test_obj.__execs__ = []
+ rv = self.test_obj(logger, setup, config)
+ self.test_obj.__execs__ = execs
+ return rv
+
+ def test__init(self):
+ @patch("%s.%s._check_execs" % (self.test_obj.__module__,
+ self.test_obj.__name__))
+ @patch("%s.%s._analyze_config" % (self.test_obj.__module__,
+ self.test_obj.__name__))
+ def inner(mock_analyze_config, mock_check_execs):
+ t = self.get_obj()
+ mock_analyze_config.assert_called_with()
+ mock_check_execs.assert_called_with()
+
+ def test__analyze_config(self):
+ t = self.get_obj()
+ t.getSupportedEntries = Mock()
+
+ t.__important__ = ["/test"]
+ important = []
+ t.config = lxml.etree.Element("Config")
+ bundle1 = lxml.etree.SubElement(t.config, "Bundle")
+ important.append(lxml.etree.SubElement(bundle1, "Path",
+ name="/foo", important="true"))
+ lxml.etree.SubElement(bundle1, "Package", name="bar", important="true")
+ lxml.etree.SubElement(bundle1, "Path", name="/bar")
+ bundle2 = lxml.etree.SubElement(t.config, "Bundle")
+ important.append(lxml.etree.SubElement(bundle2, "Path", name="/quux",
+ important="true"))
+ lxml.etree.SubElement(bundle2, "Path", name="/baz", important="false")
+
+ t._analyze_config()
+ self.assertItemsEqual(t.__important__,
+ ["/test"] + [e.get("name") for e in important])
+ t.getSupportedEntries.assert_called_with()
+
+ def test__check_execs(self):
+ t = self.get_obj()
+ if t.__execs__ == []:
+ t.__execs__.append("/bin/true")
+
+ @patch("os.stat")
+ def inner(mock_stat):
+ mock_stat.return_value = (33261, 2245040, long(64770), 1, 0, 0,
+ 25552, 1360831382, 1352194410,
+ 1354626626)
+ t._check_execs()
+ self.assertItemsEqual(mock_stat.call_args_list,
+ [call(e) for e in t.__execs__])
+
+ # not executable
+ mock_stat.reset_mock()
+ mock_stat.return_value = (33188, 2245040, long(64770), 1, 0, 0,
+ 25552, 1360831382, 1352194410,
+ 1354626626)
+ self.assertRaises(ToolInstantiationError, t._check_execs)
+
+ # non-existant
+ mock_stat.reset_mock()
+ mock_stat.side_effect = OSError
+ self.assertRaises(ToolInstantiationError, t._check_execs)
+
+ inner()
+
+ def test_BundleUpdated(self):
+ pass
+
+ def test_BundleNotUpdated(self):
+ pass
+
+ def test_Inventory(self):
+ t = self.get_obj()
+ t.canVerify = Mock()
+ t.canVerify.side_effect = lambda e: e.get("verify") != "false"
+ t.buildModlist = Mock()
+ t.FindExtra = Mock()
+ t.VerifyPath = Mock()
+ t.VerifyPackage = Mock()
+ t.VerifyService = Mock()
+
+ def reset():
+ t.canVerify.reset_mock()
+ t.buildModlist.reset_mock()
+ t.FindExtra.reset_mock()
+ t.VerifyPath.reset_mock()
+ t.VerifyPackage.reset_mock()
+ t.VerifyService.reset_mock()
+
+ paths = []
+ packages = []
+ services = []
+ config = lxml.etree.Element("Configuration")
+ bundle1 = lxml.etree.SubElement(config, "Bundle")
+ paths.append(lxml.etree.SubElement(bundle1, "Path", name="/foo"))
+ lxml.etree.SubElement(bundle1, "Package", name="foo", verify="false")
+ packages.append(lxml.etree.SubElement(bundle1, "Package", name="bar"))
+ lxml.etree.SubElement(bundle1, "Bogus")
+
+ bundle2 = lxml.etree.SubElement(config, "Bundle")
+ paths.append(lxml.etree.SubElement(bundle2, "Path", name="/bar"))
+ services.append(lxml.etree.SubElement(bundle2, "Service", name="bar"))
+ lxml.etree.SubElement(bundle2, "Path", name="/baz", verify="false")
+
+ expected_states = dict([(e, t.VerifyPath.return_value)
+ for e in paths])
+ expected_states.update(dict([(e, t.VerifyPackage.return_value)
+ for e in packages]))
+ expected_states.update(dict([(e, t.VerifyService.return_value)
+ for e in services]))
+
+ def perform_assertions(states):
+ t.buildModlist.assert_called_with()
+ t.FindExtra.assert_called_with()
+ self.assertItemsEqual(t.canVerify.call_args_list,
+ [call(e) for e in bundle1.getchildren()] + \
+ [call(e) for e in bundle2.getchildren()])
+ self.assertItemsEqual(t.VerifyPath.call_args_list,
+ [call(e, t.buildModlist.return_value)
+ for e in paths])
+ self.assertItemsEqual(t.VerifyPackage.call_args_list,
+ [call(e, t.buildModlist.return_value)
+ for e in packages])
+ self.assertItemsEqual(t.VerifyService.call_args_list,
+ [call(e, t.buildModlist.return_value)
+ for e in services])
+ self.assertItemsEqual(states, expected_states)
+ self.assertEqual(t.extra, t.FindExtra.return_value)
+
+ actual_states = dict()
+ t.Inventory(actual_states, structures=[bundle1, bundle2])
+ perform_assertions(actual_states)
+
+ reset()
+ actual_states = dict()
+ t.config = config
+ t.Inventory(actual_states)
+ perform_assertions(actual_states)
+
+ def test_Install(self):
+ t = self.get_obj()
+ t.InstallPath = Mock()
+ t.InstallPackage = Mock()
+ t.InstallService = Mock()
+
+ t.InstallPath.side_effect = lambda e: e.get("modified") == "true"
+ t.InstallPackage.side_effect = lambda e: e.get("modified") == "true"
+ t.InstallService.side_effect = lambda e: e.get("modified") == "true"
+
+ entries = [lxml.etree.Element("Path", name="/foo", modified="true"),
+ lxml.etree.Element("Package", name="bar", modified="true"),
+ lxml.etree.Element("Bogus"),
+ lxml.etree.Element("Path", name="/bar", modified="true"),
+ lxml.etree.Element("Service", name="bar")]
+
+ expected_states = dict([(e, t.InstallPath.return_value)
+ for e in entries if e.tag == "Path"])
+ expected_states.update(dict([(e, t.InstallPackage.return_value)
+ for e in entries if e.tag == "Package"]))
+ expected_states.update(dict([(e, t.InstallService.return_value)
+ for e in entries if e.tag == "Service"]))
+
+ actual_states = dict()
+ t.modified = []
+ t.Install(entries, actual_states)
+ self.assertItemsEqual(t.InstallPath.call_args_list,
+ [call(e) for e in entries if e.tag == "Path"])
+ self.assertItemsEqual(t.InstallPackage.call_args_list,
+ [call(e) for e in entries if e.tag == "Package"])
+ self.assertItemsEqual(t.InstallService.call_args_list,
+ [call(e) for e in entries if e.tag == "Service"])
+ self.assertItemsEqual(actual_states, expected_states)
+ self.assertItemsEqual(t.modified,
+ [e for e in entries
+ if e.get("modified") == "true"])
+
+ def rest_Remove(self):
+ pass
+
+ def test_getSupportedEntries(self):
+ t = self.get_obj()
+
+ def handlesEntry(entry):
+ return entry.get("handled") == "true"
+ t.handlesEntry = Mock()
+ t.handlesEntry.side_effect = handlesEntry
+
+ handled = []
+ t.config = lxml.etree.Element("Config")
+ bundle1 = lxml.etree.SubElement(t.config, "Bundle")
+ lxml.etree.SubElement(bundle1, "Path", name="/foo")
+ handled.append(lxml.etree.SubElement(bundle1, "Path", name="/bar",
+ handled="true"))
+ bundle2 = lxml.etree.SubElement(t.config, "Bundle")
+ handled.append(lxml.etree.SubElement(bundle2, "Path", name="/quux",
+ handled="true"))
+ lxml.etree.SubElement(bundle2, "Path", name="/baz")
+
+ self.assertItemsEqual(handled,
+ t.getSupportedEntries())
+
+ def test_handlesEntry(self):
+ t = self.get_obj()
+ handles = t.__handles__
+ t.__handles__ = [("Path", "file"),
+ ("Package", "yum")]
+ self.assertTrue(t.handlesEntry(lxml.etree.Element("Path", type="file",
+ name="/foo")))
+ self.assertFalse(t.handlesEntry(lxml.etree.Element("Path",
+ type="permissions",
+ name="/bar")))
+ self.assertFalse(t.handlesEntry(lxml.etree.Element("Bogus",
+ type="file",
+ name="/baz")))
+ self.assertTrue(t.handlesEntry(lxml.etree.Element("Package",
+ type="yum",
+ name="quux")))
+ t.__handles__ = handles
+
+ def test_buildModlist(self):
+ t = self.get_obj()
+ paths = []
+
+ t.config = lxml.etree.Element("Config")
+ bundle1 = lxml.etree.SubElement(t.config, "Bundle")
+ paths.append(lxml.etree.SubElement(bundle1, "Path", name="/foo"))
+ lxml.etree.SubElement(bundle1, "Package", name="bar")
+ paths.append(lxml.etree.SubElement(bundle1, "Path", name="/bar"))
+ bundle2 = lxml.etree.SubElement(t.config, "Bundle")
+ paths.append(lxml.etree.SubElement(bundle2, "Path", name="/quux"))
+ lxml.etree.SubElement(bundle2, "Service", name="baz")
+
+ self.assertItemsEqual([p.get("name") for p in paths],
+ t.buildModlist())
+
+ def test_missing_attrs(self):
+ t = self.get_obj()
+ req = t.__req__
+ t.__req__ = dict(Path=dict(file=["name"],
+ permissions=["name", "owner", "group"]),
+ Package=["name"])
+ # tuples of <entry>, <return value>
+ cases = [
+ (lxml.etree.Element("Path", name="/foo"), ["type"]),
+ (lxml.etree.Element("Path", type="file"), ["name"]),
+ (lxml.etree.Element("Path", type="file", name="/foo"), []),
+ (lxml.etree.Element("Path", type="permissions", name="/foo"),
+ ["owner", "group"]),
+ (lxml.etree.Element("Path", type="permissions", name="/foo",
+ owner="root", group="root", mode="0644"), []),
+ (lxml.etree.Element("Package", type="yum"), ["name"]),
+ (lxml.etree.Element("Package", type="yum", name="/bar"), []),
+ (lxml.etree.Element("Package", type="apt", name="/bar"), [])]
+ for entry, expected in cases:
+ self.assertItemsEqual(t.missing_attrs(entry), expected)
+
+ t.__req__ = req
+
+ def test_canVerify(self):
+ t = self.get_obj()
+ entry = Mock()
+ t._entry_is_complete = Mock()
+ self.assertEqual(t.canVerify(entry),
+ t._entry_is_complete.return_value)
+ t._entry_is_complete.assert_called_with(entry, action="verify")
+
+ def test_FindExtra(self):
+ t = self.get_obj()
+ self.assertItemsEqual(t.FindExtra(), [])
+
+ def test_canInstall(self):
+ t = self.get_obj()
+ entry = Mock()
+ t._entry_is_complete = Mock()
+ self.assertEqual(t.canInstall(entry),
+ t._entry_is_complete.return_value)
+ t._entry_is_complete.assert_called_with(entry, action="install")
+
+ def test__entry_is_complete(self):
+ t = self.get_obj()
+ t.handlesEntry = Mock()
+ t.missing_attrs = Mock()
+
+ def reset():
+ t.handlesEntry.reset_mock()
+ t.missing_attrs.reset_mock()
+
+ entry = lxml.etree.Element("Path", name="/test")
+
+ t.handlesEntry.return_value = False
+ t.missing_attrs.return_value = []
+ self.assertFalse(t._entry_is_complete(entry))
+
+ reset()
+ t.handlesEntry.return_value = True
+ t.missing_attrs.return_value = ["type"]
+ self.assertFalse(t._entry_is_complete(entry))
+
+ reset()
+ t.missing_attrs.return_value = []
+ self.assertTrue(t._entry_is_complete(entry))
+
+ reset()
+ entry.set("failure", "failure")
+ self.assertFalse(t._entry_is_complete(entry))
+
+
+class TestPkgTool(TestTool):
+ test_obj = PkgTool
+
+ def get_obj(self, **kwargs):
+ @patch("%s.%s.RefreshPackages" % (self.test_obj.__module__,
+ self.test_obj.__name__), Mock())
+ def inner():
+ return TestTool.get_obj(self, **kwargs)
+
+ return inner()
+
+ def test_VerifyPackage(self):
+ pt = self.get_obj()
+ self.assertRaises(NotImplementedError,
+ pt.VerifyPackage, Mock(), Mock())
+
+ def test_Install(self):
+ pt = self.get_obj()
+ pt.cmd = Mock()
+ pt.RefreshPackages = Mock()
+ pt.VerifyPackage = Mock()
+ pt._get_package_command = Mock()
+ pt._get_package_command.side_effect = lambda pkgs: \
+ [p.get("name") for p in pkgs]
+ packages = [lxml.etree.Element("Package", type="echo", name="foo",
+ version="1.2.3"),
+ lxml.etree.Element("Package", type="echo", name="bar",
+ version="any"),
+ lxml.etree.Element("Package", type="echo", name="baz",
+ version="2.3.4")]
+
+ def reset():
+ pt.cmd.reset_mock()
+ pt.RefreshPackages.reset_mock()
+ pt.VerifyPackage.reset_mock()
+ pt._get_package_command.reset_mock()
+ pt.modified = []
+
+ # test single-pass install success
+ reset()
+ pt.cmd.run.return_value = (0, '')
+ states = dict([(p, False) for p in packages])
+ pt.Install(packages, states)
+ pt._get_package_command.assert_called_with(packages)
+ pt.cmd.run.assert_called_with([p.get("name") for p in packages])
+ self.assertItemsEqual(states,
+ dict([(p, True) for p in packages]))
+ self.assertItemsEqual(pt.modified, packages)
+
+ # test failed single-pass install
+ reset()
+
+ def run(cmd):
+ if "foo" in cmd:
+ # fail when installing all packages, and when installing foo
+ return (1, '')
+ # succeed otherwise
+ return (0, '')
+
+ pt.VerifyPackage.side_effect = lambda p, m: p.get("name") == "bar"
+
+ pt.cmd.run.side_effect = run
+ states = dict([(p, False) for p in packages])
+ pt.Install(packages, states)
+ pt._get_package_command.assert_any_call(packages)
+ for pkg in packages:
+ pt.VerifyPackage.assert_any_call(pkg, [])
+ if pkg.get("name") != "bar":
+ pt._get_package_command.assert_any_call([pkg])
+ # pt.cmd.run is called once for all packages, and then once
+ # for each package that does not verify. "bar" verifies, so
+ # it's run for foo and baz
+ self.assertItemsEqual(pt.cmd.run.call_args_list,
+ [call([p.get("name") for p in packages]),
+ call(["foo"]),
+ call(["baz"])])
+ pt.RefreshPackages.assert_called_with()
+ self.assertItemsEqual(states,
+ dict([(p, p.get("name") != "bar")
+ for p in packages]))
+ # bar is modified, because it verifies successfully; baz is
+ # modified, because it is installed successfully. foo is not
+ # installed successfully, so is not modified.
+ self.assertItemsEqual(pt.modified,
+ [p for p in packages if p.get("name") != "foo"])
+
+ def test__get_package_command(self):
+ packages = [lxml.etree.Element("Package", type="test", name="foo",
+ version="1.2.3"),
+ lxml.etree.Element("Package", type="test", name="bar",
+ version="any"),
+ lxml.etree.Element("Package", type="test", name="baz",
+ version="2.3.4")]
+ pt = self.get_obj()
+ pkgtool = pt.pkgtool
+ pt.pkgtool = ("install %s", ("%s-%s", ["name", "version"]))
+ self.assertEqual(pt._get_package_command([
+ lxml.etree.Element("Package", type="test", name="foo",
+ version="1.2.3")]),
+ "install foo-1.2.3")
+ self.assertItemsEqual(pt._get_package_command(packages).split(),
+ ["install", "foo-1.2.3", "bar-any", "baz-2.3.4"])
+
+ def test_RefreshPackages(self):
+ pt = self.get_obj()
+ self.assertRaises(NotImplementedError, pt.RefreshPackages)
+
+ def test_FindExtra(self):
+ pt = self.get_obj()
+ pt.getSupportedEntries = Mock()
+ pt.getSupportedEntries.return_value = [
+ lxml.etree.Element("Package", name="foo"),
+ lxml.etree.Element("Package", name="bar"),
+ lxml.etree.Element("Package", name="baz")]
+ pt.installed = dict(foo="1.2.3",
+ bar="2.3.4",
+ quux="3.4.5",
+ xyzzy="4.5.6")
+ extra = pt.FindExtra()
+ self.assertEqual(len(extra), 2)
+ self.assertItemsEqual([e.get("name") for e in extra],
+ ["quux", "xyzzy"])
+ for el in extra:
+ self.assertEqual(el.tag, "Package")
+ self.assertEqual(el.get("type"), pt.pkgtype)
+
+
+class TestSvcTool(TestTool):
+ test_obj = SvcTool
+
+ def test_start_service(self):
+ st = self.get_obj()
+ st.get_svc_command = Mock()
+ st.cmd = MagicMock()
+ service = lxml.etree.Element("Service", name="foo", type="test")
+ self.assertEqual(st.start_service(service),
+ st.cmd.run.return_value[0])
+ st.get_svc_command.assert_called_with(service, "start")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ def test_stop_service(self):
+ st = self.get_obj()
+ st.get_svc_command = Mock()
+ st.cmd = MagicMock()
+ service = lxml.etree.Element("Service", name="foo", type="test")
+ self.assertEqual(st.stop_service(service),
+ st.cmd.run.return_value[0])
+ st.get_svc_command.assert_called_with(service, "stop")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ def test_restart_service(self):
+ st = self.get_obj()
+ st.get_svc_command = Mock()
+ st.cmd = MagicMock()
+
+ def reset():
+ st.get_svc_command.reset_mock()
+ st.cmd.reset_mock()
+
+ service = lxml.etree.Element("Service", name="foo", type="test")
+ self.assertEqual(st.restart_service(service),
+ st.cmd.run.return_value[0])
+ st.get_svc_command.assert_called_with(service, "restart")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ reset()
+ service.set('target', 'reload')
+ self.assertEqual(st.restart_service(service),
+ st.cmd.run.return_value[0])
+ st.get_svc_command.assert_called_with(service, "reload")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ def test_check_service(self):
+ st = self.get_obj()
+ st.get_svc_command = Mock()
+ st.cmd = MagicMock()
+ service = lxml.etree.Element("Service", name="foo", type="test")
+
+ def reset():
+ st.get_svc_command.reset_mock()
+ st.cmd.reset_mock()
+
+ st.cmd.run.return_value = (0, '')
+ self.assertEqual(st.check_service(service), True)
+ st.get_svc_command.assert_called_with(service, "status")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ reset()
+ st.cmd.run.return_value = (11, '')
+ self.assertEqual(st.check_service(service), False)
+ st.get_svc_command.assert_called_with(service, "status")
+ st.cmd.run.assert_called_with(st.get_svc_command.return_value)
+
+ def test_Remove(self):
+ st = self.get_obj()
+ st.InstallService = Mock()
+ services = [lxml.etree.Element("Service", type="test", name="foo"),
+ lxml.etree.Element("Service", type="test", name="bar",
+ status="on")]
+ st.Remove(services)
+ self.assertItemsEqual(st.InstallService.call_args_list,
+ [call(e) for e in services])
+ for entry in services:
+ self.assertEqual(entry.get("status"), "off")
+
+ @patch("Bcfg2.Client.prompt")
+ def test_BundleUpdated(self, mock_prompt):
+ st = self.get_obj(setup=dict(interactive=False,
+ servicemode='default'))
+ st.handlesEntry = Mock()
+ st.handlesEntry.side_effect = lambda e: e.tag == "Service"
+ st.stop_service = Mock()
+ st.stop_service.return_value = 0
+ st.restart_service = Mock()
+ st.restart_service.side_effect = lambda e: \
+ int(e.get("name") == "failed")
+
+ def reset():
+ st.handlesEntry.reset_mock()
+ st.stop_service.reset_mock()
+ st.restart_service.reset_mock()
+ mock_prompt.reset_mock()
+ st.restarted = []
+
+ norestart = lxml.etree.Element("Service", type="test",
+ name="norestart", restart="false")
+ interactive = lxml.etree.Element("Service", type="test",
+ name="interactive", status="on",
+ restart="interactive")
+ interactive2 = lxml.etree.Element("Service", type="test",
+ name="interactive2", status="on",
+ restart="interactive")
+ stop = lxml.etree.Element("Service", type="test", name="stop",
+ status="off")
+ restart = lxml.etree.Element("Service", type="test", name="restart",
+ status="on")
+ duplicate = lxml.etree.Element("Service", type="test", name="restart",
+ status="on")
+ failed = lxml.etree.Element("Service", type="test", name="failed",
+ status="on")
+ unhandled = lxml.etree.Element("Path", type="file", name="/unhandled")
+ services = [norestart, interactive, interactive2, stop, restart,
+ duplicate, failed]
+ entries = services + [unhandled]
+ bundle = lxml.etree.Element("Bundle")
+ bundle.extend(entries)
+
+ # test in non-interactive mode
+ reset()
+ states = dict()
+ st.BundleUpdated(bundle, states)
+ self.assertItemsEqual(st.handlesEntry.call_args_list,
+ [call(e) for e in entries])
+ st.stop_service.assert_called_with(stop)
+ self.assertItemsEqual(st.restart_service.call_args_list,
+ [call(restart), call(failed)])
+ self.assertItemsEqual(st.restarted, [restart.get("name")])
+ self.assertFalse(mock_prompt.called)
+
+ # test in interactive mode
+ reset()
+ mock_prompt.side_effect = lambda p: "interactive2" not in p
+ st.setup['interactive'] = True
+ states = dict()
+ st.BundleUpdated(bundle, states)
+ self.assertItemsEqual(st.handlesEntry.call_args_list,
+ [call(e) for e in entries])
+ st.stop_service.assert_called_with(stop)
+ self.assertItemsEqual(st.restart_service.call_args_list,
+ [call(restart), call(failed), call(interactive)])
+ self.assertItemsEqual(st.restarted, [restart.get("name"),
+ interactive.get("name")])
+ self.assertEqual(len(mock_prompt.call_args_list), 4)
+
+ # test in build mode
+ reset()
+ st.setup['interactive'] = False
+ st.setup['servicemode'] = 'build'
+ states = dict()
+ st.BundleUpdated(bundle, states)
+ self.assertItemsEqual(st.handlesEntry.call_args_list,
+ [call(e) for e in entries])
+ self.assertItemsEqual(st.stop_service.call_args_list,
+ [call(restart), call(duplicate), call(failed),
+ call(stop)])
+ self.assertFalse(mock_prompt.called)
+ self.assertFalse(st.restart_service.called)
+ self.assertItemsEqual(st.restarted, [])
+
+ @patch("Bcfg2.Client.Tools.Tool.Install")
+ def test_Install(self, mock_Install):
+ install = [lxml.etree.Element("Service", type="test", name="foo")]
+ services = install + [lxml.etree.Element("Service", type="test",
+ name="bar", install="false")]
+ st = self.get_obj()
+ states = Mock()
+ self.assertEqual(st.Install(services, states),
+ mock_Install.return_value)
+ mock_Install.assert_called_with(st, install, states)
+
+ def test_InstallService(self):
+ st = self.get_obj()
+ self.assertRaises(NotImplementedError, st.InstallService, Mock())
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgExternalCommandVerifier.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgExternalCommandVerifier.py
index 11dbdd391..0f369113b 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgExternalCommandVerifier.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgExternalCommandVerifier.py
@@ -26,7 +26,7 @@ class TestCfgExternalCommandVerifier(TestCfgVerifier):
proc = Mock()
mock_Popen.return_value = proc
proc.wait.return_value = 0
- proc.communicate.return_value = MagicMock()
+ proc.communicate.return_value = ("stdout", "stderr")
entry = lxml.etree.Element("Path", name="/test.txt")
metadata = Mock()
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
index 9ed0c3803..7be3d8e84 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
@@ -15,10 +15,10 @@ while path != "/":
path = os.path.dirname(path)
from common import *
from TestRules import TestRules
-from Testinterfaces import TestStructureValidator
+from Testinterfaces import TestGoalValidator
-class TestDefaults(TestRules, TestStructureValidator):
+class TestDefaults(TestRules, TestGoalValidator):
test_obj = Defaults
def get_obj(self, *args, **kwargs):
@@ -35,25 +35,22 @@ class TestDefaults(TestRules, TestStructureValidator):
d.HandleEvent(evt)
mock_HandleEvent.assert_called_with(d, evt)
- def test_validate_structures(self):
+ def test_validate_goals(self):
d = self.get_obj()
d.BindEntry = Mock()
metadata = Mock()
entries = []
- b1 = lxml.etree.Element("Bundle")
+ config = lxml.etree.Element("Configuration")
+ b1 = lxml.etree.SubElement(config, "Bundle")
entries.append(lxml.etree.SubElement(b1, "Path", name="/foo"))
entries.append(lxml.etree.SubElement(b1, "Path", name="/bar"))
- b2 = lxml.etree.Element("Bundle")
- bound = lxml.etree.SubElement(b2, "BoundPath", name="/baz")
- entries.append(bound)
+ b2 = lxml.etree.SubElement(config, "Bundle")
entries.append(lxml.etree.SubElement(b2, "Package", name="quux"))
- d.validate_structures(metadata, [b1, b2])
+ d.validate_goals(metadata, config)
self.assertItemsEqual(d.BindEntry.call_args_list,
[call(e, metadata) for e in entries])
- # ensure that BoundEntries stay bound
- self.assertTrue(bound.tag == "BoundPath")
def test__matches_regex_disabled(self):
""" cannot disable regex in Defaults plugin """
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestTemplateHelper.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestTemplateHelper.py
index 4db92b7c4..128d6cae5 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestTemplateHelper.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestTemplateHelper.py
@@ -39,7 +39,8 @@ class TestHelperModule(Bcfg2TestCase):
mock_load_source.side_effect = ImportError
attrs = dir(hm)
hm.HandleEvent()
- mock_load_source.assert_called_with(hm._module_name, hm.name)
+ mock_load_source.assert_called_with(safe_module_name(hm._module_name),
+ hm.name)
self.assertEqual(attrs, dir(hm))
self.assertEqual(hm._attrs, [])
@@ -50,7 +51,8 @@ class TestHelperModule(Bcfg2TestCase):
mock_load_source.return_value = Mock()
attrs = dir(hm)
hm.HandleEvent()
- mock_load_source.assert_called_with(hm._module_name, hm.name)
+ mock_load_source.assert_called_with(safe_module_name(hm._module_name),
+ hm.name)
self.assertEqual(attrs, dir(hm))
self.assertEqual(hm._attrs, [])
@@ -61,7 +63,8 @@ class TestHelperModule(Bcfg2TestCase):
mock_load_source.return_value = module
attrs = dir(hm)
hm.HandleEvent()
- mock_load_source.assert_called_with(hm._module_name, hm.name)
+ mock_load_source.assert_called_with(safe_module_name(hm._module_name),
+ hm.name)
self.assertEqual(attrs, dir(hm))
self.assertEqual(hm._attrs, [])
@@ -71,7 +74,8 @@ class TestHelperModule(Bcfg2TestCase):
mock_load_source.reset()
mock_load_source.return_value = module
hm.HandleEvent()
- mock_load_source.assert_called_with(hm._module_name, hm.name)
+ mock_load_source.assert_called_with(safe_module_name(hm._module_name),
+ hm.name)
self.assertTrue(hasattr(hm, "foo"))
self.assertTrue(hasattr(hm, "bar"))
self.assertTrue(hasattr(hm, "baz"))
@@ -83,7 +87,8 @@ class TestHelperModule(Bcfg2TestCase):
mock_load_source.reset()
mock_load_source.return_value = module
hm.HandleEvent()
- mock_load_source.assert_called_with(hm._module_name, hm.name)
+ mock_load_source.assert_called_with(safe_module_name(hm._module_name),
+ hm.name)
self.assertTrue(hasattr(hm, "foo"))
self.assertTrue(hasattr(hm, "bar"))
self.assertTrue(hasattr(hm, "quux"))
diff --git a/tools/README b/tools/README
index dc21426b8..ad6d5eac1 100644
--- a/tools/README
+++ b/tools/README
@@ -1,9 +1,5 @@
This directory contains repository maintenance tools.
-accounts2xml.py
- - Generate an XML description of accounts on a machine from
- /etc/passwd
-
basebuilder.py <image directory>
- builds v2 base.xml from bcfg1 repo
diff --git a/tools/accounts2xml.py b/tools/accounts2xml.py
deleted file mode 100755
index 749f3b68c..000000000
--- a/tools/accounts2xml.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/usr/bin/env python
-#===============================================================================
-#
-# FILE: accounts2xml.py
-#
-# USAGE: ./accounts2xml.py filename node_name
-#
-# DESCRIPTION: A python script to generate accounts.xml containing only the login
-# users from the given /etc/passwd file
-#
-# OPTIONS: ---
-# REQUIREMENTS: ---
-# BUGS: ---
-# NOTES: ---
-# AUTHOR: DongInn Kim (), dikim@cs.indiana.edu
-# ORGANIZATION: Center for Research in Extreme Scale Technologies
-# VERSION: 1.0
-# CREATED: 05/13/2012 01:44:43 PM
-# REVISION: ---
-#===============================================================================
-
-# encoding: utf-8
-
-"""
-accounts2xml.py
-
-This script coverts a csv file to an XML.
-The script takes 1 paramenters
-* filename
-
-e.g., ./accounts2xml.py /etc/passwd
-
-Created by Giovanni Collazo on 2011-02-19.
-Copyright (c) 2011 24veces.com. All rights reserved.
-
-Modified by DongInn Kim on 2012-05-13
-Copyright (c) 2012 Indiana University. All rights reserved.
-
-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.
-
-"""
-
-import sys
-import csv
-import os
-import re
-import grp
-from xml.dom.minidom import Document
-
-def main(args):
-
- try:
- filename = "./copied_passwd"
- with file(args[1], 'r') as original: data = original.read()
- with file(filename, 'w') as modified: modified.write("name:pass:uid:gid:gecos:home:shell\n" + data); modified.close()
- safe_filename = "Properties"
- except IndexError:
- print("ERROR: Please provide a filename.csv as the first argument")
- sys.exit()
-
- node_user = "UnixUser"
- node_group = "UnixGroup"
-
- f = csv.reader(open(filename, 'rb'), delimiter=':')
-
- doc = Document()
- root_element = doc.createElement(safe_filename)
- doc.appendChild(root_element)
-
- columns = f.next()
-
- groups = dict()
- for row in f:
- match = re.search(r'/bin/\w*sh', row[6]) # adjust this line to match the right shell path
- if match:
- item = doc.createElement(node_user)
- root_element.appendChild(item)
- extra_groups = os.popen("groups %s" % row[0]).readline()[:-1]
- p_group = os.popen("id -gn %s" % row[0]).readline()[:-1]
- extra_groups_str = extra_groups.split(' : ')[1]
- populate_groups(groups, extra_groups_str)
- item.setAttribute('extra_groups', get_extra_group_str(extra_groups_str, p_group))
- create_col_nodes(columns, item, doc, row)
-
- for gkey, gval in groups.items():
- item = doc.createElement(node_group)
- root_element.appendChild(item)
- item.setAttribute('name', gkey)
- (gid,extra) = gval.split(':')
- item.setAttribute('gid', gid)
-
- output_file = "accounts.xml"
- doc.writexml(open(output_file, 'w'), addindent=' ', newl='\n') # Write file
-
- print("Done: Created %s" % output_file)
- os.remove(filename)
-
-def get_extra_group_str(group_str, p_group):
- groups = group_str.split(' ')
- groups = [x for x in groups if p_group != x]
- return ' '.join(groups)
-
-
-def create_col_nodes(cols, item, doc, row):
- for col in cols:
- if col == "gid":
- att = doc.createAttribute("group")
- att.nodeValue = grp.getgrgid(int(row.pop(0)))[0]
- else:
- att = doc.createAttribute(str.replace(col, " ", "_").lower())
- att.nodeValue = row.pop(0)
-
- if col != "pass":
- item.setAttributeNode(att)
-
-def populate_groups(group_dic, group_str):
- for g in group_str.split(' '):
- if not group_dic.has_key(g):
- group_ent = os.popen("getent group %s" % g).readline()[:-1].split(':')
- gid = group_ent[2]
- extra = group_ent[3]
- extra_list = list(extra)
- for e in extra_list:
- if e == ',':
- loc = extra_list.index(e)
- extra_list[loc] = ' '
- extra = "".join(extra_list)
- group_dic[g] = gid + ":" + extra
-
-
-if __name__ == "__main__":
- sys.exit(main(sys.argv))
-
-# vim:set sr et ts=4 sw=4 ft=python fenc=utf-8: // See Vim, :help 'modeline'
-# Created: Sun, 13 May 2012 13:44:43 -0400
-
-
diff --git a/tools/bcfg2-profile-templates.py b/tools/bcfg2-profile-templates.py
index 54f8e87f3..cc7a1a3d8 100755
--- a/tools/bcfg2-profile-templates.py
+++ b/tools/bcfg2-profile-templates.py
@@ -1,62 +1,61 @@
#!/usr/bin/python -Ott
""" Benchmark template rendering times """
+import os
import sys
import time
+import signal
import logging
-import logging.handlers
import operator
-import lxml.etree
+import Bcfg2.Logger
import Bcfg2.Server.Core
LOGGER = None
-def get_logger(setup=None):
- """ set up logging according to the verbose level given on the
- command line """
- global LOGGER
- if LOGGER is None:
- if setup is None:
- setup = dict()
- LOGGER = logging.getLogger(sys.argv[0])
- stderr = logging.StreamHandler()
- level = logging.WARNING
- lformat = "%(message)s"
- if setup.get("debug", False):
- stderr.setFormatter(logging.Formatter("%(asctime)s: %(levelname)s: %(message)s"))
- level = logging.DEBUG
- elif setup.get("verbose", False):
- level = logging.INFO
- LOGGER.setLevel(level)
- LOGGER.addHandler(stderr)
- syslog = logging.handlers.SysLogHandler("/dev/log")
- syslog.setFormatter(logging.Formatter("%(name)s: %(message)s"))
- LOGGER.addHandler(syslog)
- return LOGGER
+
+def get_sigint_handler(core):
+ """ Get a function that handles SIGINT/Ctrl-C by shutting down the
+ core and exiting properly."""
+
+ def hdlr(sig, frame): # pylint: disable=W0613
+ """ Handle SIGINT/Ctrl-C by shutting down the core and exiting
+ properly. """
+ core.shutdown()
+ os._exit(1) # pylint: disable=W0212
+
+ return hdlr
+
def main():
optinfo = \
- dict(configfile=Bcfg2.Options.CFILE,
- help=Bcfg2.Options.HELP,
- encoding=Bcfg2.Options.ENCODING,
- repo=Bcfg2.Options.SERVER_REPOSITORY,
- plugins=Bcfg2.Options.SERVER_PLUGINS,
- password=Bcfg2.Options.SERVER_PASSWORD,
- debug=Bcfg2.Options.DEBUG,
- verbose=Bcfg2.Options.VERBOSE,
- client=Bcfg2.Options.Option("Benchmark templates for one client",
+ dict(client=Bcfg2.Options.Option("Benchmark templates for one client",
cmd="--client",
odesc="<client>",
long_arg=True,
default=None),
)
+ optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
+ optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
setup = Bcfg2.Options.OptionParser(optinfo)
setup.parse(sys.argv[1:])
- logger = get_logger(setup)
+
+ if setup['debug']:
+ level = logging.DEBUG
+ elif setup['verbose']:
+ level = logging.INFO
+ else:
+ level = logging.WARNING
+ Bcfg2.Logger.setup_logging("bcfg2-test",
+ to_console=setup['verbose'] or setup['debug'],
+ to_syslog=False,
+ to_file=setup['logging'],
+ level=level)
+ logger = logging.getLogger(sys.argv[0])
core = Bcfg2.Server.Core.BaseCore(setup)
+ signal.signal(signal.SIGINT, get_sigint_handler(core))
logger.info("Bcfg2 server core loaded")
- core.fam.handle_events_in_interval(4)
+ core.fam.handle_events_in_interval(0.1)
logger.debug("Repository events processed")
# how many times to render each template for each client
@@ -73,40 +72,27 @@ def main():
clients = [core.build_metadata(setup['client'])]
times = dict()
- if 'Cfg' not in core.plugins:
- logger.error("Cfg is not enabled")
- return 1
+ for metadata in clients:
+ for struct in core.GetStructures(metadata):
+ logger.info("Rendering templates from structure %s:%s" %
+ (struct.tag, struct.get("name")))
+ for entry in struct.xpath("//Path"):
+ path = entry.get("name")
+ logger.info("Rendering %s..." % path)
+ times[path] = dict()
+ avg = 0.0
+ for i in range(runs):
+ start = time.time()
+ try:
+ core.Bind(entry, metadata)
+ avg += (time.time() - start) / runs
+ except:
+ break
+ if avg:
+ logger.debug(" %s: %.02f sec" % (metadata.hostname, avg))
+ times[path][metadata.hostname] = avg
- cfg = core.plugins['Cfg']
- entrysets = []
- for template in templates:
- try:
- entrysets.append(cfg.entries[template])
- except KeyError:
- logger.debug("Template %s not found" % template)
- if not entrysets:
- logger.debug("Using all entrysets")
- entrysets = cfg.entries.values()
-
- for eset in entrysets:
- path = eset.path.replace(setup['repo'], '')
- logger.info("Rendering %s..." % path)
- times[path] = dict()
- for metadata in clients:
- avg = 0.0
- for i in range(runs):
- entry = lxml.etree.Element("Path")
- start = time.time()
- try:
- eset.bind_entry(entry, metadata)
- avg += (time.time() - start) / runs
- except:
- break
- if avg:
- logger.debug(" %s: %.02f sec" % (metadata.hostname, avg))
- times[path][metadata.hostname] = avg
-
- # print out per-template results
+ # print out per-file results
tmpltimes = []
for tmpl, clients in times.items():
try:
diff --git a/tools/upgrade/1.3/migrate_dbstats.py b/tools/upgrade/1.3/migrate_dbstats.py
index 15bd328f9..69d9514df 100755
--- a/tools/upgrade/1.3/migrate_dbstats.py
+++ b/tools/upgrade/1.3/migrate_dbstats.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import os
+os.environ['BCFG2_LEGACY_MODELS'] = '1'
os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings'
import sys
@@ -217,6 +218,14 @@ def migrate_stage1():
def _restructure():
"""major restructure of reporting data"""
+ # run any migrations from the previous schema
+ try:
+ from Bcfg2.Server.Reports.updatefix import update_database
+ update_database()
+ except:
+ logger.error("Failed to run legacy schema updates", exc_info=1)
+ return False
+
# try to avoid dangling transactions
if not migrate_stage1():
return