summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml10
-rw-r--r--debian/bcfg2-server.install1
-rw-r--r--debian/changelog12
-rw-r--r--doc/conf.py2
-rw-r--r--doc/development/compat.txt2
-rw-r--r--doc/exts/xmlschema.py7
-rw-r--r--doc/help/troubleshooting.txt4
-rw-r--r--doc/man/bcfg2.conf.txt12
-rw-r--r--doc/man/bcfg2.txt9
-rw-r--r--doc/reports/dynamic.txt14
-rw-r--r--doc/server/plugins/generators/packages.txt16
-rw-r--r--doc/server/plugins/generators/rules.txt2
-rw-r--r--doc/server/plugins/version/git.txt6
-rw-r--r--man/bcfg2-admin.85
-rw-r--r--man/bcfg2-build-reports.85
-rw-r--r--man/bcfg2-crypt.85
-rw-r--r--man/bcfg2-info.85
-rw-r--r--man/bcfg2-lint.85
-rw-r--r--man/bcfg2-lint.conf.55
-rw-r--r--man/bcfg2-reports.85
-rw-r--r--man/bcfg2-server.85
-rw-r--r--man/bcfg2.114
-rw-r--r--man/bcfg2.conf.541
-rw-r--r--misc/apache/bcfg2.conf2
-rw-r--r--misc/bcfg2-selinux.spec16
-rw-r--r--misc/bcfg2.spec23
-rw-r--r--osx/Makefile4
-rw-r--r--osx/macports/Portfile2
-rw-r--r--redhat/RELEASE2
-rw-r--r--redhat/VERSION2
-rw-r--r--redhat/bcfg2.spec.in6
-rw-r--r--schemas/bundle.xsd71
-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.py34
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py1
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/base.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/SELinux.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM.py84
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py108
-rw-r--r--src/lib/Bcfg2/Client/__init__.py28
-rw-r--r--src/lib/Bcfg2/Compat.py10
-rw-r--r--src/lib/Bcfg2/Logger.py6
-rw-r--r--src/lib/Bcfg2/Options.py12
-rw-r--r--src/lib/Bcfg2/Proxy.py7
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base.html2
-rw-r--r--src/lib/Bcfg2/Reporting/templates/widgets/filter_bar.html2
-rw-r--r--src/lib/Bcfg2/SSLServer.py9
-rw-r--r--src/lib/Bcfg2/Server/Admin/Init.py27
-rw-r--r--src/lib/Bcfg2/Server/Core.py41
-rw-r--r--src/lib/Bcfg2/Server/FileMonitor/Inotify.py23
-rw-r--r--src/lib/Bcfg2/Server/FileMonitor/__init__.py12
-rw-r--r--src/lib/Bcfg2/Server/Lint/RequiredAttrs.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/POSIXCompat.py5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py88
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/__init__.py19
-rw-r--r--src/lib/Bcfg2/Server/models.py3
-rw-r--r--src/lib/Bcfg2/version.py2
-rwxr-xr-xsrc/sbin/bcfg2-reports12
-rwxr-xr-xsrc/sbin/bcfg2-test9
-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/test_code_checks.py12
-rwxr-xr-xtestsuite/install.sh2
-rwxr-xr-xtools/bcfg2-profile-templates.py15
-rwxr-xr-xtools/export.py38
-rwxr-xr-xtools/upgrade/1.3/migrate_info.py6
-rwxr-xr-x[-rw-r--r--]tools/upgrade/1.3/migrate_perms_to_mode.py20
81 files changed, 2006 insertions, 1019 deletions
diff --git a/.travis.yml b/.travis.yml
index 8786dcc77..d0476c3c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@ python:
- "2.6"
- "2.7"
- "3.2"
+ - "3.3"
env:
- WITH_OPTIONAL_DEPS=yes
- WITH_OPTIONAL_DEPS=no
@@ -11,21 +12,22 @@ matrix:
exclude:
- python: "3.2"
env: WITH_OPTIONAL_DEPS=yes
+ - python: "3.3"
+ env: WITH_OPTIONAL_DEPS=yes
before_install:
- testsuite/before_install.sh
install:
- testsuite/install.sh
- - pip install -e .
+ - pip install --use-mirrors -e .
script:
- nosetests testsuite
branches:
except:
- - maint
+ - maint-1.2
- 1.1.0-stable
- - py3k
notifications:
email: chris.a.st.pierre@gmail.com
- irc:
+ irc:
channels:
- "irc.freenode.org#bcfg2"
use_notice: true
diff --git a/debian/bcfg2-server.install b/debian/bcfg2-server.install
index 91b1b2aef..533ca2e43 100644
--- a/debian/bcfg2-server.install
+++ b/debian/bcfg2-server.install
@@ -1,6 +1,7 @@
debian/bcfg2-server.default usr/share/bcfg2
debian/tmp/usr/bin/bcfg2-* usr/sbin
debian/tmp/usr/lib/python*/*-packages/Bcfg2/Server/*
+debian/tmp/usr/lib/python*/*-packages/Bcfg2/Reporting/*
debian/tmp/usr/share/bcfg2/Hostbase/*
debian/tmp/usr/share/bcfg2/schemas/*
debian/tmp/usr/share/bcfg2/xsl-transforms/*
diff --git a/debian/changelog b/debian/changelog
index 3a38bf02a..298e695c5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+bcfg2 (1.3.1-0.0) unstable; urgency=low
+
+ * New upstream release
+
+ -- Sol Jerome <sol.jerome@gmail.com> Thu, 21 Mar 2013 09:32:16 -0500
+
+bcfg2 (1.3.0-0.0) unstable; urgency=low
+
+ * New upstream release
+
+ -- Sol Jerome <sol.jerome@gmail.com> Fri, 15 Mar 2013 08:45:18 -0500
+
bcfg2 (1.3.0rc2-0.0) unstable; urgency=low
* New upstream release
diff --git a/doc/conf.py b/doc/conf.py
index a1bceb1b1..d3d30687b 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -66,7 +66,7 @@ else:
# The short X.Y version.
version = '1.3'
# The full version, including alpha/beta/rc tags.
-release = '1.3.0'
+release = '1.3.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
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/exts/xmlschema.py b/doc/exts/xmlschema.py
index 727b4bbd0..24cbf2e2d 100644
--- a/doc/exts/xmlschema.py
+++ b/doc/exts/xmlschema.py
@@ -76,6 +76,11 @@ from sphinx.util.nodes import make_refnode, split_explicit_title, \
from sphinx.util.compat import Directive
from sphinx.domains import ObjType, Domain
+try:
+ from new import classobj
+except ImportError:
+ classobj = type
+
XS = "http://www.w3.org/2001/XMLSchema"
XS_NS = "{%s}" % XS
NSMAP = dict(xs=XS)
@@ -653,7 +658,7 @@ def append_node(parent, cls_or_node, *contents):
def build_node(cls_or_node, *contents):
- if isinstance(cls_or_node, type):
+ if isinstance(cls_or_node, (type, classobj)):
rv = cls_or_node()
else:
rv = cls_or_node
diff --git a/doc/help/troubleshooting.txt b/doc/help/troubleshooting.txt
index 35c1e93a2..aac831ae0 100644
--- a/doc/help/troubleshooting.txt
+++ b/doc/help/troubleshooting.txt
@@ -56,8 +56,8 @@ the debug level individually on a given plugin, e.g.::
Finally, the File Activity Monitor has its own analogue to these two
methods, for setting the debug level of the FAM:
- bcfg2-admin xcmd toggle_fam_debug
- bcfg2-admin xcmd set_fam_debug false
+ bcfg2-admin xcmd Inotify.toggle_debug
+ bcfg2-admin xcmd Inotify.set_debug false
Check if all repository XML files conform to schemas
====================================================
diff --git a/doc/man/bcfg2.conf.txt b/doc/man/bcfg2.conf.txt
index d8f2bc3df..3a0217aef 100644
--- a/doc/man/bcfg2.conf.txt
+++ b/doc/man/bcfg2.conf.txt
@@ -729,11 +729,21 @@ control the database connection of the server.
port
Port for database connections. Not used for sqlite3.
+Reporting options
+-----------------
+
+ config
+ Specifies the location of the reporting configuration (default
+ is /etc/bcfg2-web.conf.
+
time_zone
- Specify a time zone other than that used on the system. (Note
+ Specifies a time zone other than that used on the system. (Note
that this will cause the Bcfg2 server to log messages in this
time zone as well).
+ web_debug
+ Turn on Django debugging.
+
See Also
--------
diff --git a/doc/man/bcfg2.txt b/doc/man/bcfg2.txt
index 6a77a4a3c..6df4f9b4f 100644
--- a/doc/man/bcfg2.txt
+++ b/doc/man/bcfg2.txt
@@ -88,10 +88,13 @@ Options
the constraints of correctness, and thus should only
be used in safe conditions.
-r mode Cause bcfg2 to remove extra configuration elements
- it detects. Mode is one of "all", "Services", or
- "Packages". "all" removes all entries. Likewise,
- "Services" and "Packages" remove only the extra
+ it detects. Mode is one of "all", "Services",
+ "Packages", or "Users". "all" removes all extra entries.
+ "Services", "Packages", and "Users" remove only the extra
configuration elements of the respective type.
+ ("Services" actually just disables extra services,
+ since they can't be removed, and "Users" removes
+ extra POSIXUser and POSIXUser entries.)
-s servicemode Set bcfg2 interaction level for services. Default
behavior is to modify all services affected by
reconfiguration. "build" mode attempts to stop all
diff --git a/doc/reports/dynamic.txt b/doc/reports/dynamic.txt
index 19c947a71..14eff6f54 100644
--- a/doc/reports/dynamic.txt
+++ b/doc/reports/dynamic.txt
@@ -166,20 +166,22 @@ Upgrading
2. Replace the DBStats plugin with the Reporting plugin.
3. Migrate historic data.
- Run `tools/upgrade/1.3/migrate_dbstats.py`
+ Run ``tools/upgrade/1.3/migrate_dbstats.py``
The reporting schema is now managed using `South <http://south.aeracode.org>`_
instead of a set of custom scripts. This creates the new schema and imports
all of the historic data to the new format.
-.. Note:
+ .. note::
- After the database is upgraded all of the old tables are left intact. To
- remove them any table starting with reports_ can be dropped.
+ After the database is upgraded all of the old tables are left
+ intact. To remove them any table starting with reports_ can
+ be dropped.
4. `(Optional)` Run the :ref:`Report Collector <report_collector>`
- Add "transport = LocalFilesystem" under "[reporting]" in `bcfg2.conf`.
- Restart the bcfg2-server and start the bcfg2-report-collector.
+ Add "transport = LocalFilesystem" under "[reporting]" in
+ ``bcfg2.conf``. Restart the bcfg2-server and start the
+ bcfg2-report-collector.
Configuring
===========
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index b6aa6190e..73145fd6b 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -279,7 +279,8 @@ something like this:
<Source type="apt" recommended="true" ...>
.. warning:: You must regenerate the Packages cache when adding or
- removing the recommended attribute.
+ removing the recommended attribute (``bcfg2-admin xcmd
+ Packages.Refresh``).
.. [#f1] Bcfg2 will by default add **Essential** packages to the
client specification. You can disable this behavior by
@@ -383,9 +384,9 @@ will report information like::
Packages: Updating http://mirror.centos.org/centos/5/extras/x86_64/repodata/filelists.xml.gz
Packages: Updating http://mirror.centos.org/centos/5/extras/x86_64/repodata/primary.xml.gz
-Once line per file download needed. ``Packages/sources.xml`` will
-be reloaded at this time, so any source specification changes (new
-or modified sources in this file) will be reflected by the server at
+One line per file download needed. ``Packages/sources.xml`` will be
+reloaded at this time, so any source specification changes (new or
+modified sources in this file) will be reflected by the server at
this point.
This process is much, much faster if you use the :ref:`native yum
@@ -488,7 +489,6 @@ Benefits to this include:
* Much lower memory usage by the ``bcfg2-server`` process.
* Much faster ``Packages.Refresh`` behavior.
* More accurate dependency resolution.
-* Support for package groups.
Drawbacks include:
@@ -537,9 +537,9 @@ generally be overridden:
Package Groups
--------------
-Yum package groups are supported by the native Yum libraries. To
-include a package group, use the
-:xml:attribute:`PackageStructure:group` attribute of the
+Yum package groups are supported by both the native Yum libraries and
+Bcfg2's internal dependency resolver. To 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:
diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt
index a3f29a803..2789411e7 100644
--- a/doc/server/plugins/generators/rules.txt
+++ b/doc/server/plugins/generators/rules.txt
@@ -393,7 +393,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/doc/server/plugins/version/git.txt b/doc/server/plugins/version/git.txt
index 3f7ab9d9b..64ff422ca 100644
--- a/doc/server/plugins/version/git.txt
+++ b/doc/server/plugins/version/git.txt
@@ -13,12 +13,6 @@ reporting purposes. Once the plugin is enabled, every time a client
checks in, it will include the current repository revision in the
reports/statistics.
-As with the other Version plugins, the Git plugin enables you to get
-revision information out of your repository for reporting
-purposes. Once the plugin is enabled, every time a client checks in,
-it will include the current repository revision in the
-reports/statistics.
-
Additionally, if the ``GitPython`` library is installed, the Git
plugin exposes an additional XML-RPC method call, ``Git.Update``.
With no arguments, ``Git.Update`` updates the working copy to the
diff --git a/man/bcfg2-admin.8 b/man/bcfg2-admin.8
index 279ee7e0b..06cbeec0b 100644
--- a/man/bcfg2-admin.8
+++ b/man/bcfg2-admin.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-ADMIN" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-ADMIN" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-admin \- Perform repository administration tasks
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -249,5 +249,4 @@ Add a shape/color key.
.sp
\fIbcfg2\-info(8)\fP, \fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-build-reports.8 b/man/bcfg2-build-reports.8
index 8d3d58ebb..3b4582820 100644
--- a/man/bcfg2-build-reports.8
+++ b/man/bcfg2-build-reports.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-BUILD-REPORTS" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-BUILD-REPORTS" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-build-reports \- Generate state reports for Bcfg2 clients
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -58,5 +58,4 @@ default is \fBrepo/etc/statistics.xml\fP.
.sp
\fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-crypt.8 b/man/bcfg2-crypt.8
index 7c11eb66c..3cdef3f84 100644
--- a/man/bcfg2-crypt.8
+++ b/man/bcfg2-crypt.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-CRYPT" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-CRYPT" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-crypt \- Bcfg2 encryption and decryption utility
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -150,5 +150,4 @@ produced and the file being encrypted or decrypted is skipped.
.sp
\fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-info.8 b/man/bcfg2-info.8
index ee650b5da..a52194372 100644
--- a/man/bcfg2-info.8
+++ b/man/bcfg2-info.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-INFO" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-INFO" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-info \- Creates a local version of the Bcfg2 server core for state observation
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -136,5 +136,4 @@ Print version of this tool.
.sp
\fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-lint.8 b/man/bcfg2-lint.8
index c81c305f1..22632f5dd 100644
--- a/man/bcfg2-lint.8
+++ b/man/bcfg2-lint.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-LINT" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-LINT" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-lint \- Check Bcfg2 specification for validity, common mistakes, and style
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -143,5 +143,4 @@ than by a mix of Cfg and TGenshi or TCheetah.
\fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP,
\fIbcfg2\-lint.conf(5)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-lint.conf.5 b/man/bcfg2-lint.conf.5
index d6d299616..a2b34e601 100644
--- a/man/bcfg2-lint.conf.5
+++ b/man/bcfg2-lint.conf.5
@@ -1,4 +1,4 @@
-.TH "BCFG2-LINT.CONF" "5" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-LINT.CONF" "5" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-lint.conf \- Configuration parameters for bcfg2-lint
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH DESCRIPTION
.sp
@@ -158,5 +158,4 @@ The full path to the XML Schema files. Default is
.sp
\fIbcfg2\-lint(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-reports.8 b/man/bcfg2-reports.8
index 7b1622a5e..6d0db36fa 100644
--- a/man/bcfg2-reports.8
+++ b/man/bcfg2-reports.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-REPORTS" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-REPORTS" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-reports \- Query reporting system for client status
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -142,5 +142,4 @@ specified file instead of the command line.
.sp
\fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2-server.8 b/man/bcfg2-server.8
index 8df8ebfd5..27f6a7b01 100644
--- a/man/bcfg2-server.8
+++ b/man/bcfg2-server.8
@@ -1,4 +1,4 @@
-.TH "BCFG2-SERVER" "8" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2-SERVER" "8" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2-server \- Server for client configuration specifications
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -76,5 +76,4 @@ Specify the path to the SSL key.
.sp
\fIbcfg2(1)\fP, \fIbcfg2\-lint(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2.1 b/man/bcfg2.1
index 1e0f526c8..5b9449fda 100644
--- a/man/bcfg2.1
+++ b/man/bcfg2.1
@@ -1,4 +1,4 @@
-.TH "BCFG2" "1" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2" "1" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2 \- Bcfg2 client tool
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH SYNOPSIS
.sp
@@ -164,10 +164,13 @@ be used in safe conditions.
.TP
.BI \-r \ mode
Cause bcfg2 to remove extra configuration elements
-it detects. Mode is one of "all", "Services", or
-"Packages". "all" removes all entries. Likewise,
-"Services" and "Packages" remove only the extra
+it detects. Mode is one of "all", "Services",
+"Packages", or "Users". "all" removes all extra entries.
+"Services", "Packages", and "Users" remove only the extra
configuration elements of the respective type.
+("Services" actually just disables extra services,
+since they can\(aqt be removed, and "Users" removes
+extra POSIXUser and POSIXUser entries.)
.TP
.BI \-s \ servicemode
Set bcfg2 interaction level for services. Default
@@ -206,5 +209,4 @@ Only configure independent entries, ignore bundles.
.sp
\fIbcfg2\-server(8)\fP, \fIbcfg2\-info(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/man/bcfg2.conf.5 b/man/bcfg2.conf.5
index 0a982b8f0..b0db91a5b 100644
--- a/man/bcfg2.conf.5
+++ b/man/bcfg2.conf.5
@@ -1,4 +1,4 @@
-.TH "BCFG2.CONF" "5" "January 14, 2013" "1.3" "Bcfg2"
+.TH "BCFG2.CONF" "5" "March 18, 2013" "1.3" "Bcfg2"
.SH NAME
bcfg2.conf \- Configuration parameters for Bcfg2
.
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.\" Man page generated from reStructeredText.
+.\" Man page generated from reStructuredText.
.
.SH DESCRIPTION
.sp
@@ -62,6 +62,8 @@ the \fIbcfg2\-admin init\fP command.
The file monitor used to watch for changes in the repository. The
default is the best available monitor. The following values are
valid:
+.INDENT 7.0
+.INDENT 3.5
.sp
.nf
.ft C
@@ -71,10 +73,14 @@ fam
pseudo
.ft P
.fi
+.UNINDENT
+.UNINDENT
.TP
.B ignore_files
A comma\-separated list of globs that should be ignored by the file
monitor. Default values are:
+.INDENT 7.0
+.INDENT 3.5
.sp
.nf
.ft C
@@ -90,6 +96,8 @@ SCCS
\&.gitignore
.ft P
.fi
+.UNINDENT
+.UNINDENT
.TP
.B listen_all
This setting tells the server to listen on all available interfaces.
@@ -99,6 +107,8 @@ bcfg2 setting in the components section of \fBbcfg2.conf\fP.
.B plugins
A comma\-delimited list of enabled server plugins. Currently
available plugins are:
+.INDENT 7.0
+.INDENT 3.5
.sp
.nf
.ft C
@@ -145,6 +155,8 @@ TGenshi
Trigger
.ft P
.fi
+.UNINDENT
+.UNINDENT
.sp
Descriptions of each plugin can be found in their respective
sections below.
@@ -156,6 +168,8 @@ default location (e.g. \fB/usr/local\fP).
.B backend
Specifies which server core backend to use. Current available
options are:
+.INDENT 7.0
+.INDENT 3.5
.sp
.nf
.ft C
@@ -164,6 +178,8 @@ builtin
best
.ft P
.fi
+.UNINDENT
+.UNINDENT
.sp
The default is \fIbest\fP, which is currently an alias for \fIbuiltin\fP.
More details on the backends can be found in the official
@@ -725,6 +741,8 @@ control the database connection of the server.
.B engine
The database engine used by the statistics module. One of the
following:
+.INDENT 7.0
+.INDENT 3.5
.sp
.nf
.ft C
@@ -734,6 +752,8 @@ sqlite3
ado_mssql
.ft P
.fi
+.UNINDENT
+.UNINDENT
.TP
.B name
The name of the database to use for statistics data. If
@@ -751,11 +771,25 @@ Host for database connections. Not used for sqlite3.
.TP
.B port
Port for database connections. Not used for sqlite3.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH REPORTING OPTIONS
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B config
+Specifies the location of the reporting configuration (default
+is /etc/bcfg2\-web.conf.
.TP
.B time_zone
-Specify a time zone other than that used on the system. (Note
+Specifies a time zone other than that used on the system. (Note
that this will cause the Bcfg2 server to log messages in this
time zone as well).
+.TP
+.B web_debug
+Turn on Django debugging.
.UNINDENT
.UNINDENT
.UNINDENT
@@ -763,5 +797,4 @@ time zone as well).
.sp
\fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP
.\" Generated by docutils manpage writer.
-.\"
.
diff --git a/misc/apache/bcfg2.conf b/misc/apache/bcfg2.conf
index 6cd5addf5..b8409a513 100644
--- a/misc/apache/bcfg2.conf
+++ b/misc/apache/bcfg2.conf
@@ -4,7 +4,7 @@
#
WSGIScriptAlias /bcfg2 "/usr/share/bcfg2/reports.wsgi"
- WSGISocketPrefix /var/run/httpd/wsgi
+ WSGISocketPrefix /var/run/apache2/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 e5a0eed16..4c05f4959 100644
--- a/misc/bcfg2-selinux.spec
+++ b/misc/bcfg2-selinux.spec
@@ -8,8 +8,8 @@
%global selinux_variants %([ -z "%{selinux_types}" ] && echo mls strict targeted || echo %{selinux_types})
Name: bcfg2-selinux
-Version: 1.3.0
-Release: 0.0rc2
+Version: 1.3.1
+Release: 1
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}rc2.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}rc2-%{release}-root-%(%{__id_u} -n)
+Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{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}rc2
+%setup -q -n %{name}-%{version}
%build
cd redhat/selinux
@@ -120,6 +120,12 @@ if [ $1 -eq 0 ] ; then
fi
%changelog
+* Thu Mar 21 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.1-1
+- New upstream release
+
+* Fri Mar 15 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0
+- New upstream release
+
* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
- New upstream release
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index 35435001f..e6b21d76c 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -5,8 +5,8 @@
%{!?_initrddir: %global _initrddir %{_sysconfdir}/rc.d/init.d}
Name: bcfg2
-Version: 1.3.0
-Release: 0.0rc2
+Version: 1.3.1
+Release: 1
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}rc2.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}rc2-%{release}-root-%(%{__id_u} -n)
+Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
BuildRequires: python-devel
@@ -87,7 +87,7 @@ deployment strategies.
This package includes the Bcfg2 client software.
%package server
-Version: 1.3.0
+Version: 1.3.1
Summary: Bcfg2 Server
%if 0%{?suse_version}
Group: System/Management
@@ -140,7 +140,7 @@ deployment strategies.
This package includes the Bcfg2 server software.
%package server-cherrypy
-Version: 1.3.0
+Version: 1.3.1
Summary: Bcfg2 Server - CherryPy backend
%if 0%{?suse_version}
Group: System/Management
@@ -220,7 +220,7 @@ deployment strategies.
This package includes the Bcfg2 documentation.
%package web
-Version: 1.3.0
+Version: 1.3.1
Summary: Bcfg2 Web Reporting Interface
%if 0%{?suse_version}
Group: System/Management
@@ -267,7 +267,7 @@ deployment strategies.
This package includes the Bcfg2 reports web frontend.
%prep
-%setup -q -n %{name}-%{version}rc2
+%setup -q -n %{name}-%{version}
%build
%{__python}%{pythonversion} setup.py build
@@ -311,6 +311,7 @@ cp -r tools/* %{buildroot}%{_defaultdocdir}/bcfg2-server-%{version}
cp -r build/sphinx/html/* %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}
%{__install} -d %{buildroot}%{apache_conf}/conf.d
+sed -i "s/apache2/httpd/g" misc/apache/bcfg2.conf
%{__install} -m 644 misc/apache/bcfg2.conf %{buildroot}%{apache_conf}/conf.d/wsgi_bcfg2.conf
%{__mkdir_p} %{buildroot}%{_localstatedir}/cache/%{name}
@@ -454,6 +455,12 @@ fi
%endif
%changelog
+* Thu Mar 21 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.1-1
+- New upstream release
+
+* Fri Mar 15 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0
+- New upstream release
+
* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
- New upstream release
diff --git a/osx/Makefile b/osx/Makefile
index a46b29116..f25e71927 100644
--- a/osx/Makefile
+++ b/osx/Makefile
@@ -29,9 +29,9 @@ 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.0rc2
+BCFGVER = 1.3.1
MAJOR = 1
-MINOR = 30
+MINOR = 31
default: clean client
diff --git a/osx/macports/Portfile b/osx/macports/Portfile
index f53974670..45cf3dd2b 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.0rc2
+version 1.3.1
categories sysutils python
maintainers gmail.com:sol.jerome
license BSD
diff --git a/redhat/RELEASE b/redhat/RELEASE
index c39908ef3..ba66466c2 100644
--- a/redhat/RELEASE
+++ b/redhat/RELEASE
@@ -1 +1 @@
-0.0rc2
+0.0
diff --git a/redhat/VERSION b/redhat/VERSION
index f0bb29e76..3a3cd8cc8 100644
--- a/redhat/VERSION
+++ b/redhat/VERSION
@@ -1 +1 @@
-1.3.0
+1.3.1
diff --git a/redhat/bcfg2.spec.in b/redhat/bcfg2.spec.in
index 161134091..b1cd0d097 100644
--- a/redhat/bcfg2.spec.in
+++ b/redhat/bcfg2.spec.in
@@ -262,6 +262,12 @@ fi
%doc %{_defaultdocdir}/bcfg2-doc-%{version}
%changelog
+* Thu Mar 21 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.1-1
+- New upstream release
+
+* Fri Mar 15 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0
+- New upstream release
+
* Tue Jan 29 2013 Sol Jerome <sol.jerome@gmail.com> 1.3.0-0.0rc2
- New upstream release
diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd
index 68e793920..337fc5ec7 100644
--- a/schemas/bundle.xsd
+++ b/schemas/bundle.xsd
@@ -58,14 +58,6 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
- <xsd:element name='SELinux' type='SELinuxStructure'>
- <xsd:annotation>
- <xsd:documentation>
- Abstract implementation of an SELinux entry. The
- full specification will be included in Rules.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
<xsd:element name='POSIXUser' type='StructureEntry'>
<xsd:annotation>
<xsd:documentation>
@@ -89,6 +81,69 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
+ <xsd:element name='SEBoolean' type='SELinuxStructure'>
+ <xsd:annotation>
+ <xsd:documentation>
+ 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>
<xsd:element name='BoundPackage' type='PackageType'>
<xsd:annotation>
<xsd:documentation>
diff --git a/setup.py b/setup.py
index 61590bcd3..99e1ef025 100755
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ if need_m2crypto:
inst_reqs.append('M2Crypto')
setup(name="Bcfg2",
- version="1.3.0rc2",
+ version="1.3.1",
description="Bcfg2 Server",
author="Narayan Desai",
author_email="desai@mcs.anl.gov",
diff --git a/solaris/Makefile b/solaris/Makefile
index d81dd0292..fd2c254bb 100644
--- a/solaris/Makefile
+++ b/solaris/Makefile
@@ -1,7 +1,7 @@
#!/usr/sfw/bin/gmake
PYTHON="/usr/local/bin/python"
-VERS=1.3.0rc2-1
+VERS=1.3.1-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 b95b24f75..2bf3abaf5 100644
--- a/solaris/pkginfo.bcfg2
+++ b/solaris/pkginfo.bcfg2
@@ -1,7 +1,7 @@
PKG="SCbcfg2"
NAME="bcfg2"
ARCH="sparc"
-VERSION="1.3.0rc2"
+VERSION="1.3.1"
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 25b0bfa4b..4425220c2 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.0rc2"
+VERSION="1.3.1"
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 a95c0a7a6..bc6bd4d4c 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':
@@ -354,7 +341,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)
# need to test to fail entries in whitelist
@@ -377,7 +364,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:
@@ -474,7 +461,8 @@ class Frame(object):
len(list(self.states.values())))
self.logger.info('Unmanaged entries: %d' % len(self.extra))
if phase == 'final' and self.setup['extra']:
- for entry in self.extra:
+ for entry in sorted(self.extra, key=lambda e: e.tag + ":" +
+ e.get('name')):
etype = entry.get('type')
if etype:
self.logger.info("%s:%s:%s" % (entry.tag, etype,
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/POSIX/base.py b/src/lib/Bcfg2/Client/Tools/POSIX/base.py
index b867fa3d8..f46875743 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/base.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/base.py
@@ -687,7 +687,7 @@ class POSIXTool(Bcfg2.Client.Tools.Tool):
if path is None:
path = entry.get("name")
cur = path
- while cur != '/':
+ while cur and cur != '/':
if not os.path.exists(cur):
created.append(cur)
cur = os.path.dirname(cur)
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 1fe275c2c..c9fae7fc7 100644
--- a/src/lib/Bcfg2/Client/Tools/YUM.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM.py
@@ -123,7 +123,7 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
('Package', 'rpm'),
('Path', 'ignore')]
- __req__ = {'Package': ['name'],
+ __req__ = {'Package': ['type'],
'Path': ['type']}
conflicts = ['YUM24', 'RPM', 'RPMng', 'YUMng']
@@ -287,6 +287,17 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
return self.yumbase.rpmdb.returnGPGPubkeyPackages()
return self.yumbase.rpmdb.searchNevra(name='gpg-pubkey')
+ def missing_attrs(self, entry):
+ """ Implementing from superclass to check for existence of either
+ name or group attribute for Package entry in the case of a YUM
+ group. """
+ missing = Bcfg2.Client.Tools.PkgTool.missing_attrs(self, entry)
+
+ if entry.get('name', None) == None and \
+ entry.get('group', None) == None:
+ missing += ['name', 'group']
+ return missing
+
def _verifyHelper(self, pkg_obj):
""" _verifyHelper primarly deals with a yum bug where the
pkg_obj.verify() method does not properly take into count multilib
@@ -409,8 +420,12 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
if entry.get('version', False) == 'auto':
self._fixAutoVersion(entry)
- self.logger.debug("Verifying package instances for %s" %
- entry.get('name'))
+ if entry.get('group'):
+ self.logger.debug("Verifying packages for group %s" %
+ entry.get('group'))
+ else:
+ self.logger.debug("Verifying package instances for %s" %
+ entry.get('name'))
self.verify_cache = dict() # Used for checking multilib packages
self.modlists[entry] = modlist
@@ -423,14 +438,58 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
entry.get('pkg_checks', 'true').lower() == 'true'
pkg_verify = self.pkg_verify and \
entry.get('pkg_verify', 'true').lower() == 'true'
+ yum_group = False
if entry.get('name') == 'gpg-pubkey':
all_pkg_objs = self._getGPGKeysAsPackages()
pkg_verify = False # No files here to verify
+ elif entry.get('group'):
+ entry.set('name', 'group:%s' % entry.get('group'))
+ yum_group = True
+ all_pkg_objs = []
+ instances = []
+ if self.yumbase.comps.has_group(entry.get('group')):
+ group = self.yumbase.comps.return_group(entry.get('group'))
+ group_packages = [p
+ for p, d in group.mandatory_packages.items()
+ if d]
+ group_type = entry.get('choose', 'default')
+ if group_type in ['default', 'optional', 'all']:
+ group_packages += [p
+ for p, d in
+ group.default_packages.items()
+ if d]
+ if group_type in ['optional', 'all']:
+ group_packages += [p
+ for p, d in
+ group.optional_packages.items()
+ if d]
+ if len(group_packages) == 0:
+ self.logger.error("No packages found for group %s" %
+ entry.get("group"))
+ for pkg in group_packages:
+ # create package instances for each package in yum group
+ instance = Bcfg2.Client.XML.SubElement(entry, 'Package')
+ instance.attrib['name'] = pkg
+ instance.attrib['type'] = 'yum'
+ try:
+ newest = \
+ self.yumbase.pkgSack.returnNewestByName(pkg)[0]
+ instance.attrib['version'] = newest['version']
+ instance.attrib['epoch'] = newest['epoch']
+ instance.attrib['release'] = newest['release']
+ except: # pylint: disable=W0702
+ self.logger.info("Error finding newest package "
+ "for %s" %
+ pkg)
+ instance.attrib['version'] = 'any'
+ instances.append(instance)
+ else:
+ self.logger.error("Group not found: %s" % entry.get("group"))
else:
all_pkg_objs = \
self.yumbase.rpmdb.searchNevra(name=entry.get('name'))
- if len(all_pkg_objs) == 0:
+ if len(all_pkg_objs) == 0 and yum_group != True:
# Some sort of virtual capability? Try to resolve it
all_pkg_objs = self.yumbase.rpmdb.searchProvides(entry.get('name'))
if len(all_pkg_objs) > 0:
@@ -441,7 +500,13 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
self.logger.info(" %s" % pkg)
for inst in instances:
- nevra = build_yname(entry.get('name'), inst)
+ if yum_group:
+ # the entry is not the name of the package
+ nevra = build_yname(inst.get('name'), inst)
+ all_pkg_objs = \
+ self.yumbase.rpmdb.searchNevra(name=inst.get('name'))
+ else:
+ nevra = build_yname(entry.get('name'), inst)
if nevra in pkg_cache:
continue # Ignore duplicate instances
else:
@@ -455,7 +520,10 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
stat['version_fail'] = False
stat['verify'] = {}
stat['verify_fail'] = False
- stat['pkg'] = entry
+ if yum_group:
+ stat['pkg'] = inst
+ else:
+ stat['pkg'] = entry
stat['modlist'] = modlist
if inst.get('verify_flags'):
# this splits on either space or comma
@@ -624,7 +692,9 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
else:
install_only = False
- if virt_pkg or (install_only and not self.setup['kevlar']):
+ if virt_pkg or \
+ (install_only and not self.setup['kevlar']) or \
+ yum_group:
# virtual capability supplied, we are probably dealing
# with multiple packages of different names. This check
# doesn't make a lot of since in this case.
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index cd86a2a4b..a4a68ea3b 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,12 +448,9 @@ 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):
+ pkgcmd = self._get_package_command(packages)
+ self.logger.debug("Running command: %s" % pkgcmd)
+ if self.cmd.run(pkgcmd):
self.logger.info("Single Pass Succeded")
# set all package states to true and flush workqueues
pkgnames = [pkg.get('name') for pkg in packages]
@@ -436,7 +459,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 +475,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 +575,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 +590,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 +607,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 4bcc76e8f..44c76303c 100644
--- a/src/lib/Bcfg2/Compat.py
+++ b/src/lib/Bcfg2/Compat.py
@@ -19,12 +19,13 @@ except ImportError:
# urllib imports
try:
+ from urllib import quote_plus
from urlparse import urljoin, urlparse
from urllib2 import HTTPBasicAuthHandler, \
HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, \
urlopen, HTTPError, URLError
except ImportError:
- from urllib.parse import urljoin, urlparse
+ from urllib.parse import urljoin, urlparse, quote_plus
from urllib.request import HTTPBasicAuthHandler, \
HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, urlopen
from urllib.error import HTTPError, URLError
@@ -262,3 +263,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/Logger.py b/src/lib/Bcfg2/Logger.py
index 618d0f2cd..8f7bb14f8 100644
--- a/src/lib/Bcfg2/Logger.py
+++ b/src/lib/Bcfg2/Logger.py
@@ -105,7 +105,11 @@ class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
(self.encodePriority(self.facility, newrec.levelname.lower()),
self.format(newrec))
try:
- self.socket.send(msg.encode('ascii'))
+ try:
+ encoded = msg.encode('utf-8')
+ except UnicodeDecodeError:
+ encoded = msg
+ self.socket.send(encoded)
except socket.error:
for i in range(10): # pylint: disable=W0612
try:
diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py
index 8e7540c22..7c91ca3cc 100644
--- a/src/lib/Bcfg2/Options.py
+++ b/src/lib/Bcfg2/Options.py
@@ -642,17 +642,19 @@ WEB_CFILE = \
default="/etc/bcfg2-web.conf",
cmd='-W',
odesc='<conffile>',
- cf=('statistics', 'config'),)
+ cf=('reporting', 'config'),
+ deprecated_cf=('statistics', 'web_prefix'),)
DJANGO_TIME_ZONE = \
Option('Django timezone',
default=None,
- cf=('statistics', 'time_zone'),)
+ cf=('reporting', 'time_zone'),
+ deprecated_cf=('statistics', 'web_prefix'),)
DJANGO_DEBUG = \
Option('Django debug',
default=None,
- cf=('statistics', 'web_debug'),
+ cf=('reporting', 'web_debug'),
+ deprecated_cf=('statistics', 'web_prefix'),
cook=get_bool,)
-# Django options
DJANGO_WEB_PREFIX = \
Option('Web prefix',
default=None,
@@ -1107,7 +1109,7 @@ CRYPT_STDOUT = \
cmd='--stdout',
long_arg=True)
CRYPT_PASSPHRASE = \
- Option('Encryption passphrase (name or passphrase)',
+ Option('Encryption passphrase name',
default=None,
cmd='-p',
odesc='<passphrase>')
diff --git a/src/lib/Bcfg2/Proxy.py b/src/lib/Bcfg2/Proxy.py
index 3aefed5d1..62b83d0b4 100644
--- a/src/lib/Bcfg2/Proxy.py
+++ b/src/lib/Bcfg2/Proxy.py
@@ -1,6 +1,6 @@
-import logging
import re
import socket
+import logging
# The ssl module is provided by either Python 2.6 or a separate ssl
# package that works on older versions of Python (see
@@ -20,7 +20,7 @@ import sys
import time
# Compatibility imports
-from Bcfg2.Compat import httplib, xmlrpclib, urlparse
+from Bcfg2.Compat import httplib, xmlrpclib, urlparse, quote_plus
version = sys.version_info[:2]
has_py26 = version >= (2, 6)
@@ -352,7 +352,8 @@ def ComponentProxy(url, user=None, password=None, key=None, cert=None, ca=None,
if user and password:
method, path = urlparse(url)[:2]
- newurl = "%s://%s:%s@%s" % (method, user, password, path)
+ newurl = "%s://%s:%s@%s" % (method, quote_plus(user, ''),
+ quote_plus(password, ''), path)
else:
newurl = url
ssl_trans = XMLRPCTransport(key, cert, ca,
diff --git a/src/lib/Bcfg2/Reporting/templates/base.html b/src/lib/Bcfg2/Reporting/templates/base.html
index 533dcc79e..c73339911 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.0rc2</span>
+ <span>Bcfg2 Version 1.3.1</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/widgets/filter_bar.html b/src/lib/Bcfg2/Reporting/templates/widgets/filter_bar.html
index 759415507..bb4f650d1 100644
--- a/src/lib/Bcfg2/Reporting/templates/widgets/filter_bar.html
+++ b/src/lib/Bcfg2/Reporting/templates/widgets/filter_bar.html
@@ -16,7 +16,7 @@
<label for="id_group">Group filter:</label>
<select id="id_group" name="group" onchange="javascript:url=document.forms['filter_form'].group.value; if(url) { location.href=url }">
{% for group, group_url, selected in groups %}
- <option label="{{group}}" value="{{group_url}}" {% if selected %}selected {% endif %}/>
+ <option value="{{group_url}}" {% if selected %}selected {% endif %}>{{group}}</option>
{% endfor %}
</select>
{% endif %}
diff --git a/src/lib/Bcfg2/SSLServer.py b/src/lib/Bcfg2/SSLServer.py
index 5e3c6232a..a149b676f 100644
--- a/src/lib/Bcfg2/SSLServer.py
+++ b/src/lib/Bcfg2/SSLServer.py
@@ -403,12 +403,9 @@ class XMLRPCServer(SocketServer.ThreadingMixIn, SSLServer,
name = instance.name
except AttributeError:
name = "unknown"
- if hasattr(instance, 'plugins'):
- for pname, pinst in list(instance.plugins.items()):
- for mname in pinst.__rmi__:
- xmname = "%s.%s" % (pname, mname)
- fn = getattr(pinst, mname)
- self.register_function(fn, name=xmname)
+ if hasattr(instance, '_get_rmi'):
+ for fname, func in instance._get_rmi().items():
+ self.register_function(func, name=fname)
self.logger.info("serving %s at %s" % (name, self.url))
def serve_forever(self):
diff --git a/src/lib/Bcfg2/Server/Admin/Init.py b/src/lib/Bcfg2/Server/Admin/Init.py
index 14065980d..4b8d65597 100644
--- a/src/lib/Bcfg2/Server/Admin/Init.py
+++ b/src/lib/Bcfg2/Server/Admin/Init.py
@@ -13,6 +13,7 @@ import subprocess
import Bcfg2.Server.Admin
import Bcfg2.Server.Plugin
import Bcfg2.Options
+import Bcfg2.Server.Plugins.Metadata
from Bcfg2.Compat import input # pylint: disable=W0622
# default config file
@@ -174,8 +175,6 @@ class Init(Bcfg2.Server.Admin.Mode):
self.data['certpath'] = os.path.join(basepath, 'bcfg2.crt')
def __call__(self, args):
- Bcfg2.Server.Admin.Mode.__call__(self, args)
-
# Parse options
opts = Bcfg2.Options.OptionParser(self.options)
opts.parse(args)
@@ -214,7 +213,7 @@ class Init(Bcfg2.Server.Admin.Mode):
"""Ask for the repository path."""
while True:
newrepo = safe_input("Location of Bcfg2 repository [%s]: " %
- self.data['repopath'])
+ self.data['repopath'])
if newrepo != '':
self.data['repopath'] = os.path.abspath(newrepo)
if os.path.isdir(self.data['repopath']):
@@ -292,7 +291,7 @@ class Init(Bcfg2.Server.Admin.Mode):
"created [%s]: " % self.data['keypath'])
if keypath:
self.data['keypath'] = keypath
- certpath = safe_input("Path where Bcfg2 server cert will be created"
+ certpath = safe_input("Path where Bcfg2 server cert will be created "
"[%s]: " % self.data['certpath'])
if certpath:
self.data['certpath'] = certpath
@@ -320,6 +319,16 @@ class Init(Bcfg2.Server.Admin.Mode):
def init_repo(self):
"""Setup a new repo and create the content of the
configuration file."""
+ # Create the repository
+ path = os.path.join(self.data['repopath'], 'etc')
+ try:
+ os.makedirs(path)
+ self._init_plugins()
+ print("Repository created successfuly in %s" %
+ self.data['repopath'])
+ except OSError:
+ print("Failed to create %s." % path)
+
confdata = CONFIG % (self.data['repopath'],
','.join(self.plugins),
self.data['sendmail'],
@@ -335,13 +344,3 @@ class Init(Bcfg2.Server.Admin.Mode):
create_key(self.data['shostname'], self.data['keypath'],
self.data['certpath'], self.data['country'],
self.data['state'], self.data['location'])
-
- # Create the repository
- path = os.path.join(self.data['repopath'], 'etc')
- try:
- os.makedirs(path)
- self._init_plugins()
- print("Repository created successfuly in %s" %
- self.data['repopath'])
- except OSError:
- print("Failed to create %s." % path)
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 9be71e2e2..0ded7ac26 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -2,13 +2,14 @@
implementations inherit from. """
import os
-import atexit
-import logging
-import select
import sys
-import threading
import time
+import atexit
+import select
+import signal
+import logging
import inspect
+import threading
import lxml.etree
import Bcfg2.settings
import Bcfg2.Server
@@ -302,6 +303,14 @@ class BaseCore(object):
#: The CA that signed the server cert
self.ca = setup['ca']
+ def hdlr(sig, frame): # pylint: disable=W0613
+ """ Handle SIGINT/Ctrl-C by shutting down the core and exiting
+ properly. """
+ self.shutdown()
+ os._exit(1) # pylint: disable=W0212
+
+ signal.signal(signal.SIGINT, hdlr)
+
#: The FAM :class:`threading.Thread`,
#: :func:`_file_monitor_thread`
self.fam_thread = \
@@ -904,10 +913,12 @@ class BaseCore(object):
def _get_rmi(self):
""" Get a list of RMI calls exposed by plugins """
rmi = dict()
- if self.plugins:
- for pname, pinst in list(self.plugins.items()):
- for mname in pinst.__rmi__:
- rmi["%s.%s" % (pname, mname)] = getattr(pinst, mname)
+ for pname, pinst in list(self.plugins.items()):
+ for mname in pinst.__rmi__:
+ rmi["%s.%s" % (pname, mname)] = getattr(pinst, mname)
+ famname = self.fam.__class__.__name__
+ for mname in self.fam.__rmi__:
+ rmi["%s.%s" % (famname, mname)] = getattr(self.fam, mname)
return rmi
def _resolve_exposed_method(self, method_name):
@@ -1177,12 +1188,15 @@ class BaseCore(object):
return self.set_core_debug(address, not self.debug_flag)
@exposed
- def toggle_fam_debug(self, _):
+ def toggle_fam_debug(self, address):
""" Toggle debug status of the FAM
:returns: bool - The new debug state of the FAM
"""
- return self.fam.toggle_debug()
+ self.logger.warning("Deprecated method set_fam_debug called by %s" %
+ address[0])
+ return "This method is deprecated and will be removed in a future " + \
+ "release\n%s" % self.fam.toggle_debug()
@exposed
def set_debug(self, address, debug):
@@ -1227,7 +1241,7 @@ class BaseCore(object):
return self.debug_flag
@exposed
- def set_fam_debug(self, _, debug):
+ def set_fam_debug(self, address, debug):
""" Explicitly set debug status of the FAM
:param debug: The new debug status of the FAM. This can
@@ -1239,4 +1253,7 @@ class BaseCore(object):
"""
if debug not in [True, False]:
debug = debug.lower() == "true"
- return self.fam.set_debug(debug)
+ self.logger.warning("Deprecated method set_fam_debug called by %s" %
+ address[0])
+ return "This method is deprecated and will be removed in a future " + \
+ "release\n%s" % self.fam.set_debug(debug)
diff --git a/src/lib/Bcfg2/Server/FileMonitor/Inotify.py b/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
index 178a47b1a..cdd52dbb9 100644
--- a/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
+++ b/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
@@ -2,6 +2,7 @@
support. """
import os
+import errno
import logging
import pyinotify
from Bcfg2.Compat import reduce # pylint: disable=W0622
@@ -15,6 +16,8 @@ class Inotify(Pseudo, pyinotify.ProcessEvent):
""" File monitor backend with `inotify
<http://inotify.aiken.cz/>`_ support. """
+ __rmi__ = Pseudo.__rmi__ + ["list_watches", "list_paths"]
+
#: Inotify is the best FAM backend, so it gets a very high
#: priority
__priority__ = 99
@@ -182,6 +185,9 @@ class Inotify(Pseudo, pyinotify.ProcessEvent):
try:
watchdir = self.watches_by_path[watch_path]
except KeyError:
+ if not os.path.exists(watch_path):
+ raise OSError(errno.ENOENT,
+ "No such file or directory: '%s'" % path)
watchdir = self.watchmgr.add_watch(watch_path, self.mask,
quiet=False)[watch_path]
self.watches_by_path[watch_path] = watchdir
@@ -211,3 +217,20 @@ class Inotify(Pseudo, pyinotify.ProcessEvent):
if self.notifier:
self.notifier.stop()
shutdown.__doc__ = Pseudo.shutdown.__doc__
+
+ def list_watches(self):
+ """ XML-RPC that returns a list of current inotify watches for
+ debugging purposes. """
+ return list(self.watches_by_path.keys())
+
+ def list_paths(self):
+ """ XML-RPC that returns a list of paths that are handled for
+ debugging purposes. Because inotify doesn't like watching
+ files, but prefers to watch directories, this will be
+ different from
+ :func:`Bcfg2.Server.FileMonitor.Inotify.Inotify.ListWatches`. For
+ instance, if a plugin adds a monitor to
+ ``/var/lib/bcfg2/Plugin/foo.xml``, :func:`ListPaths` will
+ return ``/var/lib/bcfg2/Plugin/foo.xml``, while
+ :func:`ListWatches` will return ``/var/lib/bcfg2/Plugin``. """
+ return list(self.handles.keys())
diff --git a/src/lib/Bcfg2/Server/FileMonitor/__init__.py b/src/lib/Bcfg2/Server/FileMonitor/__init__.py
index 54d35e38d..e430e3160 100644
--- a/src/lib/Bcfg2/Server/FileMonitor/__init__.py
+++ b/src/lib/Bcfg2/Server/FileMonitor/__init__.py
@@ -116,6 +116,9 @@ class FileMonitor(Debuggable):
#: should have higher priorities.
__priority__ = -1
+ #: List of names of methods to be exposed as XML-RPC functions
+ __rmi__ = Debuggable.__rmi__ + ["list_event_handlers"]
+
def __init__(self, ignore=None, debug=False):
"""
:param ignore: A list of filename globs describing events that
@@ -312,6 +315,15 @@ class FileMonitor(Debuggable):
"""
raise NotImplementedError
+ def list_event_handlers(self):
+ """ XML-RPC that returns
+ :attr:`Bcfg2.Server.FileMonitor.FileMonitor.handles` for
+ debugging purposes. """
+ rv = dict()
+ for watch, handler in self.handles.items():
+ rv[watch] = getattr(handler, "name", handler.__class__.__name__)
+ return rv
+
#: A dict of all available FAM backends. Keys are the human-readable
#: names of the backends, which are used in bcfg2.conf to select a
diff --git a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
index 61b737a82..2a10da417 100644
--- a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
+++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
@@ -43,7 +43,7 @@ def is_octal_mode(val):
def is_username(val):
""" Return True if val is a string giving either a positive
integer uid, or a valid Unix username """
- return re.match(r'^([a-z]\w{0,30}|\d+)$', val)
+ return re.match(r'^([A-z][-_A-z0-9]{0,30}|\d+)$', val)
def is_device_mode(val):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index 4fa2fb894..c2e5afbad 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -101,6 +101,10 @@ class CfgGenshiGenerator(CfgGenerator):
__init__.__doc__ = CfgGenerator.__init__.__doc__
def get_data(self, entry, metadata):
+ if self.template is None:
+ raise PluginExecutionError("Failed to load template %s" %
+ self.name)
+
fname = entry.get('realname', entry.get('name'))
stream = \
self.template.generate(name=fname,
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 09ecfaf82..a81139b5d 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -139,7 +139,7 @@ class XMLMetadataConfig(Bcfg2.Server.Plugin.XMLFileBacked):
self.logger.error('Failed to parse %s' % self.basefile)
return
self.extras = []
- self.basedata = copy.copy(xdata)
+ self.basedata = copy.deepcopy(xdata)
self._follow_xincludes(xdata=xdata)
if self.extras:
try:
diff --git a/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py b/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
index 0dd42c9cb..490ee6f20 100644
--- a/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
+++ b/src/lib/Bcfg2/Server/Plugins/POSIXCompat.py
@@ -15,6 +15,11 @@ class POSIXCompat(Bcfg2.Server.Plugin.Plugin,
def validate_goals(self, metadata, goals):
"""Verify that we are generating correct old POSIX entries."""
+ if metadata.version_info and metadata.version_info > (1, 3, 0, '', 0):
+ # do not care about a client that is _any_ 1.3.0 release
+ # (including prereleases and RCs)
+ return
+
for goal in goals:
for entry in goal.getchildren():
if entry.tag == 'Path' and 'mode' in entry.keys():
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index 46231c636..4cd938651 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -313,9 +313,7 @@ class YumCollection(Collection):
@property
def __package_groups__(self):
- """ YumCollections support package groups only if
- :attr:`use_yum` is True """
- return self.use_yum
+ return True
@property
def helper(self):
@@ -665,11 +663,6 @@ class YumCollection(Collection):
In this implementation the packages may be strings or tuples.
See :ref:`yum-pkg-objects` for more information. """
- if not self.use_yum:
- self.logger.warning("Packages: Package groups are not supported "
- "by Bcfg2's internal Yum dependency generator")
- return dict()
-
if not grouplist:
return dict()
@@ -680,8 +673,16 @@ class YumCollection(Collection):
if not ptype:
ptype = "default"
gdicts.append(dict(group=group, type=ptype))
-
- return self.call_helper("get_groups", inputdata=gdicts)
+
+ if self.use_yum:
+ return self.call_helper("get_groups", inputdata=gdicts)
+ else:
+ pkgs = dict()
+ for gdict in gdicts:
+ pkgs[gdict['group']] = Collection.get_group(self,
+ gdict['group'],
+ gdict['type'])
+ return pkgs
def _element_to_pkg(self, el, name):
""" Convert a Package or Instance element to a package tuple """
@@ -991,6 +992,7 @@ class YumSource(Source):
for x in ['global'] + self.arches])
self.needed_paths = set()
self.file_to_arch = dict()
+ self.yumgroups = dict()
__init__.__doc__ = Source.__init__.__doc__
@property
@@ -1008,7 +1010,8 @@ class YumSource(Source):
if not self.use_yum:
cache = open(self.cachefile, 'wb')
cPickle.dump((self.packages, self.deps, self.provides,
- self.filemap, self.url_map), cache, 2)
+ self.filemap, self.url_map,
+ self.yumgroups), cache, 2)
cache.close()
def load_state(self):
@@ -1018,7 +1021,7 @@ class YumSource(Source):
if not self.use_yum:
data = open(self.cachefile)
(self.packages, self.deps, self.provides,
- self.filemap, self.url_map) = cPickle.load(data)
+ self.filemap, self.url_map, self.yumgroups) = cPickle.load(data)
@property
def urls(self):
@@ -1073,7 +1076,7 @@ class YumSource(Source):
urls = []
for elt in xdata.findall(RPO + 'data'):
- if elt.get('type') in ['filelists', 'primary']:
+ if elt.get('type') in ['filelists', 'primary', 'group']:
floc = elt.find(RPO + 'location')
fullurl = url + floc.get('href')
urls.append(fullurl)
@@ -1090,11 +1093,14 @@ class YumSource(Source):
# we have to read primary.xml first, and filelists.xml afterwards;
primaries = list()
filelists = list()
+ groups = list()
for fname in self.files:
if fname.endswith('primary.xml.gz'):
primaries.append(fname)
elif fname.endswith('filelists.xml.gz'):
filelists.append(fname)
+ elif fname.find('comps'):
+ groups.append(fname)
for fname in primaries:
farch = self.file_to_arch[fname]
@@ -1104,6 +1110,9 @@ class YumSource(Source):
farch = self.file_to_arch[fname]
fdata = lxml.etree.parse(fname).getroot()
self.parse_filelist(fdata, farch)
+ for fname in groups:
+ fdata = lxml.etree.parse(fname).getroot()
+ self.parse_group(fdata)
# merge data
sdata = list(self.packages.values())
@@ -1167,6 +1176,35 @@ class YumSource(Source):
self.provides[arch][prov] = list()
self.provides[arch][prov].append(pkgname)
+ @Bcfg2.Server.Plugin.track_statistics()
+ def parse_group(self, data):
+ """ parse comps.xml.gz data """
+ for group in data.getchildren():
+ if not group.tag.endswith('group'):
+ continue
+ try:
+ groupid = group.xpath('id')[0].text
+ self.yumgroups[groupid] = {'mandatory': list(),
+ 'default': list(),
+ 'optional': list(),
+ 'conditional': list()}
+ except IndexError:
+ continue
+ try:
+ packagelist = group.xpath('packagelist')[0]
+ except IndexError:
+ continue
+ for pkgreq in packagelist.getchildren():
+ pkgtype = pkgreq.get('type', None)
+ if pkgtype == 'mandatory':
+ self.yumgroups[groupid]['mandatory'].append(pkgreq.text)
+ elif pkgtype == 'default':
+ self.yumgroups[groupid]['default'].append(pkgreq.text)
+ elif pkgtype == 'optional':
+ self.yumgroups[groupid]['optional'].append(pkgreq.text)
+ elif pkgtype == 'conditional':
+ self.yumgroups[groupid]['conditional'].append(pkgreq.text)
+
def is_package(self, metadata, package):
arch = [a for a in self.arches if a in metadata.groups]
if not arch:
@@ -1246,3 +1284,27 @@ class YumSource(Source):
return self.pulp_id
else:
return Source.get_repo_name(self, url_map)
+
+ def get_group(self, metadata, group, ptype=None): # pylint: disable=W0613
+ """ Get the list of packages of the given type in a package
+ group.
+
+ :param group: The name of the group to query
+ :type group: string
+ :param ptype: The type of packages to get, for backends that
+ support multiple package types in package groups
+ (e.g., "recommended," "optional," etc.)
+ :type ptype: string
+ :returns: list of strings - package names
+ """
+ try:
+ yumgroup = self.yumgroups[group]
+ except KeyError:
+ return []
+ packages = yumgroup['conditional'] + yumgroup['mandatory']
+ if ptype in ['default', 'optional', 'all']:
+ packages += yumgroup['default']
+ if ptype in ['optional', 'all']:
+ packages += yumgroup['optional']
+ return packages
+
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
index f112c65cd..c3eadc6bb 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
@@ -310,20 +310,22 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
"""
if self.disableResolver:
# Config requests no resolver
+ for struct in structures:
+ for pkg in struct.xpath('//Package | //BoundPackage'):
+ if pkg.get("group"):
+ if pkg.get("type"):
+ pkg.set("choose", pkg.get("type"))
return
if collection is None:
collection = self.get_collection(metadata)
- # base is the set of initial packages -- explicitly
- # given in the specification, from expanded package groups,
- # and essential to the distribution
- base = set()
+ initial = set()
to_remove = []
groups = []
for struct in structures:
for pkg in struct.xpath('//Package | //BoundPackage'):
if pkg.get("name"):
- base.update(collection.packages_from_entry(pkg))
+ initial.update(collection.packages_from_entry(pkg))
elif pkg.get("group"):
groups.append((pkg.get("group"),
pkg.get("type")))
@@ -335,6 +337,11 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
pkg,
xml_declaration=False).decode('UTF-8'))
+ # base is the set of initial packages explicitly given in the
+ # specification, packages from expanded package groups, and
+ # packages essential to the distribution
+ base = set(initial)
+
# remove package groups
for el in to_remove:
el.getparent().remove(el)
@@ -350,7 +357,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
if unknown:
self.logger.info("Packages: Got %d unknown entries" % len(unknown))
self.logger.info("Packages: %s" % list(unknown))
- newpkgs = collection.get_new_packages(base, packages)
+ newpkgs = collection.get_new_packages(initial, packages)
self.debug_log("Packages: %d base, %d complete, %d new" %
(len(base), len(packages), len(newpkgs)))
newpkgs.sort()
diff --git a/src/lib/Bcfg2/Server/models.py b/src/lib/Bcfg2/Server/models.py
index 0328c6bea..1f64111e7 100644
--- a/src/lib/Bcfg2/Server/models.py
+++ b/src/lib/Bcfg2/Server/models.py
@@ -1,6 +1,7 @@
""" Django database models for all plugins """
import sys
+import copy
import logging
import Bcfg2.Options
import Bcfg2.Server.Plugins
@@ -19,7 +20,7 @@ def load_models(plugins=None, cfile='/etc/bcfg2.conf', quiet=True):
# we want to provide a different default plugin list --
# namely, _all_ plugins, so that the database is guaranteed to
# work, even if /etc/bcfg2.conf isn't set up properly
- plugin_opt = Bcfg2.Options.SERVER_PLUGINS
+ plugin_opt = copy.deepcopy(Bcfg2.Options.SERVER_PLUGINS)
plugin_opt.default = Bcfg2.Server.Plugins.__all__
setup = \
diff --git a/src/lib/Bcfg2/version.py b/src/lib/Bcfg2/version.py
index 8223d7543..6f3ba3e49 100644
--- a/src/lib/Bcfg2/version.py
+++ b/src/lib/Bcfg2/version.py
@@ -2,7 +2,7 @@
import re
-__version__ = "1.3.0rc2"
+__version__ = "1.3.1"
class Bcfg2VersionInfo(tuple):
diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports
index 9f2ff96c2..2c4a918be 100755
--- a/src/sbin/bcfg2-reports
+++ b/src/sbin/bcfg2-reports
@@ -10,7 +10,7 @@ from Bcfg2.Compat import ConfigParser
try:
import Bcfg2.settings
except ConfigParser.NoSectionError:
- print("Your bcfg2.conf is currently missing the statistics section which "
+ print("Your bcfg2.conf is currently missing the [database] section which "
"is necessary for the reporting interface. Please see bcfg2.conf(5) "
"for more details.")
sys.exit(1)
@@ -121,7 +121,7 @@ def main():
help="Show hosts that haven't run in the last 24 "
"hours")
parser.add_option_group(allhostmodes)
-
+
# entry modes
entrymodes = \
OptionGroup(parser, "Entry Modes",
@@ -166,7 +166,7 @@ def main():
(mode.get_opt_string(), opt.get_opt_string()))
mode = opt
mode_family = parser.get_option_group(opt.get_opt_string())
-
+
# you can specify more than one of --bad, --extra, --modified, --show, so
# consider single-host options separately
if not mode_family:
@@ -174,7 +174,7 @@ def main():
if getattr(options, opt.dest):
mode_family = parser.get_option_group(opt.get_opt_string())
break
-
+
if not mode_family:
parser.error("You must specify a mode")
@@ -243,7 +243,7 @@ def main():
parser.error("%s require either a list of entries on the "
"command line or the --file options" %
mode_family.title)
-
+
if options.badentry:
result = hosts_by_entry_type(clients, "bad", entries)
elif options.modifiedentry:
@@ -263,7 +263,7 @@ def main():
# todo batch fetch this. sqlite could break
for client in clients:
- ents = entry_cls.objects.filter(name=entries[0][1],
+ ents = entry_cls.objects.filter(name=entries[0][1],
interaction=client.current_interaction)
if len(ents) == 0:
continue
diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test
index 4a57fa42f..6eaf0cc33 100755
--- a/src/sbin/bcfg2-test
+++ b/src/sbin/bcfg2-test
@@ -128,7 +128,14 @@ class ClientTest(TestCase):
"\n".join(output + ["Configuration is missing bundle(s): %s" %
':'.join(missing)])
- # check for render failures
+ # check for unknown packages
+ unknown_pkgs = [el.get("name")
+ for el in config.xpath('//Package[@type="unknown"]')
+ if not self.ignore_entry(el.tag, el.get("name"))]
+ assert len(unknown_pkgs) == 0, \
+ "Configuration contains unknown packages: %s" % \
+ ", ".join(unknown_pkgs)
+
failures = []
msg = output + ["Failures:"]
for failure in config.xpath('//*[@failure]'):
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/test_code_checks.py b/testsuite/Testsrc/test_code_checks.py
index 7d168af75..85aea29d6 100644
--- a/testsuite/Testsrc/test_code_checks.py
+++ b/testsuite/Testsrc/test_code_checks.py
@@ -170,10 +170,14 @@ class CodeTestCase(Bcfg2TestCase):
return self.has_command
def get_env(self):
- env = copy.copy(os.environ)
- env['PYTHONPATH'] = ':'.join([env.get("PYTHONPATH", ""),
- testdir])
- return env
+ if ('PYTHONPATH' not in os.environ or
+ testdir not in os.environ['PYTHONPATH'].split(":")):
+ env = copy.copy(os.environ)
+ env['PYTHONPATH'] = ':'.join([env.get("PYTHONPATH", ""),
+ testdir])
+ return env
+ else:
+ return os.environ
def _test_full(self, files, extra_args=None):
""" test select files for all problems """
diff --git a/testsuite/install.sh b/testsuite/install.sh
index 2c962e171..c1685f831 100755
--- a/testsuite/install.sh
+++ b/testsuite/install.sh
@@ -4,7 +4,7 @@
pip install -r testsuite/requirements.txt --use-mirrors
-PYVER=$(python -c 'import sys;print ".".join(str(v) for v in sys.version_info[0:2])')
+PYVER=$(python -c 'import sys;print(".".join(str(v) for v in sys.version_info[0:2]))')
if [[ "$WITH_OPTIONAL_DEPS" == "yes" ]]; then
if [[ $PYVER == "2.5" ]]; then
diff --git a/tools/bcfg2-profile-templates.py b/tools/bcfg2-profile-templates.py
index cc7a1a3d8..3cd3786f9 100755
--- a/tools/bcfg2-profile-templates.py
+++ b/tools/bcfg2-profile-templates.py
@@ -4,7 +4,6 @@
import os
import sys
import time
-import signal
import logging
import operator
import Bcfg2.Logger
@@ -13,19 +12,6 @@ import Bcfg2.Server.Core
LOGGER = None
-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(client=Bcfg2.Options.Option("Benchmark templates for one client",
@@ -53,7 +39,6 @@ def main():
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(0.1)
logger.debug("Repository events processed")
diff --git a/tools/export.py b/tools/export.py
index 33c42d238..716c831d9 100755
--- a/tools/export.py
+++ b/tools/export.py
@@ -164,11 +164,17 @@ E.G. 1.2.0pre1 is a valid version.
print(help_message)
quit()
- rpmchangelog = ["* %s %s <%s> %s-0.0%s\n" %
- (datetime.datetime.now().strftime("%a %b %d %Y"),
- name, email,
- version_release, version_info['build']),
- "- New upstream release\n", "\n"]
+ if version_info['build'] == '':
+ rpmchangelog = ["* %s %s <%s> %s-1\n" %
+ (datetime.datetime.now().strftime("%a %b %d %Y"),
+ name, email, version_release),
+ "- New upstream release\n", "\n"]
+ else:
+ rpmchangelog = ["* %s %s <%s> %s-0.%s.%s\n" %
+ (datetime.datetime.now().strftime("%a %b %d %Y"),
+ name, email, version_release,
+ version_info['build'][-1], version_info['build']),
+ "- New upstream release\n", "\n"]
# write out the new RPM changelog
specs = ["misc/bcfg2.spec", "misc/bcfg2-selinux.spec", "redhat/bcfg2.spec.in"]
@@ -236,12 +242,22 @@ E.G. 1.2.0pre1 is a valid version.
find_and_replace('misc/bcfg2-selinux.spec', 'Version:',
'Version: %s\n' % version_release,
dryrun=options.dryrun)
- find_and_replace('misc/bcfg2.spec', 'Release: ',
- 'Release: 0.0%s\n' % version_info['build'],
- dryrun=options.dryrun)
- find_and_replace('misc/bcfg2-selinux.spec', 'Release: ',
- 'Release: 0.0%s\n' % version_info['build'],
- dryrun=options.dryrun)
+ if version_info['build'] == '':
+ find_and_replace('misc/bcfg2.spec', 'Release: ',
+ 'Release: 1\n',
+ dryrun=options.dryrun)
+ find_and_replace('misc/bcfg2-selinux.spec', 'Release: ',
+ 'Release: 1\n',
+ dryrun=options.dryrun)
+ else:
+ find_and_replace('misc/bcfg2.spec', 'Release: ',
+ 'Release: 0.%s.%s\n' %
+ (version_info['build'][-1], version_info['build']),
+ dryrun=options.dryrun)
+ find_and_replace('misc/bcfg2-selinux.spec', 'Release: ',
+ 'Release: 0.%s.%s\n' %
+ (version_info['build'][-1], version_info['build']),
+ dryrun=options.dryrun)
find_and_replace('misc/bcfg2.spec', '%setup',
'%%setup -q -n %%{name}-%%{version}%s\n' %
version_info['build'],
diff --git a/tools/upgrade/1.3/migrate_info.py b/tools/upgrade/1.3/migrate_info.py
index 5ff4b73b7..e72599daf 100755
--- a/tools/upgrade/1.3/migrate_info.py
+++ b/tools/upgrade/1.3/migrate_info.py
@@ -4,7 +4,8 @@ import os
import sys
import lxml.etree
import Bcfg2.Options
-from Bcfg2.Server.Plugin import info_regex
+from Bcfg2.Server.Plugin import INFO_REGEX
+
def convert(info_file):
info_xml = os.path.join(os.path.dirname(info_file), "info.xml")
@@ -15,7 +16,7 @@ def convert(info_file):
fileinfo = lxml.etree.Element("FileInfo")
info = lxml.etree.SubElement(fileinfo, "Info")
for line in open(info_file).readlines():
- match = info_regex.match(line)
+ match = INFO_REGEX.match(line)
if match:
mgd = match.groupdict()
for key, value in list(mgd.items()):
@@ -25,6 +26,7 @@ def convert(info_file):
open(info_xml, "w").write(lxml.etree.tostring(fileinfo, pretty_print=True))
os.unlink(info_file)
+
def main():
opts = dict(repo=Bcfg2.Options.SERVER_REPOSITORY,
configfile=Bcfg2.Options.CFILE,
diff --git a/tools/upgrade/1.3/migrate_perms_to_mode.py b/tools/upgrade/1.3/migrate_perms_to_mode.py
index 0aa9c574c..e061558d3 100644..100755
--- a/tools/upgrade/1.3/migrate_perms_to_mode.py
+++ b/tools/upgrade/1.3/migrate_perms_to_mode.py
@@ -24,7 +24,12 @@ def writefile(f, xdata):
def convertinfo(ifile):
"""Do perms -> mode conversion for info.xml files."""
- xdata = lxml.etree.parse(ifile)
+ try:
+ xdata = lxml.etree.parse(ifile)
+ except lxml.etree.XMLSyntaxError:
+ err = sys.exc_info()[1]
+ print("Could not parse %s, skipping: %s" % (ifile, err))
+ return
found = False
for i in xdata.findall('//Info'):
found = setmodeattr(i)
@@ -34,11 +39,14 @@ def convertinfo(ifile):
def convertstructure(structfile):
"""Do perms -> mode conversion for structure files."""
- xdata = lxml.etree.parse(structfile)
+ try:
+ xdata = lxml.etree.parse(structfile)
+ except lxml.etree.XMLSyntaxError:
+ err = sys.exc_info()[1]
+ print("Could not parse %s, skipping: %s" % (structfile, err))
+ return
found = False
- for path in xdata.findall('//BoundPath'):
- found = setmodeattr(path)
- for path in xdata.findall('//Path'):
+ for path in xdata.xpath('//BoundPath|//Path'):
found = setmodeattr(path)
if found:
writefile(structfile, xdata)
@@ -57,7 +65,7 @@ def main():
for root, dirs, files in os.walk(os.path.join(repo, plugin)):
for fname in files:
convertstructure(os.path.join(root, fname))
- if plugin not in ['Cfg', 'TGenshi', 'TCheetah']:
+ if plugin not in ['Cfg', 'TGenshi', 'TCheetah', 'SSHbase', 'SSLCA']:
continue
for root, dirs, files in os.walk(os.path.join(repo, plugin)):
for fname in files: