diff options
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> @@ -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") |