summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debian/bcfg2-doc.links1
-rw-r--r--debian/control13
-rwxr-xr-xdebian/rules11
-rw-r--r--doc/_templates/indexsidebar.html11
-rw-r--r--doc/conf.py4
-rw-r--r--doc/server/plugins/connectors/templatehelper.txt6
-rw-r--r--doc/server/plugins/generators/packages.txt15
-rw-r--r--gentoo/bcfg2-1.2.2.ebuild43
-rw-r--r--misc/bcfg2.spec116
-rw-r--r--redhat/bcfg2.spec.in1
-rw-r--r--schemas/bundle.xsd3
-rw-r--r--schemas/clients.xsd8
-rw-r--r--schemas/grouppatterns.xsd6
-rw-r--r--schemas/packages.xsd18
-rw-r--r--schemas/pkgtype.xsd3
-rwxr-xr-xsetup.py82
-rw-r--r--src/lib/Bcfg2/Bcfg2Py3Incompat.py2
-rw-r--r--src/lib/Bcfg2/Bcfg2Py3k.py11
-rw-r--r--src/lib/Bcfg2/Component.py4
-rw-r--r--src/lib/Bcfg2/Options.py10
-rw-r--r--src/lib/Bcfg2/Server/Core.py6
-rw-r--r--src/lib/Bcfg2/Server/Plugin.py37
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py21
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py14
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TGenshi.py10
-rw-r--r--src/lib/Bcfg2/Server/Reports/settings.py8
-rwxr-xr-xsrc/sbin/bcfg2-info43
-rwxr-xr-x[-rw-r--r--]src/sbin/bcfg2-test104
-rw-r--r--testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py53
29 files changed, 393 insertions, 271 deletions
diff --git a/debian/bcfg2-doc.links b/debian/bcfg2-doc.links
new file mode 100644
index 000000000..133a58e2e
--- /dev/null
+++ b/debian/bcfg2-doc.links
@@ -0,0 +1 @@
+usr/share/doc/bcfg2/html/_sources usr/share/doc/bcfg2/rst
diff --git a/debian/control b/debian/control
index 63137dabf..7835334da 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: admin
Priority: optional
Maintainer: Arto Jantunen <viiru@debian.org>
Uploaders: Sami Haahtinen <ressu@debian.org>
-Build-Depends: debhelper (>= 7.0.50~), python (>= 2.3.5-7), python-setuptools
+Build-Depends: debhelper (>= 7.0.50~), python (>= 2.3.5-7), python-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx
Build-Depends-Indep: python-support (>= 0.5.3)
Standards-Version: 3.8.0.0
XS-Python-Version: >= 2.3
@@ -24,7 +24,7 @@ Architecture: all
Depends: ${python:Depends}, ${misc:Depends}, python-lxml (>= 0.9), libxml2-utils (>= 2.6.23), lsb-base (>= 3.1-9), ucf, bcfg2 (= ${binary:Version}), openssl, python-ssl | python2.6 | python3.0 | python3.1 | python3.2, python-gamin
XB-Python-Version: >= 2.4
Recommends: graphviz, patch
-Suggests: python-cheetah, python-genshi (>= 0.4.4), python-profiler, python-sqlalchemy (>= 0.5.0), python-django, mail-transport-agent
+Suggests: python-cheetah, python-genshi (>= 0.4.4), python-profiler, python-sqlalchemy (>= 0.5.0), python-django, mail-transport-agent, bcfg2-doc (= ${binary:Version})
Description: Configuration management server
Bcfg2 is a configuration management system that generates configuration sets
for clients bound by client profiles.
@@ -40,3 +40,12 @@ Description: Configuration management web interface
Bcfg2 is a configuration management system that generates configuration sets
for clients bound by client profiles.
bcfg2-web is the reporting server for bcfg2.
+
+Package: bcfg2-doc
+Architecture: all
+Depends: ${sphinxdoc:Depends}, ${misc:Depends}
+XB-Python-Version: >= 2.4
+Description: Configuration management system documentation
+ Bcfg2 is a configuration management system that generates configuration sets
+ for clients bound by client profiles.
+ bcfg2-doc is the documentation for bcfg2.
diff --git a/debian/rules b/debian/rules
index 1638b8415..30fd64a43 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,9 +1,7 @@
#!/usr/bin/make -f
-WSGI_LOC = $(shell find debian/bcfg2-server/ -name reports.wsgi | perl -p -e 's|debian/bcfg2-server||')
-
%:
- dh --with python-support $@
+ dh $@ --with python-support,sphinxdoc
override_dh_auto_install:
# Make the build destination dir consistent between pre-7.3 and 7.3 and
@@ -18,3 +16,10 @@ override_dh_installinit:
# Install bcfg2-server initscript without starting it on postinst
dh_installinit --package=bcfg2-server --no-start
+override_dh_installdocs:
+ python setup.py build_sphinx
+ dh_installdocs build/sphinx/html
+
+override_dh_auto_clean:
+ dh_auto_clean
+ rm -rf build
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
new file mode 100644
index 000000000..39916315d
--- /dev/null
+++ b/doc/_templates/indexsidebar.html
@@ -0,0 +1,11 @@
+<!-- FIXME: Add download page with pdf/html/txt archives of these documents
+ <h3>Download</h3>
+ <p><a href="{{ pathto('download') }}">Download these documents</a></p>
+-->
+
+ <h3>Docs for other versions</h3>
+ <ul>
+ <li><a href="http://docs.bcfg2.org/1.1/">Bcfg2 1.1 (stable)</a></li>
+ <li><a href="http://docs.bcfg2.org/1.2/">Bcfg2 1.2 (stable)</a></li>
+ <li><a href="http://docs.bcfg2.org/dev/">Bcfg2 development documentation</a></li>
+ </ul>
diff --git a/doc/conf.py b/doc/conf.py
index 2d5be4e9e..5903b009a 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -141,7 +141,9 @@ html_last_updated_fmt = '%b %d, %Y'
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+html_sidebars = {
+ 'index': 'indexsidebar.html'
+}
# Additional templates that should be rendered to pages, maps page names to
# template names.
diff --git a/doc/server/plugins/connectors/templatehelper.txt b/doc/server/plugins/connectors/templatehelper.txt
index 67219109f..24d7f18b5 100644
--- a/doc/server/plugins/connectors/templatehelper.txt
+++ b/doc/server/plugins/connectors/templatehelper.txt
@@ -20,13 +20,15 @@ Restart ``bcfg2-server``.
Now, any ``.py`` file placed in ``/var/lib/bcfg2/TemplateHelper/``
will be read and added to matching client metadata objects. See
-:ref:`Writing Helpers` below for more information on how to write
-TemplateHelper scripts.
+:ref:`writing-templatehelpers` below for more information on how to
+write TemplateHelper scripts.
TemplateHelper supports group- and host-specific helpers, so you could
create, e.g., ``foo.py.G80_test`` to create a helper that only applied
to machines in the group ``test``.
+.. _writing-templatehelpers:
+
Writing Helpers
===============
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index 2bc3dab31..855a3c51c 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -51,7 +51,7 @@ member clients.
| Yum | yum | |
+--------+----------+--------------+
-.. note::
+.. note::
.. versionadded:: 1.2.0
@@ -574,7 +574,7 @@ You can also view the sources applicable to a client::
Type: yum
URL: http://mirror.example.com/centos-6-x86_64-updates
GPG Key(s): http://mirror.example.com/centos-6-x86_64-updates/RPM-GPG-KEY-CentOS-6
-
+
Name: centos-6-x86_64-os
Type: yum
URL: http://mirror.example.com/centos-6-x86_64-os
@@ -671,17 +671,6 @@ It understands the following directives:
"/etc/pki/rpm-gpg".
* ``version``: Set the version attribute used when binding
Packages. Default is ``auto``.
-* ``import_gpg_keys``: The RPM release of an RPM GPG key cannot be
- reliably and automatically determined without importing the key into
- the server's key chain. If ``import_gpg_keys`` is "false" (the
- default), the release of automatically-generated RPM GPG key entries
- in the specification will be set to "any", which disables
- verification of the release. (Version will still be verified.) In
- practice, this is unlikely to be an issue, as the RPM version of a
- GPG key is the key's fingerprint, and collisions are rare. If you
- do encounter a GPG key version collision, you will need to set this
- to "true", whereupon Packages will import the keys into the server's
- key chain. Python RPM libraries must be installed for this to work.
[yum] section
-------------
diff --git a/gentoo/bcfg2-1.2.2.ebuild b/gentoo/bcfg2-1.2.2.ebuild
index 0ac428bef..c054446fe 100644
--- a/gentoo/bcfg2-1.2.2.ebuild
+++ b/gentoo/bcfg2-1.2.2.ebuild
@@ -1,6 +1,6 @@
# Copyright 1999-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
-# $Header: $
+# $Header: /var/cvsroot/gentoo-x86/app-admin/bcfg2/bcfg2-1.2.0.ebuild,v 1.1 2011/12/28 07:56:20 xmw Exp $
EAPI="3"
PYTHON_DEPEND="2:2.6"
@@ -12,20 +12,19 @@ inherit distutils
DESCRIPTION="configuration management tool"
HOMEPAGE="http://bcfg2.org"
-
-# handle the "pre" case
-MY_P="${P/_/}"
-SRC_URI="ftp://ftp.mcs.anl.gov/pub/bcfg/${MY_P}.tar.gz"
-S="${WORKDIR}/${MY_P}"
+SRC_URI="ftp://ftp.mcs.anl.gov/pub/bcfg/${P}.tar.gz"
LICENSE="BSD"
SLOT="0"
KEYWORDS="~amd64 ~x86 ~amd64-linux ~x86-linux ~x64-solaris"
-IUSE="server"
+IUSE="doc genshi server"
-DEPEND="dev-python/setuptools"
+DEPEND="dev-python/setuptools
+ doc? ( dev-python/sphinx )"
RDEPEND="app-portage/gentoolkit
+ genshi? ( dev-python/genshi )
server? (
+ virtual/fam
dev-python/lxml
dev-libs/libgamin[python] )"
@@ -37,19 +36,37 @@ distutils_src_install_post_hook() {
fi
}
+src_compile() {
+ distutils_src_compile
+
+ if use doc; then
+ einfo "Building Bcfg2 documentation"
+ PYTHONPATH="build-$(PYTHON -f --ABI)" \
+ sphinx-build doc doc_output || die
+ fi
+}
+
src_install() {
distutils_src_install --record=PY_SERVER_LIBS --install-scripts "${EPREFIX}/usr/sbin"
if ! use server; then
- # Remove files only necessary for a server installation
- rm -rf "${ED}usr/share/bcfg2"
- rm -rf "${ED}usr/share/man/man8"
+ # Remove files only necessary for a server installation
+ rm -rf "${ED}usr/share/bcfg2" || die
+ rm -rf "${ED}usr/share/man/man8" || die
else
- newinitd "${FILESDIR}/bcfg2-server.rc" bcfg2-server
+ newinitd "${FILESDIR}/${PN}-server-1.2.0.rc" bcfg2-server
fi
insinto /etc
- doins examples/bcfg2.conf || die "doins failed"
+ doins examples/bcfg2.conf
+
+ if use doc; then
+ # install the sphinx documentation
+ pushd doc_output > /dev/null
+ insinto /usr/share/doc/${PF}/html
+ doins -r [a-z]* _images _static || die "Failed to install documentation"
+ popd > /dev/null
+ fi
}
pkg_postinst () {
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index 9fa119edf..866f2f6ba 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -1,4 +1,4 @@
-%define release 0.1
+%define release 0.2
%define __python python
%{!?py_ver: %define py_ver %(%{__python} -c 'import sys;print(sys.version[0:3])')}
%define pythonversion %{py_ver}
@@ -51,14 +51,15 @@ BuildRequires: python-sphinx10
BuildRequires: python-sphinx >= 0.6
%endif
+Requires: python-nose
Requires: python-lxml >= 0.9
%if 0%{?rhel_version}
# the debian init script needs redhat-lsb.
# iff we switch to the redhat one, this might not be needed anymore.
Requires: redhat-lsb
%endif
-%if 0%{?fedora} == 0
-# fedora 15 and 16 (and possibly other distros) do not know this tag.
+%if "%{_vendor}" != "redhat"
+# fedora and rhel (and possibly other distros) do not know this tag.
Recommends: cron
%endif
@@ -90,7 +91,7 @@ deployment strategies.
This package includes the Bcfg2 client software.
-%package -n bcfg2-server
+%package server
Version: 1.2.2
Summary: Bcfg2 Server
%if 0%{?suse_version}
@@ -106,8 +107,10 @@ Requires: python-lxml >= 1.2.1
%if "%{_vendor}" == "redhat"
Requires: gamin-python
%endif
+Requires: /usr/sbin/sendmail
+Requires: /usr/bin/openssl
-%description -n bcfg2-server
+%description server
Bcfg2 helps system administrators produce a consistent, reproducible,
and verifiable description of their environment, and offers
visualization and reporting tools to aid in day-to-day administrative
@@ -135,7 +138,7 @@ deployment strategies.
This package includes the Bcfg2 server software.
-%package -n bcfg2-doc
+%package doc
Summary: Configuration management system documentation
%if 0%{?suse_version}
Group: Documentation/HTML
@@ -143,7 +146,7 @@ Group: Documentation/HTML
Group: Documentation
%endif
-%description -n bcfg2-doc
+%description doc
Bcfg2 helps system administrators produce a consistent, reproducible,
and verifiable description of their environment, and offers
visualization and reporting tools to aid in day-to-day administrative
@@ -171,7 +174,7 @@ deployment strategies.
This package includes the Bcfg2 documentation.
-%package -n bcfg2-web
+%package web
Version: 1.2.2
Summary: Bcfg2 Web Reporting Interface
%if 0%{?suse_version}
@@ -189,7 +192,7 @@ Requires: apache2-mod_wsgi
%define apache_conf %{_sysconfdir}/apache2
%endif
-%description -n bcfg2-web
+%description web
Bcfg2 helps system administrators produce a consistent, reproducible,
and verifiable description of their environment, and offers
visualization and reporting tools to aid in day-to-day administrative
@@ -218,7 +221,7 @@ deployment strategies.
This package includes the Bcfg2 reports web frontend.
%prep
-%setup -q -n bcfg2-%{version}
+%setup -q -n %{name}-%{version}
%build
%{__python}%{pythonversion} setup.py build
@@ -228,6 +231,7 @@ This package includes the Bcfg2 reports web frontend.
%{__python}%{pythonversion} setup.py build_sphinx
%install
+rm -rf %{buildroot}
%{__python}%{pythonversion} setup.py install --root=%{buildroot} --record=INSTALLED_FILES --prefix=/usr
%{__install} -d %{buildroot}%{_bindir}
%{__install} -d %{buildroot}%{_sbindir}
@@ -241,7 +245,7 @@ mkdir -p %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}
%{__install} -d %{buildroot}/var/adm/fillup-templates
%endif
-%{__mv} %{buildroot}/usr/bin/bcfg2* %{buildroot}%{_sbindir}
+%{__mv} %{buildroot}%{_bindir}/bcfg2* %{buildroot}%{_sbindir}
%{__install} -m 755 debian/bcfg2.init %{buildroot}%{_initrddir}/bcfg2
%{__install} -m 755 debian/bcfg2-server.init %{buildroot}%{_initrddir}/bcfg2-server
%{__install} -m 755 debian/bcfg2.default %{buildroot}%{_sysconfdir}/default/bcfg2
@@ -267,7 +271,7 @@ mv build/dtd %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}/
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} || exit 2
-%files -n bcfg2
+%files
%defattr(-,root,root,-)
%{_sbindir}/bcfg2
%dir %{python_sitelib}/Bcfg2
@@ -291,47 +295,7 @@ mv build/dtd %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}/
%ghost %attr(0600,root,root) %{_sysconfdir}/bcfg2.conf
%endif
-%post -n bcfg2-server
-# enable daemon on first install only (not on update).
-if [ $1 -eq 1 ]; then
-%if 0%{?suse_version}
- %fillup_and_insserv -f bcfg2-server
-%else
- /sbin/chkconfig --add bcfg2-server
-%endif
-fi
-
-%preun -n bcfg2
-%if 0%{?suse_version}
-# stop on removal (not on update).
-if [ $1 -eq 0 ]; then
- %stop_on_removal bcfg2
-fi
-%endif
-
-%preun -n bcfg2-server
-%if 0%{?suse_version}
-if [ $1 -eq 0 ]; then
- %stop_on_removal bcfg2-server
-fi
-%endif
-
-%postun -n bcfg2
-%if 0%{?suse_version}
-if [ $1 -eq 0 ]; then
- %insserv_cleanup
-fi
-%endif
-
-%postun -n bcfg2-server
-%if 0%{?suse_version}
-if [ $1 -eq 0 ]; then
- # clean up on removal.
- %insserv_cleanup
-fi
-%endif
-
-%files -n bcfg2-server
+%files server
%defattr(-,root,root,-)
%{_initrddir}/bcfg2-server
%dir %{python_sitelib}/Bcfg2
@@ -368,11 +332,11 @@ fi
%ghost %attr(0600,root,root) %{_sysconfdir}/bcfg2.conf
%endif
-%files -n bcfg2-doc
+%files doc
%defattr(-,root,root,-)
%doc %{_defaultdocdir}/bcfg2-doc-%{version}
-%files -n bcfg2-web
+%files web
%defattr(-,root,root,-)
%{_datadir}/bcfg2/reports.wsgi
%{_datadir}/bcfg2/site_media
@@ -383,6 +347,46 @@ fi
%ghost %attr(0600,root,root) %{_sysconfdir}/bcfg2-web.conf
%endif
+%post server
+# enable daemon on first install only (not on update).
+if [ $1 -eq 1 ]; then
+%if 0%{?suse_version}
+ %fillup_and_insserv -f bcfg2-server
+%else
+ /sbin/chkconfig --add bcfg2-server
+%endif
+fi
+
+%preun
+%if 0%{?suse_version}
+# stop on removal (not on update).
+if [ $1 -eq 0 ]; then
+ %stop_on_removal bcfg2
+fi
+%endif
+
+%preun server
+%if 0%{?suse_version}
+if [ $1 -eq 0 ]; then
+ %stop_on_removal bcfg2-server
+fi
+%endif
+
+%postun
+%if 0%{?suse_version}
+if [ $1 -eq 0 ]; then
+ %insserv_cleanup
+fi
+%endif
+
+%postun server
+%if 0%{?suse_version}
+if [ $1 -eq 0 ]; then
+ # clean up on removal.
+ %insserv_cleanup
+fi
+%endif
+
%changelog
* Sat Feb 18 2012 Christopher 'm4z' Holm <686f6c6d@googlemail.com> 1.2.1
- Added Fedora and Mandriva compatibilty (for Open Build Service).
@@ -396,7 +400,7 @@ fi
- Added -doc sub-package
* Mon Jun 21 2010 Fabian Affolter <fabian@bernewireless.net> - 1.1.0rc3-0.1
-- Changed source0 in order that it works with spectool
+- Changed source0 in order that it works with spectool
* Fri Feb 2 2007 Mike Brady <mike.brady@devnull.net.nz> 0.9.1
- Removed use of _libdir due to Red Hat x86_64 issue.
diff --git a/redhat/bcfg2.spec.in b/redhat/bcfg2.spec.in
index 881296533..64adbe5c2 100644
--- a/redhat/bcfg2.spec.in
+++ b/redhat/bcfg2.spec.in
@@ -44,6 +44,7 @@ Requires: python-elementtree
%else if "%{py_ver}" < "2.5"
Requires: python-lxml
%endif
+Requires: python-nose
Requires: initscripts
Requires(post): /sbin/chkconfig
Requires(preun): /sbin/chkconfig
diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd
index 1e67e1f3a..4e034ee3c 100644
--- a/schemas/bundle.xsd
+++ b/schemas/bundle.xsd
@@ -1,6 +1,6 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
-
+
<xsd:annotation>
<xsd:documentation>
bundle schema for bcfg2
@@ -237,6 +237,7 @@
<xsd:attribute type='xsd:string' name='origin'/>
<xsd:attribute type='xsd:string' name='revision'/>
<xsd:attributeGroup ref="py:genshiAttrs"/>
+ <xsd:attribute ref="xml:base"/>
</xsd:complexType>
<xsd:element name='Bundle' type='BundleType'>
diff --git a/schemas/clients.xsd b/schemas/clients.xsd
index 9e8d5a22a..56f458a45 100644
--- a/schemas/clients.xsd
+++ b/schemas/clients.xsd
@@ -1,5 +1,5 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
-
+
<xsd:annotation>
<xsd:documentation>
client schema for bcfg2
@@ -7,12 +7,15 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="xml.xsd"/>
+
<xsd:complexType name='ClientType'>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Alias'>
<xsd:complexType>
<xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='address'/>
+ <xsd:attribute type='xsd:string' name='address'/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -34,6 +37,7 @@
<xsd:element name='Clients' type='ClientsType'/>
</xsd:choice>
<xsd:attribute name='version' type='xsd:string'/>
+ <xsd:attribute ref="xml:base"/>
</xsd:complexType>
<xsd:element name='Clients' type='ClientsType'/>
diff --git a/schemas/grouppatterns.xsd b/schemas/grouppatterns.xsd
index f2bdceccd..6c63b8694 100644
--- a/schemas/grouppatterns.xsd
+++ b/schemas/grouppatterns.xsd
@@ -1,5 +1,5 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
-
+
<xsd:annotation>
<xsd:documentation>
group patterns config schema for bcfg2
@@ -7,6 +7,9 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="xml.xsd"/>
+
<xsd:complexType name="PatternType">
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="NameRange" type="xsd:string"/>
@@ -21,6 +24,7 @@
<xsd:element name="GroupPattern" type="PatternType"/>
<xsd:element name="GroupPatterns" type="GroupPatternsType"/>
</xsd:choice>
+ <xsd:attribute ref="xml:base"/>
</xsd:complexType>
<xsd:element name="GroupPatterns" type="GroupPatternsType"/>
diff --git a/schemas/packages.xsd b/schemas/packages.xsd
index e09dcf8b5..c29a85ecf 100644
--- a/schemas/packages.xsd
+++ b/schemas/packages.xsd
@@ -1,5 +1,5 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
-
+
<xsd:annotation>
<xsd:documentation>
packages config schema for bcfg2
@@ -7,6 +7,9 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="xml.xsd"/>
+
<xsd:simpleType name="sourceTypeEnum">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="yum"/>
@@ -45,12 +48,13 @@
</xsd:complexType>
<xsd:complexType name="sourcesType">
- <xsd:choice minOccurs="1" maxOccurs="unbounded">
- <xsd:element name="Group" type="groupType"/>
- <xsd:element name="Client" type="groupType"/>
- <xsd:element name="Source" type="sourceType"/>
- <xsd:element name="Sources" type="sourcesType"/>
- </xsd:choice>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element name="Group" type="groupType"/>
+ <xsd:element name="Client" type="groupType"/>
+ <xsd:element name="Source" type="sourceType"/>
+ <xsd:element name="Sources" type="sourcesType"/>
+ </xsd:choice>
+ <xsd:attribute ref="xml:base"/>
</xsd:complexType>
<xsd:element name="Sources" type="sourcesType"/>
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index 9ddc0682e..0aaea0c22 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -50,7 +50,8 @@
<xsd:attribute type="xsd:string" name="srcs"/>
<xsd:attribute type="PackageTypeEnum" name="type"/>
<xsd:attribute type="xsd:string" name="bname"/>
- <xsd:attribute name="pkg_checks" type="xsd:string"/>
+ <xsd:attribute name="pkg_checks" type="xsd:boolean"/>
+ <xsd:attribute name="pkg_verify" type="xsd:boolean"/>
<xsd:attribute name="verify_flags" type="xsd:string"/>
<xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
diff --git a/setup.py b/setup.py
index f819ecd22..64dcdeb50 100755
--- a/setup.py
+++ b/setup.py
@@ -14,6 +14,7 @@ version = sys.version_info[:2]
if version < (2, 6):
need_m2crypto = True
+
class BuildDTDDoc (Command):
"""Build DTD documentation"""
@@ -35,8 +36,8 @@ class BuildDTDDoc (Command):
self.build_links = False
self.links_file = None
self.source_dir = None
- self.build_dir = None
- self.xslt = None
+ self.build_dir = None
+ self.xslt = None
def finalize_options(self):
"""Set final values for all the options that this command
@@ -71,7 +72,7 @@ class BuildDTDDoc (Command):
self.announce("Using XSLT file %s" % self.xslt)
self.ensure_filename('xslt')
- def run (self):
+ def run(self):
"""Perform XSLT transforms, writing output to self.build_dir"""
xslt = lxml.etree.parse(self.xslt).getroot()
@@ -81,18 +82,18 @@ class BuildDTDDoc (Command):
self.announce("Building linksFile %s" % self.links_file)
links_xml = \
lxml.etree.Element('links',
- attrib={'xmlns':"http://titanium.dstc.edu.au/xml/xs3p"})
+ attrib={'xmlns': "http://titanium.dstc.edu.au/xml/xs3p"})
for filename in glob(os.path.join(self.source_dir, '*.xsd')):
- attrib = {'file-location':os.path.basename(filename),
- 'docfile-location':os.path.splitext(os.path.basename(filename))[0] + ".html"}
+ attrib = {'file-location': os.path.basename(filename),
+ 'docfile-location': os.path.splitext(os.path.basename(filename))[0] + ".html"}
links_xml.append(lxml.etree.Element('schema', attrib=attrib))
open(os.path.join(self.source_dir, self.links_file),
"w").write(lxml.etree.tostring(links_xml))
# build parameter dict
- params = {'printLegend':"'false'",
- 'printGlossary':"'false'",
- 'sortByComponent':"'false'",}
+ params = {'printLegend': "'false'",
+ 'printGlossary': "'false'",
+ 'sortByComponent': "'false'"}
if self.links_file is not None:
params['linksFile'] = "'%s'" % self.links_file
params['searchIncludedSchemas'] = "'true'"
@@ -110,22 +111,12 @@ class BuildDTDDoc (Command):
cmdclass = {}
try:
- from sphinx.setup_command import BuildDoc
- cmdclass['build_sphinx'] = BuildDoc
-except ImportError:
- pass
-
-try:
import lxml.etree
cmdclass['build_dtddoc'] = BuildDTDDoc
except ImportError:
pass
-py3lib = 'src/lib/Bcfg2/Bcfg2Py3Incompat.py'
-if sys.hexversion < 0x03000000 and os.path.exists(py3lib):
- os.remove(py3lib)
-
-inst_reqs = ["lxml"]
+inst_reqs = ["lxml", "nose"]
if need_m2crypto:
inst_reqs.append("M2Crypto")
@@ -150,29 +141,30 @@ setup(cmdclass=cmdclass,
"Bcfg2.Server.Reports.reports.templatetags",
"Bcfg2.Server.Snapshots",
],
- install_requires = inst_reqs,
- package_dir = {'Bcfg2': 'src/lib/Bcfg2'},
- package_data = {'Bcfg2.Server.Reports.reports':['fixtures/*.xml',
- 'templates/*.html', 'templates/*/*.html',
- 'templates/*/*.inc' ] },
- scripts = glob('src/sbin/*'),
- data_files = [('share/bcfg2/schemas',
- glob('schemas/*.xsd')),
- ('share/bcfg2/xsl-transforms',
- glob('reports/xsl-transforms/*.xsl')),
- ('share/bcfg2/xsl-transforms/xsl-transform-includes',
- glob('reports/xsl-transforms/xsl-transform-includes/*.xsl')),
- ('share/bcfg2', glob('reports/reports.wsgi')),
- ('share/man/man1', glob("man/bcfg2.1")),
- ('share/man/man5', glob("man/*.5")),
- ('share/man/man8', glob("man/*.8")),
- ('share/bcfg2/Hostbase/templates',
- glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/*.*')),
- ('share/bcfg2/Hostbase/templates/hostbase',
- glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/*')),
- ('share/bcfg2/Hostbase/repo',
- glob('src/lib/Bcfg2/Server/Hostbase/templates/*')),
- ('share/bcfg2/site_media',
- glob('reports/site_media/*')),
- ]
+ install_requires=inst_reqs,
+ package_dir={'Bcfg2': 'src/lib/Bcfg2'},
+ package_data={'Bcfg2.Server.Reports.reports': ['fixtures/*.xml',
+ 'templates/*.html',
+ 'templates/*/*.html',
+ 'templates/*/*.inc']},
+ scripts=glob('src/sbin/*'),
+ data_files=[('share/bcfg2/schemas',
+ glob('schemas/*.xsd')),
+ ('share/bcfg2/xsl-transforms',
+ glob('reports/xsl-transforms/*.xsl')),
+ ('share/bcfg2/xsl-transforms/xsl-transform-includes',
+ glob('reports/xsl-transforms/xsl-transform-includes/*.xsl')),
+ ('share/bcfg2', glob('reports/reports.wsgi')),
+ ('share/man/man1', glob("man/bcfg2.1")),
+ ('share/man/man5', glob("man/*.5")),
+ ('share/man/man8', glob("man/*.8")),
+ ('share/bcfg2/Hostbase/templates',
+ glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/*.*')),
+ ('share/bcfg2/Hostbase/templates/hostbase',
+ glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/*')),
+ ('share/bcfg2/Hostbase/repo',
+ glob('src/lib/Bcfg2/Server/Hostbase/templates/*')),
+ ('share/bcfg2/site_media',
+ glob('reports/site_media/*')),
+ ]
)
diff --git a/src/lib/Bcfg2/Bcfg2Py3Incompat.py b/src/lib/Bcfg2/Bcfg2Py3Incompat.py
deleted file mode 100644
index 6b66e72b0..000000000
--- a/src/lib/Bcfg2/Bcfg2Py3Incompat.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def fprint(s, f):
- print(s, file=f)
diff --git a/src/lib/Bcfg2/Bcfg2Py3k.py b/src/lib/Bcfg2/Bcfg2Py3k.py
index ee05b7e41..6af8b3e5c 100644
--- a/src/lib/Bcfg2/Bcfg2Py3k.py
+++ b/src/lib/Bcfg2/Bcfg2Py3k.py
@@ -75,17 +75,6 @@ def u_str(string, encoding=None):
else:
return unicode(string)
-"""
-In order to use the new syntax for printing to a file, we need to do
-a conditional import because there is a syntax incompatibility between
-the two versions of python.
-"""
-if sys.hexversion >= 0x03000000:
- from Bcfg2.Bcfg2Py3Incompat import fprint
-else:
- def fprint(s, f):
- print >> f, s
-
if sys.hexversion >= 0x03000000:
from io import FileIO as file
else:
diff --git a/src/lib/Bcfg2/Component.py b/src/lib/Bcfg2/Component.py
index e21b5d359..eb9ea166a 100644
--- a/src/lib/Bcfg2/Component.py
+++ b/src/lib/Bcfg2/Component.py
@@ -14,7 +14,7 @@ import Bcfg2.Logger
from Bcfg2.Statistics import Statistics
from Bcfg2.SSLServer import XMLRPCServer
# Compatibility import
-from Bcfg2.Bcfg2Py3k import xmlrpclib, urlparse, fprint
+from Bcfg2.Bcfg2Py3k import xmlrpclib, urlparse
logger = logging.getLogger()
@@ -55,7 +55,7 @@ def run_component(component_cls, listen_all, location, daemon, pidfile_name,
os.chdir(os.sep)
pidfile = open(pidfile_name or "/dev/null", "w")
- fprint(os.getpid(), pidfile)
+ pidfile.write("%s\n" % os.getpid())
pidfile.close()
component = component_cls(cfile=cfile, **cls_kwargs)
diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py
index 6a6b83d61..f6273924a 100644
--- a/src/lib/Bcfg2/Options.py
+++ b/src/lib/Bcfg2/Options.py
@@ -3,6 +3,7 @@
import getopt
import os
import sys
+import shlex
import Bcfg2.Client.Tools
# Compatibility imports
from Bcfg2.Bcfg2Py3k import ConfigParser
@@ -353,6 +354,15 @@ CLIENT_TIMEOUT = Option('Set the client XML-RPC timeout', default=90,
cmd='-t', cf=('communication', 'timeout'),
odesc='<timeout>')
+# bcfg2-test options
+TEST_NOSEOPTS = Option('Options to pass to nosetests', default=[],
+ cmd='--nose-options', cf=('bcfg2_test', 'nose_options'),
+ odesc='<opts>', long_arg=True, cook=shlex.split)
+TEST_IGNORE = Option('Ignore these entries if they fail to build.', default=[],
+ cmd='--ignore',
+ cf=('bcfg2_test', 'ignore_entries'), long_arg=True,
+ odesc='<Type>:<name>,<Type>:<name>', cook=list_split)
+
# APT client tool options
CLIENT_APT_TOOLS_INSTALL_PATH = Option('Apt tools install path',
cf=('APT', 'install_path'),
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 3647ee622..112c484b1 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -6,6 +6,8 @@ import select
import sys
import threading
import time
+from traceback import format_exc
+
try:
import lxml.etree
except ImportError:
@@ -239,12 +241,12 @@ class Core(Component):
self.Bind(entry, metadata)
except PluginExecutionError, exc:
if 'failure' not in entry.attrib:
- entry.set('failure', 'bind error: %s' % exc)
+ entry.set('failure', 'bind error: %s' % format_exc())
logger.error("Failed to bind entry: %s %s" % \
(entry.tag, entry.get('name')))
except Exception, exc:
if 'failure' not in entry.attrib:
- entry.set('failure', 'bind error: %s' % exc)
+ entry.set('failure', 'bind error: %s' % format_exc())
logger.error("Unexpected failure in BindStructure: %s %s" \
% (entry.tag, entry.get('name')), exc_info=1)
diff --git a/src/lib/Bcfg2/Server/Plugin.py b/src/lib/Bcfg2/Server/Plugin.py
index 3cdfdbb41..11e6c5c20 100644
--- a/src/lib/Bcfg2/Server/Plugin.py
+++ b/src/lib/Bcfg2/Server/Plugin.py
@@ -592,6 +592,29 @@ class SingleXMLFileBacked(XMLFileBacked):
self.fam = fam
self.fam.AddMonitor(filename, self)
+ def _follow_xincludes(self, fname=None, xdata=None):
+ ''' follow xincludes, adding included files to fam and to
+ self.extras '''
+ if xdata is None:
+ if fname is None:
+ xdata = self.xdata.getroottree()
+ else:
+ xdata = lxml.etree.parse(fname)
+ included = [ent.get('href')
+ for ent in xdata.findall('//{http://www.w3.org/2001/XInclude}include')]
+ for name in included:
+ if name not in self.extras:
+ if name.startswith("/"):
+ fpath = name
+ else:
+ fpath = os.path.join(os.path.dirname(self.name), name)
+ self.add_monitor(fpath, name)
+ self._follow_xincludes(fname=fpath)
+
+ def add_monitor(self, fpath, fname):
+ self.fam.AddMonitor(fpath, self)
+ self.extras.append(fname)
+
def Index(self):
"""Build local data structures."""
try:
@@ -601,22 +624,14 @@ class SingleXMLFileBacked(XMLFileBacked):
logger.error("Failed to parse %s: %s" % (self.name, err))
raise Bcfg2.Server.Plugin.PluginInitError
- included = [ent.get('href')
- for ent in self.xdata.findall('./{http://www.w3.org/2001/XInclude}include')]
- if included:
- for name in included:
- if name not in self.extras:
- self.fam.AddMonitor(os.path.join(os.path.dirname(self.name),
- name),
- self)
- self.extras.append(name)
+ self._follow_xincludes()
+ if self.extras:
try:
self.xdata.getroottree().xinclude()
except lxml.etree.XIncludeError:
err = sys.exc_info()[1]
logger.error("XInclude failed on %s: %s" % (self.name, err))
-
self.entries = self.xdata.getchildren()
if self.__identifier__ is not None:
self.label = self.xdata.attrib[self.__identifier__]
@@ -844,7 +859,7 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
self._matches(entry, metadata,
src.cache[1][entry.tag]))]
if len(matching) == 0:
- raise PluginExecutionError
+ raise PluginExecutionError('No matching source for entry when retrieving attributes for %s(%s)' % (entry.tag, entry.attrib.get('name')))
elif len(matching) == 1:
index = 0
else:
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index f39993496..5ba4de12f 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -36,13 +36,16 @@ class MetadataRuntimeError(Exception):
pass
-class XMLMetadataConfig(object):
+class XMLMetadataConfig(Bcfg2.Server.Plugin.SingleXMLFileBacked):
"""Handles xml config files and all XInclude statements"""
def __init__(self, metadata, watch_clients, basefile):
+ Bcfg2.Server.Plugin.SingleXMLFileBacked.__init__(self,
+ os.path.join(metadata.data,
+ basefile),
+ metadata.core.fam)
self.metadata = metadata
self.basefile = basefile
self.should_monitor = watch_clients
- self.extras = []
self.data = None
self.basedata = None
self.basedir = metadata.data
@@ -62,11 +65,10 @@ class XMLMetadataConfig(object):
raise MetadataRuntimeError
return self.basedata
- def add_monitor(self, fname):
+ def add_monitor(self, fpath, fname):
"""Add a fam monitor for an included file"""
if self.should_monitor:
- self.metadata.core.fam.AddMonitor(os.path.join(self.basedir, fname),
- self.metadata)
+ self.metadata.core.fam.AddMonitor(fpath, self.metadata)
self.extras.append(fname)
def load_xml(self):
@@ -76,13 +78,10 @@ class XMLMetadataConfig(object):
except lxml.etree.XMLSyntaxError:
self.logger.error('Failed to parse %s' % self.basefile)
return
+ self.extras = []
self.basedata = copy.copy(xdata)
- included = [ent.get('href') for ent in \
- xdata.findall('./{http://www.w3.org/2001/XInclude}include')]
- if included:
- for name in included:
- if name not in self.extras:
- self.add_monitor(name)
+ self._follow_xincludes(xdata=xdata)
+ if self.extras:
try:
xdata.xinclude()
except lxml.etree.XIncludeError:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
index 3511cfc3d..8d0067b6a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
@@ -44,8 +44,18 @@ class PackagesSources(Bcfg2.Server.Plugin.SingleXMLFileBacked,
def HandleEvent(self, event=None):
Bcfg2.Server.Plugin.SingleXMLFileBacked.HandleEvent(self, event=event)
- if event.filename != self.name:
- self.parsed.add(os.path.basename(event.filename))
+ if event and event.filename != self.name:
+ for fname in self.extras:
+ fpath = None
+ if fname.startswith("/"):
+ fpath = os.path.abspath(fname)
+ else:
+ fpath = \
+ os.path.abspath(os.path.join(os.path.dirname(self.name),
+ fname))
+ if fpath == os.path.abspath(event.filename):
+ self.parsed.add(fname)
+ break
if sorted(list(self.parsed)) == sorted(self.extras):
self.logger.info("Reloading Packages plugin")
diff --git a/src/lib/Bcfg2/Server/Plugins/TGenshi.py b/src/lib/Bcfg2/Server/Plugins/TGenshi.py
index 3ba0f4272..c4dd40614 100644
--- a/src/lib/Bcfg2/Server/Plugins/TGenshi.py
+++ b/src/lib/Bcfg2/Server/Plugins/TGenshi.py
@@ -114,13 +114,13 @@ class TemplateFile:
if entry.text == '':
entry.set('empty', 'true')
except TemplateError:
- terror = sys.exc_info()[1]
- logger.error('Genshi template error: %s' % terror)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ err = sys.exc_info()[1]
+ logger.exception('Genshi template error')
+ raise Bcfg2.Server.Plugin.PluginExecutionError('Genshi template error: %s' % err)
except AttributeError:
err = sys.exc_info()[1]
- logger.error('Genshi template loading error: %s' % err)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ logger.exception('Genshi template loading error')
+ raise Bcfg2.Server.Plugin.PluginExecutionError('Genshi template loading error: %s' % err)
class TGenshi(Bcfg2.Server.Plugin.GroupSpool):
diff --git a/src/lib/Bcfg2/Server/Reports/settings.py b/src/lib/Bcfg2/Server/Reports/settings.py
index c8ceb5d88..4d567f1a2 100644
--- a/src/lib/Bcfg2/Server/Reports/settings.py
+++ b/src/lib/Bcfg2/Server/Reports/settings.py
@@ -64,10 +64,10 @@ if django.VERSION[0] == 1 and django.VERSION[1] < 2:
# Local time zone for this installation. All choices can be found here:
# http://docs.djangoproject.com/en/dev/ref/settings/#time-zone
-try:
- TIME_ZONE = c.get('statistics', 'time_zone')
-except:
- if django.VERSION[0] == 1 and django.VERSION[1] > 2:
+if django.VERSION[0] == 1 and django.VERSION[1] > 2:
+ try:
+ TIME_ZONE = c.get('statistics', 'time_zone')
+ except:
TIME_ZONE = None
# Language code for this installation. All choices can be found here:
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 1e7ec4d49..580de5248 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-
"""This tool loads the Bcfg2 core into an interactive debugger."""
from code import InteractiveConsole
@@ -521,7 +520,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
if len(source.whitelist):
print(" Whitelist: %s" % ", ".join(source.whitelist))
print("")
-
+
def do_profile(self, arg):
"""."""
if not have_profile:
@@ -545,27 +544,25 @@ if __name__ == '__main__':
optinfo = {
'configfile': Bcfg2.Options.CFILE,
'help': Bcfg2.Options.HELP,
- }
- optinfo.update({
- 'event debug': Bcfg2.Options.DEBUG,
- 'profile': Bcfg2.Options.CORE_PROFILE,
- 'encoding': Bcfg2.Options.ENCODING,
- # Server options
- 'repo': Bcfg2.Options.SERVER_REPOSITORY,
- 'plugins': Bcfg2.Options.SERVER_PLUGINS,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- 'mconnect': Bcfg2.Options.SERVER_MCONNECT,
- 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR,
- 'location': Bcfg2.Options.SERVER_LOCATION,
- 'static': Bcfg2.Options.SERVER_STATIC,
- 'key': Bcfg2.Options.SERVER_KEY,
- 'cert': Bcfg2.Options.SERVER_CERT,
- 'ca': Bcfg2.Options.SERVER_CA,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- 'protocol': Bcfg2.Options.SERVER_PROTOCOL,
- # More options
- 'logging': Bcfg2.Options.LOGGING_FILE_PATH
- })
+ 'event debug': Bcfg2.Options.DEBUG,
+ 'profile': Bcfg2.Options.CORE_PROFILE,
+ 'encoding': Bcfg2.Options.ENCODING,
+ # Server options
+ 'repo': Bcfg2.Options.SERVER_REPOSITORY,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'mconnect': Bcfg2.Options.SERVER_MCONNECT,
+ 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR,
+ 'location': Bcfg2.Options.SERVER_LOCATION,
+ 'static': Bcfg2.Options.SERVER_STATIC,
+ 'key': Bcfg2.Options.SERVER_KEY,
+ 'cert': Bcfg2.Options.SERVER_CERT,
+ 'ca': Bcfg2.Options.SERVER_CA,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'protocol': Bcfg2.Options.SERVER_PROTOCOL,
+ # More options
+ 'logging': Bcfg2.Options.LOGGING_FILE_PATH
+ }
setup = Bcfg2.Options.OptionParser(optinfo)
setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(),
USAGE)
diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test
index af2225245..01a2a4893 100644..100755
--- a/src/sbin/bcfg2-test
+++ b/src/sbin/bcfg2-test
@@ -1,41 +1,60 @@
#!/usr/bin/env python
-"""This tool verifies that all clients known to the server build without failures"""
+"""This tool verifies that all clients known to the server build
+without failures"""
import sys
+import fnmatch
+import logging
+import Bcfg2.Logger
import Bcfg2.Server.Core
from nose.core import TestProgram
+from nose.suite import LazySuite
from unittest import TestCase
class ClientTest(TestCase):
"""
- A test case representing the build of all of the configuration for a single host.
- Checks that none of the build config entities has had a failure when it is building.
- Optionally ignores some config files that we know will cause errors (because they
- are private files we don't have access to, for instance)
+ A test case representing the build of all of the configuration for
+ a single host. Checks that none of the build config entities has
+ had a failure when it is building. Optionally ignores some config
+ files that we know will cause errors (because they are private
+ files we don't have access to, for instance)
"""
__test__ = False # Do not collect
- def __init__(self, bcfg2_core, client):
+ def __init__(self, bcfg2_core, client, ignore=None):
TestCase.__init__(self)
self.bcfg2_core = bcfg2_core
self.client = client
+ if ignore is None:
+ self.ignore = dict()
+ else:
+ self.ignore = ignore
+
+ def ignore_entry(self, tag, name):
+ if tag in self.ignore:
+ if name in self.ignore[tag]:
+ return True
+ else:
+ # try wildcard matching
+ for pattern in self.ignore[tag]:
+ if fnmatch.fnmatch(name, pattern):
+ return True
+ return False
def runTest(self):
config = self.bcfg2_core.BuildConfiguration(self.client)
- failures = config.xpath('//*[@failure]')
- def format_failure(failure):
- return "%s(%s): %s" % (
- failure.tag,
- failure.attrib.get('name'),
- failure.attrib.get('failure')
- )
+ failures = []
+ msg = ["Failures:"]
+ for failure in config.xpath('//*[@failure]'):
+ if not self.ignore_entry(failure.tag, failure.get('name')):
+ failures.append(failure)
+ msg.append("%s:%s: %s" % (failure.tag, failure.get("name"),
+ failure.get("failure")))
+
+ assert len(failures) == 0, "\n".join(msg)
- assert len(failures) == 0, "Failures:\n%s" % "\n".join(
- [format_failure(failure) for failure in failures]
- )
-
def __str__(self):
return "ClientTest(%s)" % self.client
@@ -43,26 +62,55 @@ class ClientTest(TestCase):
def main():
optinfo = {
- 'configfile': Bcfg2.Options.CFILE,
- 'help': Bcfg2.Options.HELP,
- 'encoding': Bcfg2.Options.ENCODING,
- 'repo': Bcfg2.Options.SERVER_REPOSITORY,
- 'plugins': Bcfg2.Options.SERVER_PLUGINS,
- 'password': Bcfg2.Options.SERVER_PASSWORD,
- }
+ 'configfile': Bcfg2.Options.CFILE,
+ 'help': Bcfg2.Options.HELP,
+ 'encoding': Bcfg2.Options.ENCODING,
+ 'repo': Bcfg2.Options.SERVER_REPOSITORY,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'verbose': Bcfg2.Options.VERBOSE,
+ 'noseopts': Bcfg2.Options.TEST_NOSEOPTS,
+ 'ignore': Bcfg2.Options.TEST_IGNORE,
+ }
setup = Bcfg2.Options.OptionParser(optinfo)
+ setup.hm = \
+ "bcfg2-test [options] [client] [client] [...]\nOptions:\n %s" % \
+ setup.buildHelpMessage()
setup.parse(sys.argv[1:])
+
+ if setup['verbose']:
+ Bcfg2.Logger.setup_logging("bcfg2-test", to_syslog=False)
+
core = Bcfg2.Server.Core.Core(
setup['repo'],
setup['plugins'],
setup['password'],
setup['encoding'],
filemonitor='pseudo'
- )
- core.fam.handle_events_in_interval(0.1)
- suite = [ClientTest(core, client) for client in core.metadata.clients]
+ )
+
+ ignore = dict()
+ for entry in setup['ignore']:
+ tag, name = entry.split(":")
+ try:
+ ignore[tag].append(name)
+ except KeyError:
+ ignore[tag] = [name]
+
+ def run_tests():
+ core.fam.handle_events_in_interval(0.1)
+
+ if setup['args']:
+ clients = setup['args']
+ else:
+ clients = core.metadata.clients
+
+ for client in clients:
+ logging.info("Building %s" % client)
+ yield ClientTest(core, client, ignore)
- TestProgram(argv=sys.argv[0:1], suite = suite)
+ TestProgram(argv=sys.argv[0:1] + setup['noseopts'],
+ suite=LazySuite(run_tests))
if __name__ == "__main__":
sys.exit(main())
diff --git a/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
index 4d0b96ee7..455731d00 100644
--- a/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
+++ b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
@@ -67,11 +67,12 @@ def get_metadata_object(core=None, watch_clients=False):
class TestXMLMetadataConfig(unittest.TestCase):
- def get_config_object(self, basefile=None, core=None, watch_clients=False):
+ def get_config_object(self, basefile="clients.xml", core=None,
+ watch_clients=False):
self.metadata = get_metadata_object(core=core,
watch_clients=watch_clients)
return XMLMetadataConfig(self.metadata, watch_clients, basefile)
-
+
def test_xdata(self):
config = self.get_config_object()
# we can't use assertRaises here because xdata is a property
@@ -99,17 +100,19 @@ class TestXMLMetadataConfig(unittest.TestCase):
def test_add_monitor(self):
core = Mock()
config = self.get_config_object(core=core)
+
+ fname = "test.xml"
+ fpath = os.path.join(self.metadata.data, "test.xml")
+
config.extras = []
- config.add_monitor("test.xml")
+ config.add_monitor(fpath, fname)
self.assertFalse(core.fam.AddMonitor.called)
self.assertEqual(config.extras, [])
-
+
config = self.get_config_object(core=core, watch_clients=True)
- config.add_monitor("test.xml")
- core.fam.AddMonitor.assert_called_with(os.path.join(self.metadata.data,
- "test.xml"),
- self.metadata)
- self.assertItemsEqual(config.extras, ["test.xml"])
+ config.add_monitor(fpath, fname)
+ core.fam.AddMonitor.assert_called_with(fpath, self.metadata)
+ self.assertItemsEqual(config.extras, [fname])
@patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.add_monitor")
@patch("lxml.etree.parse")
@@ -131,11 +134,15 @@ class TestXMLMetadataConfig(unittest.TestCase):
config.data = None
config.basedata = None
- mock_parse.return_value.findall = \
- Mock(return_value=[lxml.etree.Element(XI + "include",
- href="more.xml"),
- lxml.etree.Element(XI + "include",
- href="evenmore.xml")])
+
+ def side_effect(*args):
+ def second_call(*args):
+ return []
+ mock_parse.return_value.findall.side_effect = second_call
+ return [lxml.etree.Element(XI + "include", href="more.xml"),
+ lxml.etree.Element(XI + "include", href="evenmore.xml")]
+
+ mock_parse.return_value.findall = Mock(side_effect=side_effect)
config.load_xml()
mock_add_monitor.assert_any_call("more.xml")
mock_add_monitor.assert_any_call("evenmore.xml")
@@ -163,9 +170,9 @@ class TestXMLMetadataConfig(unittest.TestCase):
fpath = os.path.join(self.metadata.data, fname)
tmpfile = "%s.new" % fpath
linkdest = os.path.join(self.metadata.data, "client-link.xml")
-
+
mock_islink.return_value = False
-
+
config.write_xml(fpath, clients_test_tree)
mock_open.assert_called_with(tmpfile, "w")
self.assertTrue(mock_open.return_value.write.called)
@@ -293,7 +300,7 @@ class TestMetadata(unittest.TestCase):
mock_parse.assert_called_with(os.path.join(datastore, "Metadata",
"groups.xml"))
self.assertIsInstance(groups, lxml.etree._Element)
-
+
def test_search_xdata_name(self):
# test finding a node with the proper name
metadata = get_metadata_object()
@@ -403,7 +410,7 @@ class TestMetadata(unittest.TestCase):
self.assertRaises(MetadataConsistencyError,
metadata.remove_group,
"bogus_group")
-
+
def test_add_bundle(self):
metadata = get_metadata_object()
metadata.groups_xml.write = Mock()
@@ -441,7 +448,7 @@ class TestMetadata(unittest.TestCase):
self.assertRaises(MetadataConsistencyError,
metadata.remove_bundle,
"bogus_bundle")
-
+
def test_add_client(self):
metadata = get_metadata_object()
metadata.clients_xml.write = Mock()
@@ -692,7 +699,7 @@ class TestMetadata(unittest.TestCase):
set(["bundle1", "bundle2"]), set(),
set(["1.2.3.2"]), dict(category1="group1"),
None, None))
-
+
imd = metadata.get_initial_metadata("alias1")
self.assertEqual(mock_clientmetadata.call_args[0][:9],
("client3", "group1", set(["group1"]), set(),
@@ -708,7 +715,7 @@ class TestMetadata(unittest.TestCase):
self.assertRaises(MetadataConsistencyError,
metadata.get_initial_metadata,
"client_new2")
-
+
@patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
def test_get_all_group_names(self):
@@ -836,7 +843,7 @@ class TestMetadata(unittest.TestCase):
# floating cert-auth clients add themselves to the cache
self.assertIn("1.2.3.1", metadata.session_cache)
self.assertEqual(metadata.session_cache["1.2.3.1"][1], "client1")
-
+
cert = dict(subject=[[("commonName", "client7")]])
self.assertTrue(metadata.AuthenticateConnection(cert, "root", None,
"1.2.3.4"))
@@ -877,7 +884,7 @@ class TestMetadata(unittest.TestCase):
self.assertFalse(metadata.AuthenticateConnection(None, "client5",
"password2",
- "1.2.3.7"))
+ "1.2.3.7"))
@patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
@patch("Bcfg2.Server.Plugins.Metadata.Metadata.update_client")