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/appendix/guides/gentoo.txt73
-rw-r--r--doc/conf.py8
-rw-r--r--doc/development/index.txt1
-rw-r--r--doc/development/plugins.txt4
-rw-r--r--doc/development/setup.txt7
-rw-r--r--doc/development/unit-testing.txt25
-rw-r--r--doc/server/plugins/connectors/templatehelper.txt74
-rw-r--r--doc/server/plugins/generators/packages.txt40
-rw-r--r--doc/server/plugins/probes/group.txt2
-rw-r--r--examples/TemplateHelper/include.py97
-rw-r--r--gentoo/bcfg2-1.2.2.ebuild43
-rw-r--r--misc/bcfg2.spec116
-rw-r--r--redhat/bcfg2.spec.in3
-rw-r--r--schemas/base.xsd1
-rw-r--r--schemas/bundle.xsd3
-rw-r--r--schemas/clients.xsd9
-rw-r--r--schemas/decisions.xsd1
-rw-r--r--schemas/defaults.xsd1
-rw-r--r--schemas/genshi.xsd1
-rw-r--r--schemas/grouppatterns.xsd6
-rw-r--r--schemas/metadata.xsd1
-rw-r--r--schemas/packages.xsd18
-rw-r--r--schemas/pathentry.xsd1
-rw-r--r--schemas/pkglist.xsd1
-rw-r--r--schemas/pkgtype.xsd4
-rw-r--r--schemas/rules.xsd1
-rw-r--r--schemas/services.xsd1
-rw-r--r--schemas/servicetype.xsd1
-rw-r--r--schemas/types.xsd1
-rwxr-xr-xsetup.py82
-rw-r--r--src/lib/Bcfg2/Bcfg2Py3k.py (renamed from src/lib/Bcfg2Py3k.py)11
-rw-r--r--src/lib/Bcfg2/Client/Frame.py (renamed from src/lib/Client/Frame.py)4
-rw-r--r--src/lib/Bcfg2/Client/Tools/APK.py (renamed from src/lib/Client/Tools/APK.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/APT.py (renamed from src/lib/Client/Tools/APT.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/Action.py (renamed from src/lib/Client/Tools/Action.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/Blast.py (renamed from src/lib/Client/Tools/Blast.py)2
-rw-r--r--src/lib/Bcfg2/Client/Tools/Chkconfig.py (renamed from src/lib/Client/Tools/Chkconfig.py)2
-rw-r--r--src/lib/Bcfg2/Client/Tools/DebInit.py (renamed from src/lib/Client/Tools/DebInit.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/Encap.py (renamed from src/lib/Client/Tools/Encap.py)2
-rw-r--r--src/lib/Bcfg2/Client/Tools/FreeBSDInit.py (renamed from src/lib/Client/Tools/FreeBSDInit.py)0
-rw-r--r--src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py (renamed from src/lib/Client/Tools/FreeBSDPackage.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/IPS.py (renamed from src/lib/Client/Tools/IPS.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/MacPorts.py (renamed from src/lib/Client/Tools/MacPorts.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX.py (renamed from src/lib/Client/Tools/POSIX.py)3
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pacman.py (renamed from src/lib/Client/Tools/Pacman.py)0
-rw-r--r--src/lib/Bcfg2/Client/Tools/Portage.py125
-rw-r--r--src/lib/Bcfg2/Client/Tools/RPMng.py (renamed from src/lib/Client/Tools/RPMng.py)10
-rw-r--r--src/lib/Bcfg2/Client/Tools/RcUpdate.py (renamed from src/lib/Client/Tools/RcUpdate.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/SMF.py (renamed from src/lib/Client/Tools/SMF.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/SYSV.py (renamed from src/lib/Client/Tools/SYSV.py)2
-rw-r--r--src/lib/Bcfg2/Client/Tools/Systemd.py (renamed from src/lib/Client/Tools/Systemd.py)0
-rw-r--r--src/lib/Bcfg2/Client/Tools/Upstart.py (renamed from src/lib/Client/Tools/Upstart.py)1
-rw-r--r--src/lib/Bcfg2/Client/Tools/VCS.py (renamed from src/lib/Client/Tools/VCS.py)0
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM24.py (renamed from src/lib/Client/Tools/YUM24.py)7
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUMng.py (renamed from src/lib/Client/Tools/YUMng.py)10
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py (renamed from src/lib/Client/Tools/__init__.py)5
-rw-r--r--src/lib/Bcfg2/Client/Tools/launchd.py (renamed from src/lib/Client/Tools/launchd.py)1
-rwxr-xr-xsrc/lib/Bcfg2/Client/Tools/rpmtools.py (renamed from src/lib/Client/Tools/rpmtools.py)1
-rw-r--r--src/lib/Bcfg2/Client/XML.py (renamed from src/lib/Client/XML.py)1
-rw-r--r--src/lib/Bcfg2/Client/__init__.py (renamed from src/lib/Client/__init__.py)1
-rw-r--r--src/lib/Bcfg2/Component.py (renamed from src/lib/Component.py)6
-rw-r--r--src/lib/Bcfg2/Logger.py (renamed from src/lib/Logger.py)1
-rw-r--r--src/lib/Bcfg2/Options.py (renamed from src/lib/Options.py)20
-rw-r--r--src/lib/Bcfg2/Proxy.py (renamed from src/lib/Proxy.py)5
-rw-r--r--src/lib/Bcfg2/SSLServer.py (renamed from src/lib/SSLServer.py)2
-rw-r--r--src/lib/Bcfg2/Server/Admin/Backup.py (renamed from src/lib/Server/Admin/Backup.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Bundle.py (renamed from src/lib/Server/Admin/Bundle.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Client.py (renamed from src/lib/Server/Admin/Client.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Compare.py (renamed from src/lib/Server/Admin/Compare.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Group.py (renamed from src/lib/Server/Admin/Group.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Init.py (renamed from src/lib/Server/Admin/Init.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Minestruct.py (renamed from src/lib/Server/Admin/Minestruct.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Perf.py (renamed from src/lib/Server/Admin/Perf.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Pull.py (renamed from src/lib/Server/Admin/Pull.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Query.py (renamed from src/lib/Server/Admin/Query.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Reports.py (renamed from src/lib/Server/Admin/Reports.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Snapshots.py (renamed from src/lib/Server/Admin/Snapshots.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Tidy.py (renamed from src/lib/Server/Admin/Tidy.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Viz.py (renamed from src/lib/Server/Admin/Viz.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/Xcmd.py (renamed from src/lib/Server/Admin/Xcmd.py)0
-rw-r--r--src/lib/Bcfg2/Server/Admin/__init__.py (renamed from src/lib/Server/Admin/__init__.py)4
-rw-r--r--src/lib/Bcfg2/Server/Core.py (renamed from src/lib/Server/Core.py)9
-rw-r--r--src/lib/Bcfg2/Server/FileMonitor.py (renamed from src/lib/Server/FileMonitor.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/.gitignore (renamed from src/lib/Server/Hostbase/.gitignore)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/__init__.py (renamed from src/lib/Server/Hostbase/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/backends.py (renamed from src/lib/Server/Hostbase/backends.py)2
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/__init__.py (renamed from src/lib/Server/Hostbase/hostbase/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/admin.py (renamed from src/lib/Server/Hostbase/hostbase/admin.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/models.py (renamed from src/lib/Server/Hostbase/hostbase/models.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/sql/zone.sql (renamed from src/lib/Server/Hostbase/hostbase/sql/zone.sql)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/urls.py (renamed from src/lib/Server/Hostbase/hostbase/urls.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/views.py (renamed from src/lib/Server/Hostbase/hostbase/views.py)2
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/base.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/base.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/confirm.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/confirm.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/copy.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/copy.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dns.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/dns.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dnsedit.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/dnsedit.html)4
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/edit.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/edit.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/errors.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/errors.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/host.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/host.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html)2
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/index.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/index.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/login.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/login.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/logout.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.tmpl (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/logout.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logviewer.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/logviewer.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/navbar.tmpl (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/navbar.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/new.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/new.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/remove.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/remove.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/results.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/results.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/search.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/search.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneedit.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/zoneedit.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zonenew.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/zonenew.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zones.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/zones.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneview.html (renamed from src/lib/Server/Hostbase/hostbase/webtemplates/zoneview.html)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/ldapauth.py (renamed from src/lib/Server/Hostbase/ldapauth.py)0
-rwxr-xr-xsrc/lib/Bcfg2/Server/Hostbase/manage.py (renamed from src/lib/Server/Hostbase/manage.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/media/base.css (renamed from src/lib/Server/Hostbase/media/base.css)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/media/boxypastel.css (renamed from src/lib/Server/Hostbase/media/boxypastel.css)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/media/global.css (renamed from src/lib/Server/Hostbase/media/global.css)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/media/layout.css (renamed from src/lib/Server/Hostbase/media/layout.css)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/nisauth.py (renamed from src/lib/Server/Hostbase/nisauth.py)4
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/regex.py (renamed from src/lib/Server/Hostbase/regex.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/settings.py (renamed from src/lib/Server/Hostbase/settings.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/batchadd.tmpl (renamed from src/lib/Server/Hostbase/templates/batchadd.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.conf.head (renamed from src/lib/Server/Hostbase/templates/dhcpd.conf.head)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.tmpl (renamed from src/lib/Server/Hostbase/templates/dhcpd.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/hosts.tmpl (renamed from src/lib/Server/Hostbase/templates/hosts.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/hostsappend.tmpl (renamed from src/lib/Server/Hostbase/templates/hostsappend.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/named.tmpl (renamed from src/lib/Server/Hostbase/templates/named.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/namedviews.tmpl (renamed from src/lib/Server/Hostbase/templates/namedviews.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/reverseappend.tmpl (renamed from src/lib/Server/Hostbase/templates/reverseappend.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/reversesoa.tmpl (renamed from src/lib/Server/Hostbase/templates/reversesoa.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/templates/zone.tmpl (renamed from src/lib/Server/Hostbase/templates/zone.tmpl)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/test/harness.py (renamed from src/lib/Server/Hostbase/test/harness.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/test/test_environ_settings.py (renamed from src/lib/Server/Hostbase/test/test_environ_settings.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/test/test_ldapauth.py (renamed from src/lib/Server/Hostbase/test/test_ldapauth.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/test/test_settings.py (renamed from src/lib/Server/Hostbase/test/test_settings.py)0
-rw-r--r--src/lib/Bcfg2/Server/Hostbase/urls.py (renamed from src/lib/Server/Hostbase/urls.py)0
-rw-r--r--src/lib/Bcfg2/Server/Lint/Bundles.py (renamed from src/lib/Server/Lint/Bundles.py)4
-rw-r--r--src/lib/Bcfg2/Server/Lint/Comments.py (renamed from src/lib/Server/Lint/Comments.py)8
-rw-r--r--src/lib/Bcfg2/Server/Lint/Duplicates.py (renamed from src/lib/Server/Lint/Duplicates.py)2
-rwxr-xr-xsrc/lib/Bcfg2/Server/Lint/Genshi.py (renamed from src/lib/Server/Lint/Genshi.py)7
-rw-r--r--src/lib/Bcfg2/Server/Lint/GroupPatterns.py (renamed from src/lib/Server/Lint/GroupPatterns.py)4
-rw-r--r--src/lib/Bcfg2/Server/Lint/InfoXML.py (renamed from src/lib/Server/Lint/InfoXML.py)0
-rw-r--r--src/lib/Bcfg2/Server/Lint/MergeFiles.py (renamed from src/lib/Server/Lint/MergeFiles.py)2
-rw-r--r--src/lib/Bcfg2/Server/Lint/Pkgmgr.py (renamed from src/lib/Server/Lint/Pkgmgr.py)0
-rw-r--r--src/lib/Bcfg2/Server/Lint/RequiredAttrs.py (renamed from src/lib/Server/Lint/RequiredAttrs.py)0
-rw-r--r--src/lib/Bcfg2/Server/Lint/Validate.py (renamed from src/lib/Server/Lint/Validate.py)6
-rw-r--r--src/lib/Bcfg2/Server/Lint/__init__.py (renamed from src/lib/Server/Lint/__init__.py)4
-rw-r--r--src/lib/Bcfg2/Server/Plugin.py (renamed from src/lib/Server/Plugin.py)66
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Account.py (renamed from src/lib/Server/Plugins/Account.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/BB.py (renamed from src/lib/Server/Plugins/BB.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Base.py (renamed from src/lib/Server/Plugins/Base.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bundler.py (renamed from src/lib/Server/Plugins/Bundler.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bzr.py (renamed from src/lib/Server/Plugins/Bzr.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg.py (renamed from src/lib/Server/Plugins/Cfg.py)66
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cvs.py (renamed from src/lib/Server/Plugins/Cvs.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/DBStats.py (renamed from src/lib/Server/Plugins/DBStats.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Darcs.py (renamed from src/lib/Server/Plugins/Darcs.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Decisions.py (renamed from src/lib/Server/Plugins/Decisions.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Defaults.py (renamed from src/lib/Server/Plugins/Defaults.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Deps.py (renamed from src/lib/Server/Plugins/Deps.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Editor.py (renamed from src/lib/Server/Plugins/Editor.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/FileProbes.py (renamed from src/lib/Server/Plugins/FileProbes.py)12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Fossil.py (renamed from src/lib/Server/Plugins/Fossil.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py (renamed from src/lib/Server/Plugins/Git.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupPatterns.py (renamed from src/lib/Server/Plugins/GroupPatterns.py)0
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Guppy.py (renamed from src/lib/Server/Plugins/Guppy.py)5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Hg.py (renamed from src/lib/Server/Plugins/Hg.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Hostbase.py (renamed from src/lib/Server/Plugins/Hostbase.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ldap.py (renamed from src/lib/Server/Plugins/Ldap.py)53
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py (renamed from src/lib/Server/Plugins/Metadata.py)637
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py (renamed from src/lib/Server/Plugins/NagiosGen.py)7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ohai.py (renamed from src/lib/Server/Plugins/Ohai.py)0
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Apt.py (renamed from src/lib/Server/Plugins/Packages/Apt.py)8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Collection.py (renamed from src/lib/Server/Plugins/Packages/Collection.py)8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pac.py (renamed from src/lib/Server/Plugins/Packages/Pac.py)9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesConfig.py (renamed from src/lib/Server/Plugins/Packages/PackagesConfig.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py (renamed from src/lib/Server/Plugins/Packages/PackagesSources.py)16
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Source.py (renamed from src/lib/Server/Plugins/Packages/Source.py)5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py (renamed from src/lib/Server/Plugins/Packages/Yum.py)36
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/__init__.py (renamed from src/lib/Server/Plugins/Packages/__init__.py)12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Pkgmgr.py (renamed from src/lib/Server/Plugins/Pkgmgr.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Probes.py (renamed from src/lib/Server/Plugins/Probes.py)5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Properties.py (renamed from src/lib/Server/Plugins/Properties.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Rules.py (renamed from src/lib/Server/Plugins/Rules.py)4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SGenshi.py (renamed from src/lib/Server/Plugins/SGenshi.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSHbase.py (renamed from src/lib/Server/Plugins/SSHbase.py)14
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSLCA.py (renamed from src/lib/Server/Plugins/SSLCA.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Snapshots.py (renamed from src/lib/Server/Plugins/Snapshots.py)0
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Statistics.py (renamed from src/lib/Server/Plugins/Statistics.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Svcmgr.py (renamed from src/lib/Server/Plugins/Svcmgr.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Svn.py (renamed from src/lib/Server/Plugins/Svn.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Svn2.py (renamed from src/lib/Server/Plugins/Svn2.py)4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TCheetah.py (renamed from src/lib/Server/Plugins/TCheetah.py)2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TGenshi.py (renamed from src/lib/Server/Plugins/TGenshi.py)12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py83
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Trigger.py (renamed from src/lib/Server/Plugins/Trigger.py)1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/__init__.py (renamed from src/lib/Server/Plugins/__init__.py)3
-rw-r--r--src/lib/Bcfg2/Server/Reports/__init__.py (renamed from src/lib/Server/Reports/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/backends.py (renamed from src/lib/Server/Reports/backends.py)0
-rwxr-xr-xsrc/lib/Bcfg2/Server/Reports/importscript.py (renamed from src/lib/Server/Reports/importscript.py)3
-rwxr-xr-xsrc/lib/Bcfg2/Server/Reports/manage.py (renamed from src/lib/Server/Reports/manage.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/nisauth.py (renamed from src/lib/Server/Reports/nisauth.py)2
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/__init__.py (renamed from src/lib/Server/Reports/reports/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/fixtures/initial_version.xml (renamed from src/lib/Server/Reports/reports/fixtures/initial_version.xml)4
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/models.py (renamed from src/lib/Server/Reports/reports/models.py)1
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/sql/client.sql (renamed from src/lib/Server/Reports/reports/sql/client.sql)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/404.html (renamed from src/lib/Server/Reports/reports/templates/404.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/base-timeview.html (renamed from src/lib/Server/Reports/reports/templates/base-timeview.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/base.html (renamed from src/lib/Server/Reports/reports/templates/base.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/clients/detail.html (renamed from src/lib/Server/Reports/reports/templates/clients/detail.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/clients/detailed-list.html (renamed from src/lib/Server/Reports/reports/templates/clients/detailed-list.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/clients/history.html (renamed from src/lib/Server/Reports/reports/templates/clients/history.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/clients/index.html (renamed from src/lib/Server/Reports/reports/templates/clients/index.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/clients/manage.html (renamed from src/lib/Server/Reports/reports/templates/clients/manage.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/config_items/item.html (renamed from src/lib/Server/Reports/reports/templates/config_items/item.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/config_items/listing.html (renamed from src/lib/Server/Reports/reports/templates/config_items/listing.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/displays/summary.html (renamed from src/lib/Server/Reports/reports/templates/displays/summary.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/displays/timing.html (renamed from src/lib/Server/Reports/reports/templates/displays/timing.html)2
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/widgets/filter_bar.html (renamed from src/lib/Server/Reports/reports/templates/widgets/filter_bar.html)2
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/widgets/interaction_list.inc (renamed from src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templates/widgets/page_bar.html (renamed from src/lib/Server/Reports/reports/templates/widgets/page_bar.html)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templatetags/__init__.py (renamed from src/lib/Server/Reports/reports/templatetags/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templatetags/bcfg2_tags.py (renamed from src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py)4
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/templatetags/syntax_coloring.py (renamed from src/lib/Server/Reports/reports/templatetags/syntax_coloring.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/urls.py (renamed from src/lib/Server/Reports/reports/urls.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/views.py (renamed from src/lib/Server/Reports/reports/views.py)0
-rw-r--r--src/lib/Bcfg2/Server/Reports/settings.py (renamed from src/lib/Server/Reports/settings.py)12
-rw-r--r--src/lib/Bcfg2/Server/Reports/updatefix.py (renamed from src/lib/Server/Reports/updatefix.py)96
-rw-r--r--src/lib/Bcfg2/Server/Reports/urls.py (renamed from src/lib/Server/Reports/urls.py)0
-rwxr-xr-xsrc/lib/Bcfg2/Server/Reports/utils.py (renamed from src/lib/Server/Reports/utils.py)0
-rw-r--r--src/lib/Bcfg2/Server/Snapshots/__init__.py (renamed from src/lib/Server/Snapshots/__init__.py)0
-rw-r--r--src/lib/Bcfg2/Server/Snapshots/model.py (renamed from src/lib/Server/Snapshots/model.py)0
-rw-r--r--src/lib/Bcfg2/Server/__init__.py (renamed from src/lib/Server/__init__.py)2
-rw-r--r--src/lib/Bcfg2/Statistics.py (renamed from src/lib/Statistics.py)0
-rw-r--r--src/lib/Bcfg2/__init__.py (renamed from src/lib/__init__.py)1
-rw-r--r--src/lib/Bcfg2Py3Incompat.py2
-rw-r--r--src/lib/Client/Tools/Portage.py72
-rwxr-xr-xsrc/sbin/bcfg21
-rwxr-xr-xsrc/sbin/bcfg2-admin3
-rwxr-xr-xsrc/sbin/bcfg2-build-reports2
-rwxr-xr-xsrc/sbin/bcfg2-info60
-rwxr-xr-xsrc/sbin/bcfg2-lint1
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep2
-rwxr-xr-xsrc/sbin/bcfg2-reports1
-rwxr-xr-xsrc/sbin/bcfg2-server1
-rwxr-xr-x[-rw-r--r--]src/sbin/bcfg2-test104
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper2
-rw-r--r--testsuite/TestFrame.py104
-rw-r--r--testsuite/TestOptions.py103
-rw-r--r--testsuite/TestPlugin.py122
-rw-r--r--testsuite/Testlib/TestOptions.py124
-rw-r--r--testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py905
-rwxr-xr-xtools/bcfg2-export-config1
-rwxr-xr-xtools/create-debian-pkglist.py1
-rwxr-xr-xtools/encap-util-count.sh1
-rwxr-xr-xtools/encap-util-expand.sh1
-rwxr-xr-xtools/encap-util-place.sh1
-rwxr-xr-xtools/encap-util-xml.sh1
-rwxr-xr-xtools/hostbasepush.py2
-rwxr-xr-xtools/pkgmgr_gen.py1
268 files changed, 2421 insertions, 1386 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/appendix/guides/gentoo.txt b/doc/appendix/guides/gentoo.txt
index d635e310b..da4acef19 100644
--- a/doc/appendix/guides/gentoo.txt
+++ b/doc/appendix/guides/gentoo.txt
@@ -16,28 +16,38 @@ let the list know if you find errors or omissions.
Installing Bcfg2
================
-Early in July 2008, Bcfg2 was added to the Gentoo portage tree. So far
-it's only keyworded for ~x86, but we hope to see it soon in the amd64 and
-x64-solaris ports. If you're using Gentoo on some other architecture, it
-should still work provided that you have a reasonably up to date Python;
-try adding `app-admin/bcfg2 ~*` to your `/etc/portage/package.keywords`
-file.
+Early in July 2008, Bcfg2 was added to the Gentoo portage tree.
If you don't use portage to install Bcfg2, you'll want to make sure you
have all the prerequisites installed first. For a server, you'll need:
-* ``app-admin/gamin`` or ``app-admin/fam``
+* ``dev-libs/libgamin[python]``
* ``dev-python/lxml``
Clients will need at least:
* ``app-portage/gentoolkit``
+Portage installs from source
+============================
+
+.. versionadded:: 1.3.0
+
+By default the client will run with the ``--gitbinpkgonly`` option. If
+you want your client to install packages from source (rather than
+having a binary build host as seen below), you can set the following in
+``/etc/bcfg2.conf``.::
+
+ [Portage]
+ binpkgonly = false
+
Package Repository
==================
+.. note: This is only necessary for using binary packages.
+
You’ll need (to make) at least one archive of binary packages. The
-Portage driver calls ``emerge`` with the ``-getbinpkgonly`` option. See
+Portage driver calls ``emerge`` with the ``--getbinpkgonly`` option. See
:manpage:`make.conf(5)` and :manpage:`emerge(1)` manpages, specifically
the :envvar:`PORTAGE_BINHOST` environment variable.
@@ -109,60 +119,17 @@ Configuring Client Machines
Set up ``/etc/bcfg2.conf`` the way you would for any other Bcfg2 client.
In ``make.conf``, set *PORTAGE_BINHOST* to point to the URI of
-your package repository. You may want to create versions of
+your package repository. You may want to create versions of
``make.conf`` for each package repository you maintain, with
appropriate *PORTAGE_BINHOST* URI's in each, and associated with
that package archive's group under ``Cfg`` -- for example, we have
-``Cfg/etc/make.conf/make.conf.G99_gentoo-200701-vmware``. If a client
+``Cfg/etc/make.conf/make.conf.G99_gentoo-200701-vmware``. If a client
host switches groups, and the new group needs a different set of packages,
everything should just fall into place.
Pitfalls
========
-Package Verification Issues
----------------------------
-
-As of this writing (2007/01/31), we're aware of a number of packages
-marked stable in the Gentoo x86 tree which, for one reason or another,
-consistently fail to verify cleanly under ``equery check``. In some cases
-(pam, openldap), files which don't (ever) exist on the system are
-nonetheless recorded in the package database; in some (python, Bcfg2,
-ahem), whole classes of files (.pyc and .pyo files) consistently fail
-their md5sum checks; and in others, the problem appears to be a
-discrepancy in the way that symlinks are created vs. the way they're
-recorded in the database. For example, in the OpenSSH package,
-/usr/bin/slogin is a symlink to ./ssh, but equery expects it to point to
-an unadorned ssh. An analogous situation exists with their manpages,
-leading to noise like this::
-
- # equery check openssh
- [ Checking net-misc/openssh-4.5_p1 ]
- !!! /etc/ssh/sshd_config has incorrect md5sum
- !!! /usr/bin/slogin does not point to ssh
- !!! /usr/share/man/man1/slogin.1.gz does not point to ssh.1.gz
- !!! /etc/ssh/ssh_config has incorrect md5sum
- * 62 out of 66 files good
-
-We can ignore the lines for ``ssh_config`` and ``sshd_config``; those will
-be caught by Bcfg2 as registered config files and handled appropriately.
-
-Because Bcfg2 relies on the client system's native package reporting
-tool to judge the state of installed packages, complaints like these
-about trivial or intractable verification failures can trigger unnecessary
-bundle reinstalls when the Bcfg2 client runs. Bcfg2 will catch on after a
-pass or two that the situation isn't getting any better with repeated
-package installs, stop trying, and list those packages as "bad" in
-the client system's statistics.
-
-Aside from filing bugs with the Gentoo package maintainers, your narrator
-has been unable to come up with a good approach to this. Maybe write a
-series of ``Rules`` definitions according to what the package database
-thinks it should find, and/or stage copies of affected files under
-``Cfg``, and associate those rules and files with the affected package in
-a bundle? Annoying but possibly necessary if you want your stats file
-to look good.
-
/boot
-----
diff --git a/doc/conf.py b/doc/conf.py
index 38160715d..5903b009a 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -104,7 +104,9 @@ html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+html_theme_options = {
+ "collapsiblesidebar": "true"
+}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
@@ -139,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/development/index.txt b/doc/development/index.txt
index 352000dc8..2a54bfad8 100644
--- a/doc/development/index.txt
+++ b/doc/development/index.txt
@@ -39,3 +39,4 @@ git access. Mail the :ref:`help-mailinglist` for details.
testing
documentation
docstyleguide
+ unit-testing
diff --git a/doc/development/plugins.txt b/doc/development/plugins.txt
index 15b512365..b2b70f553 100644
--- a/doc/development/plugins.txt
+++ b/doc/development/plugins.txt
@@ -164,7 +164,6 @@ Example Connector
Bcfg2.Server.Plugin.Connector):
'''The Foo plugin is here to illustrate a barebones connector'''
name = 'Foo'
- version = '$Revision: $'
experimental = True
def __init__(self, core, datastore):
@@ -195,13 +194,10 @@ do so. We will call our new plugin `MyMetadata`.
.. code-block:: python
- __revision__ = '$Revision$'
-
import Bcfg2.Server.Plugins.Metadata
class MyMetadata(Bcfg2.Server.Plugins.Metadata.Metadata):
'''This class contains data for bcfg2 server metadata'''
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore, watch_clients=True):
diff --git a/doc/development/setup.txt b/doc/development/setup.txt
index e9fc6e1e5..b04bce3fe 100644
--- a/doc/development/setup.txt
+++ b/doc/development/setup.txt
@@ -12,13 +12,8 @@ Checking Out a Copy of the Code
git clone git://git.mcs.anl.gov/bcfg2.git
-* Create link to :file:`src/lib`::
-
- cd bcfg2
- ln -s src/lib Bcfg2
-
* Add :file:`bcfg2/src/sbin` to your :envvar:`PATH` environment variable
-* Add :file:`bcfg2` to your :envvar:`PYTHONPATH` environment variable
+* Add :file:`bcfg2/src/lib` to your :envvar:`PYTHONPATH` environment variable
Using a Virtual Environment for Development
diff --git a/doc/development/unit-testing.txt b/doc/development/unit-testing.txt
new file mode 100644
index 000000000..30217dcc5
--- /dev/null
+++ b/doc/development/unit-testing.txt
@@ -0,0 +1,25 @@
+.. -*- mode: rst -*-
+
+.. _development-unit-testing:
+
+==================
+Bcfg2 unit testing
+==================
+
+.. _Python Mock Module: http://python-mock.sourceforge.net/
+.. _Python Nose: http://readthedocs.org/docs/nose/en/latest/
+
+You will first need to install the `Python Mock Module`_ and `Python
+Nose`_ modules. You can then run the existing tests with the
+following.::
+
+ cd testsuite
+ nosetests
+
+You should see output something like the following::
+
+ ..................................................
+ ----------------------------------------------------------------------
+ Ran 50 tests in 0.121s
+
+ OK
diff --git a/doc/server/plugins/connectors/templatehelper.txt b/doc/server/plugins/connectors/templatehelper.txt
new file mode 100644
index 000000000..24d7f18b5
--- /dev/null
+++ b/doc/server/plugins/connectors/templatehelper.txt
@@ -0,0 +1,74 @@
+.. -*- mode: rst -*-
+
+.. _server-plugins-connectors-templatehelper:
+
+==============
+TemplateHelper
+==============
+
+The TemplateHelper plugin is a connector plugin that adds Python
+classes and methods to client metadata instances for use in
+templates. This allows you to easily reuse code that is common
+amongst multiple templates and add convenience methods.
+
+Using TemplateHelper
+====================
+
+First, ``mkdir /var/lib/bcfg2/TemplateHelper`` and add
+**TemplateHelper** to your ``plugins`` line in ``/etc/bcfg2.conf``.
+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-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
+===============
+
+A helper module is just a Python module with three special conditions:
+
+* The filename must end with ``.py`` (before any specificity
+ strings, e.g., ``.G80_foo`` or ``.H_blah.example.com``
+* The module must have an attribute, ``__export__``, that lists all of
+ the classes, functions, variables, or other symbols you wish to
+ export from the module.
+* ``data``, ``handle_event``, ``name``, and ``specific`` are reserved
+ names. You should not include symbols with a reserved name in
+ ``__export__``. Additionally, including symbols that start with an
+ underscore or double underscore is bad form, and may also produce
+ errors.
+
+See ``examples/TemplateHelper`` for examples of helper modules.
+
+Usage
+=====
+
+Specific helpers can be referred to in
+templates as ``metadata.TemplateHelper[<modulename>]``. That accesses
+a HelperModule object will has, as attributes, all symbols listed in
+``__export__``. For example, consider this helper module::
+
+ __export__ = ["hello"]
+
+ def hello(metadata):
+ return "Hello, %s!" % metadata.hostname
+
+To use this in a TGenshi template, we could do::
+
+ ${metadata.TemplateHelper['hello'].hello(metadata)}
+
+The template would produce::
+
+ Hello, foo.example.com!
+
+Note that the client metadata object is not passed to a helper module
+in any magical way; if you want to access the client metadata object
+in a helper function or class, you must pass the object to the
+function manually.
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index 276b73093..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
@@ -205,14 +205,13 @@ something like this::
.. versionadded:: 1.1.0
- The default behavior of the Packages plugin is to not make
- any assumptions about which packages you want to have added
- automatically. For that reason, neither **Recommended** nor
- **Suggested** packages are added as dependencies by default. You
- will notice that the default behavior for apt is to add Recommended
- packages as dependencies. You can configure the Packages plugin to
- add recommended packages by adding the ``recommended`` attribute,
- e.g.:
+ The default behavior of the Packages plugin is to not make any
+ assumptions about which packages you want to have added automatically
+ [#f1]_. For that reason, neither **Recommended** nor **Suggested**
+ packages are added as dependencies by default. You will notice
+ that the default behavior for apt is to add Recommended packages as
+ dependencies. You can configure the Packages plugin to add recommended
+ packages by adding the ``recommended`` attribute, e.g.:
.. code-block:: xml
@@ -221,6 +220,14 @@ something like this::
.. warning:: You must regenerate the Packages cache when adding or
removing the recommended attribute.
+ .. [#f1] Bcfg2 will by default add **Essential** packages to the
+ client specification. You can disable this behavior by
+ setting the ``essential`` attribute to *false*:
+
+ .. code-block:: xml
+
+ <Source type="apt" essential="false" ...>
+
Yum sources can be similarly specified::
<Sources>
@@ -567,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
@@ -662,17 +669,8 @@ It understands the following directives:
* ``gpg_keypath``: The path on the client RPM GPG keys will be copied
to before they are imported on the client. Default is
"/etc/pki/rpm-gpg".
-* ``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.
+* ``version``: Set the version attribute used when binding
+ Packages. Default is ``auto``.
[yum] section
-------------
diff --git a/doc/server/plugins/probes/group.txt b/doc/server/plugins/probes/group.txt
index 5c4d6ecb1..03c13db42 100644
--- a/doc/server/plugins/probes/group.txt
+++ b/doc/server/plugins/probes/group.txt
@@ -52,7 +52,7 @@ Probe used to dynamically set client groups based on OS/distro.
# redhat based
if [ -x /bin/rpm ]; then
OUTPUT="${OUTPUT}\ngroup:rpm"
- OS_GROUP=`/bin/rpm -q --qf "%{NAME}" --whatprovides redhat-release | sed 's/-release.*//' | tr '[A-Z]' '[a-z]'`
+ OS_GROUP=`/bin/rpm -q --qf "%{NAME}" --whatprovides redhat-release | grep -vi 'freeing read locks for locker' | sed 's/-release.*//' | tr '[A-Z]' '[a-z]'`
REDHAT_VERSION=`/bin/rpm -q --qf "%{VERSION}" --whatprovides redhat-release`
case "$OS_GROUP" in
"centos" | "fedora" | "sl")
diff --git a/examples/TemplateHelper/include.py b/examples/TemplateHelper/include.py
new file mode 100644
index 000000000..5fba75558
--- /dev/null
+++ b/examples/TemplateHelper/include.py
@@ -0,0 +1,97 @@
+"""IncludeHelper makes it easier to include group- and host-specific files in a template.
+
+Synopsis:
+
+ {% python
+ import os
+ include = metadata.TemplateHelper['include'].IncludeHelper
+ custom = include(metadata, path).files(os.path.basename(name))
+ %}\
+ {% for file in custom %}\
+
+ ########## Start ${include.specificity(file)} ##########
+ {% include ${file} %}
+ ########## End ${include.specificity(file)} ##########
+ {% end %}\
+
+This would let you include files with the same base name; e.g. in a
+template for ''foo.conf'', the include files would be called
+''foo.conf.G_<group>.genshi_include''. If a template needs to include
+different files in different places, you can do that like so:
+
+ inc = metadata.TemplateHelper['include'].IncludeHelper(metadata, path)
+ custom_bar = inc.files("bar")
+ custom_baz = inc.files("baz")
+
+This would result in two different sets of custom files being used,
+one drawn from ''bar.conf.G_<group>.genshi_include'' and the other
+from ''baz.conf.G_<group>.genshi_include''.
+
+==== Methods ====
+
+
+=== files ===
+
+Usage:
+
+
+
+"""
+
+import os
+import re
+import Bcfg2.Options
+
+__export__ = ["IncludeHelper"]
+
+class IncludeHelper (object):
+ def __init__(self, metadata, path):
+ """ Constructor.
+
+ The template path can be found in the ''path'' variable that is set for all Genshi templates."""
+ self.metadata = metadata
+ self.path = path
+
+ def _get_basedir(self):
+ setup = Bcfg2.Options.OptionParser({'repo':
+ Bcfg2.Options.SERVER_REPOSITORY})
+ setup.parse('--')
+ return os.path.join(setup['repo'], os.path.dirname(self.path))
+
+ def files(self, fname):
+ """ Return a list of files to include for this host. Files
+ are found in the template directory based on the following
+ patterns:
+
+ * ''<prefix>.H_<hostname>.genshi_include'': Host-specific files
+ * ''<prefix>.G_<group>.genshi_include'': Group-specific files
+
+ Note that there is no numeric priority on the group-specific
+ files. All matching files are returned by
+ ''IncludeHelper.files()''. """
+ files = []
+ hostfile = os.path.join(self._get_basedir(),
+ "%s.H_%s.genshi_include" %
+ (fname, self.metadata.hostname))
+ if os.path.isfile(hostfile):
+ files.append(hostfile)
+
+ for group in self.metadata.groups:
+ filename = os.path.join(self._get_basedir(),
+ "%s.G_%s.genshi_include" % (fname, group))
+ if os.path.isfile(filename):
+ files.append(filename)
+
+ return sorted(files)
+
+ @staticmethod
+ def specificity(fname):
+ """ Get a string describing the specificity of the given file """
+ match = re.search(r'(G|H)_(.*)\.genshi_include', fname)
+ if match:
+ if match.group(1) == "G":
+ stype = "group"
+ else:
+ stype = "host"
+ return "%s-specific configs for %s" % (stype, match.group(2))
+ return "Unknown specificity"
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 c727de948..86c277a25 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 02420fe56..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
@@ -117,7 +118,7 @@ Configuration management system documentation
%{__perl} -pi -e 's@chkconfig: (\d+)@chkconfig: -@' debian/bcfg2-server.init
# get rid of extraneous shebangs
-for f in `find src/lib -name \*.py`
+for f in `find src/lib/Bcfg2 -name \*.py`
do
%{__sed} -i -e '/^#!/,1d' $f
done
diff --git a/schemas/base.xsd b/schemas/base.xsd
index 91b7ac8f5..cca665b38 100644
--- a/schemas/base.xsd
+++ b/schemas/base.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
base schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
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 0a9ce5202..56f458a45 100644
--- a/schemas/clients.xsd
+++ b/schemas/clients.xsd
@@ -1,19 +1,21 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
-
+
<xsd:annotation>
<xsd:documentation>
client schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</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>
@@ -35,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/decisions.xsd b/schemas/decisions.xsd
index a354ec8cb..30115b367 100644
--- a/schemas/decisions.xsd
+++ b/schemas/decisions.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
decision list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/defaults.xsd b/schemas/defaults.xsd
index 27e749470..c7e2edc7e 100644
--- a/schemas/defaults.xsd
+++ b/schemas/defaults.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/genshi.xsd b/schemas/genshi.xsd
index 088ec56be..35d81e2f1 100644
--- a/schemas/genshi.xsd
+++ b/schemas/genshi.xsd
@@ -8,7 +8,6 @@
<xs:documentation>
Genshi schema
Chris St. Pierre
- $Id$
</xs:documentation>
</xs:annotation>
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/metadata.xsd b/schemas/metadata.xsd
index 58f9e8029..f79039d25 100644
--- a/schemas/metadata.xsd
+++ b/schemas/metadata.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
metadata schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
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/pathentry.xsd b/schemas/pathentry.xsd
index 315b0082e..080758d0b 100644
--- a/schemas/pathentry.xsd
+++ b/schemas/pathentry.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
path entry schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/pkglist.xsd b/schemas/pkglist.xsd
index c16ed654e..c0d449f54 100644
--- a/schemas/pkglist.xsd
+++ b/schemas/pkglist.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
package list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index 3655c646b..0aaea0c22 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
package list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
@@ -51,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/schemas/rules.xsd b/schemas/rules.xsd
index 06c3b38ac..924792b18 100644
--- a/schemas/rules.xsd
+++ b/schemas/rules.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/services.xsd b/schemas/services.xsd
index 828959e82..b91e851d2 100644
--- a/schemas/services.xsd
+++ b/schemas/services.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
services schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/servicetype.xsd b/schemas/servicetype.xsd
index 6b497383d..af5bc64a6 100644
--- a/schemas/servicetype.xsd
+++ b/schemas/servicetype.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
services schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/types.xsd b/schemas/types.xsd
index 689e693b7..ead377192 100644
--- a/schemas/types.xsd
+++ b/schemas/types.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/setup.py b/setup.py
index 69c67c0b1..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/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'},
- 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/Server/Hostbase/hostbase/webtemplates/*.*')),
- ('share/bcfg2/Hostbase/templates/hostbase',
- glob('src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/*')),
- ('share/bcfg2/Hostbase/repo',
- glob('src/lib/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/Bcfg2Py3k.py b/src/lib/Bcfg2/Bcfg2Py3k.py
index ee05b7e41..6af8b3e5c 100644
--- a/src/lib/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/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py
index d17f70f1b..9ad669ad6 100644
--- a/src/lib/Client/Frame.py
+++ b/src/lib/Bcfg2/Client/Frame.py
@@ -2,7 +2,6 @@
Frame is the Client Framework that verifies and
installs entries, and generates statistics.
"""
-__revision__ = '$Revision$'
import logging
import sys
@@ -292,7 +291,7 @@ class Frame:
if self.setup['interactive']:
promptFilter(prompt, actions)
self.DispatchInstallCalls(actions)
-
+
# need to test to fail entries in whitelist
if False in [self.states[a] for a in actions]:
# then display bundles forced off with entries
@@ -427,7 +426,6 @@ class Frame:
stats = Bcfg2.Client.XML.SubElement(feedback,
'Statistics',
total=str(len(self.states)),
- client_version=__revision__,
version='2.0',
revision=self.config.get('revision', '-1'))
good = len([key for key, val in list(self.states.items()) if val])
diff --git a/src/lib/Client/Tools/APK.py b/src/lib/Bcfg2/Client/Tools/APK.py
index 6a6fd51b3..aaaf2472f 100644
--- a/src/lib/Client/Tools/APK.py
+++ b/src/lib/Bcfg2/Client/Tools/APK.py
@@ -1,5 +1,4 @@
"""This provides Bcfg2 support for Alpine Linux APK packages."""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Bcfg2/Client/Tools/APT.py
index db8cd56e7..6b839ffbc 100644
--- a/src/lib/Client/Tools/APT.py
+++ b/src/lib/Bcfg2/Client/Tools/APT.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 support for apt-get."""
-__revision__ = '$Revision$'
# suppress apt API warnings
import warnings
diff --git a/src/lib/Client/Tools/Action.py b/src/lib/Bcfg2/Client/Tools/Action.py
index c089cde1d..dc49347e9 100644
--- a/src/lib/Client/Tools/Action.py
+++ b/src/lib/Bcfg2/Client/Tools/Action.py
@@ -1,5 +1,4 @@
"""Action driver"""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
from Bcfg2.Client.Frame import matches_white_list, passes_black_list
diff --git a/src/lib/Client/Tools/Blast.py b/src/lib/Bcfg2/Client/Tools/Blast.py
index 29cdfa116..5d5e74ab2 100644
--- a/src/lib/Client/Tools/Blast.py
+++ b/src/lib/Bcfg2/Client/Tools/Blast.py
@@ -1,6 +1,4 @@
-# This is the bcfg2 support for blastwave packages (pkg-get)
"""This provides Bcfg2 support for Blastwave."""
-__revision__ = '$Revision$'
import tempfile
import Bcfg2.Client.Tools.SYSV
diff --git a/src/lib/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
index 17e8bf09b..12ea5f132 100644
--- a/src/lib/Client/Tools/Chkconfig.py
+++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
@@ -1,8 +1,6 @@
# This is the bcfg2 support for chkconfig
-# $Id$
"""This is chkconfig support."""
-__revision__ = '$Revision$'
import os
diff --git a/src/lib/Client/Tools/DebInit.py b/src/lib/Bcfg2/Client/Tools/DebInit.py
index 022332602..ca6fc439e 100644
--- a/src/lib/Client/Tools/DebInit.py
+++ b/src/lib/Bcfg2/Client/Tools/DebInit.py
@@ -1,5 +1,4 @@
"""Debian Init Support for Bcfg2"""
-__revision__ = '$Revision$'
import glob
import os
diff --git a/src/lib/Client/Tools/Encap.py b/src/lib/Bcfg2/Client/Tools/Encap.py
index 92062a750..fa09c3ec7 100644
--- a/src/lib/Client/Tools/Encap.py
+++ b/src/lib/Bcfg2/Client/Tools/Encap.py
@@ -1,7 +1,5 @@
"""Bcfg2 Support for Encap Packages"""
-__revision__ = '$Revision$'
-
import glob
import re
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/FreeBSDInit.py b/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py
index 10f0f2e93..10f0f2e93 100644
--- a/src/lib/Client/Tools/FreeBSDInit.py
+++ b/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py
diff --git a/src/lib/Client/Tools/FreeBSDPackage.py b/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py
index 04c05adaa..3e6f2b6bb 100644
--- a/src/lib/Client/Tools/FreeBSDPackage.py
+++ b/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 tool for the FreeBSD package system."""
-__revision__ = '$Rev$'
# TODO
# - actual package installation
diff --git a/src/lib/Client/Tools/IPS.py b/src/lib/Bcfg2/Client/Tools/IPS.py
index 9afd23143..e30bbd2a4 100644
--- a/src/lib/Client/Tools/IPS.py
+++ b/src/lib/Bcfg2/Client/Tools/IPS.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 support for OpenSolaris packages."""
-__revision__ = '$Revision$'
import pkg.client.image as image
import pkg.client.progress as progress
diff --git a/src/lib/Client/Tools/MacPorts.py b/src/lib/Bcfg2/Client/Tools/MacPorts.py
index 2a7ba9eb9..9724fab57 100644
--- a/src/lib/Client/Tools/MacPorts.py
+++ b/src/lib/Bcfg2/Client/Tools/MacPorts.py
@@ -1,5 +1,4 @@
"""This provides Bcfg2 support for macports packages."""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Bcfg2/Client/Tools/POSIX.py
index 3591c33ad..9dd0362f3 100644
--- a/src/lib/Client/Tools/POSIX.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX.py
@@ -1,5 +1,4 @@
"""All POSIX Type client support for Bcfg2."""
-__revision__ = '$Revision$'
import binascii
from datetime import datetime
@@ -496,7 +495,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
(err.filename, err))
return False
different = content != tempdata
-
+
if different:
if self.setup['interactive']:
prompt = [entry.get('qtext', '')]
diff --git a/src/lib/Client/Tools/Pacman.py b/src/lib/Bcfg2/Client/Tools/Pacman.py
index c8c05061c..c8c05061c 100644
--- a/src/lib/Client/Tools/Pacman.py
+++ b/src/lib/Bcfg2/Client/Tools/Pacman.py
diff --git a/src/lib/Bcfg2/Client/Tools/Portage.py b/src/lib/Bcfg2/Client/Tools/Portage.py
new file mode 100644
index 000000000..4516f419d
--- /dev/null
+++ b/src/lib/Bcfg2/Client/Tools/Portage.py
@@ -0,0 +1,125 @@
+"""This is the Bcfg2 tool for the Gentoo Portage system."""
+
+import re
+import Bcfg2.Client.Tools
+from Bcfg2.Bcfg2Py3k import ConfigParser
+
+
+class Portage(Bcfg2.Client.Tools.PkgTool):
+ """The Gentoo toolset implements package and service operations and
+ inherits the rest from Toolset.Toolset."""
+ name = 'Portage'
+ __execs__ = ['/usr/bin/emerge', '/usr/bin/equery']
+ __handles__ = [('Package', 'ebuild')]
+ __req__ = {'Package': ['name', 'version']}
+ pkgtype = 'ebuild'
+ # requires a working PORTAGE_BINHOST in make.conf
+ _binpkgtool = ('emerge --getbinpkgonly %s', ('=%s-%s', \
+ ['name', 'version']))
+ pkgtool = ('emerge %s', ('=%s-%s', ['name', 'version']))
+
+ def __init__(self, logger, cfg, setup):
+ self._initialised = False
+ Bcfg2.Client.Tools.PkgTool.__init__(self, logger, cfg, setup)
+ self._initialised = True
+ self.__important__ = self.__important__ + ['/etc/make.conf']
+ self._pkg_pattern = re.compile('(.*)-(\d.*)')
+ self._ebuild_pattern = re.compile('(ebuild|binary)')
+ self.cfg = cfg
+ self.installed = {}
+ self._binpkgonly = True
+
+ # Used to get options from configuration file
+ parser = ConfigParser.ConfigParser()
+ parser.read(self.setup.get('setup'))
+ for opt in ['binpkgonly']:
+ if parser.has_option(self.name, opt):
+ setattr(self, ('_%s' % opt),
+ self._StrToBoolIfBool(parser.get(self.name, opt)))
+
+ if self._binpkgonly:
+ self.pkgtool = self._binpkgtool
+ self.RefreshPackages()
+
+ def _StrToBoolIfBool(self, s):
+ """Returns a boolean if the string specifies a boolean value.
+ Returns a string otherwise"""
+ if s.lower() in ('true', 'yes', 't', 'y', '1'):
+ return True
+ elif s.lower() in ('false', 'no', 'f', 'n', '0'):
+ return False
+ else:
+ return s
+
+ def RefreshPackages(self):
+ """Refresh memory hashes of packages."""
+ if not self._initialised:
+ return
+ self.logger.info('Getting list of installed packages')
+ cache = self.cmd.run("equery -q list '*'")[1]
+ self.installed = {}
+ for pkg in cache:
+ if self._pkg_pattern.match(pkg):
+ name = self._pkg_pattern.match(pkg).group(1)
+ version = self._pkg_pattern.match(pkg).group(2)
+ self.installed[name] = version
+ else:
+ self.logger.info("Failed to parse pkg name %s" % pkg)
+
+ def VerifyPackage(self, entry, modlist):
+ """Verify package for entry."""
+ if not 'version' in entry.attrib:
+ self.logger.info("Cannot verify unversioned package %s" %
+ (entry.get('name')))
+ return False
+
+ if not (entry.get('name') in self.installed):
+ # Can't verify package that isn't installed
+ entry.set('current_exists', 'false')
+ return False
+
+ # get the installed version
+ version = self.installed[entry.get('name')]
+ entry.set('current_version', version)
+
+ if not self.setup['quick']:
+ if ('verify' not in entry.attrib) or \
+ self._StrToBoolIfBool(entry.get('verify')):
+
+ # Check the package if:
+ # - Not running in quick mode
+ # - No verify option is specified in the literal configuration
+ # OR
+ # - Verify option is specified and is true
+
+ self.logger.debug('Running equery check on %s' %
+ entry.get('name'))
+ output = self.cmd.run("/usr/bin/equery -N check '=%s-%s' "
+ "2>&1 | grep '!!!' | awk '{print $2}'"
+ % ((entry.get('name'), version)))[1]
+ if [filename for filename in output \
+ if filename not in modlist]:
+ return False
+
+ # By now the package must be in one of the following states:
+ # - Not require checking
+ # - Have no files modified at all
+ # - Have modified files in the modlist only
+ if self.installed[entry.get('name')] == version:
+ # Specified package version is installed
+ # Specified package version may be any in literal configuration
+ return True
+
+ # Something got skipped. Indicates a bug
+ return False
+
+ def RemovePackages(self, packages):
+ """Deal with extra configuration detected."""
+ pkgnames = " ".join([pkg.get('name') for pkg in packages])
+ if len(packages) > 0:
+ self.logger.info('Removing packages:')
+ self.logger.info(pkgnames)
+ self.cmd.run("emerge --unmerge --quiet %s" %
+ " ".join(pkgnames.split(' ')))
+ self.RefreshPackages()
+ self.extra = self.FindExtraPackages()
diff --git a/src/lib/Client/Tools/RPMng.py b/src/lib/Bcfg2/Client/Tools/RPMng.py
index 5376118c2..00dd00d71 100644
--- a/src/lib/Client/Tools/RPMng.py
+++ b/src/lib/Bcfg2/Client/Tools/RPMng.py
@@ -1,7 +1,5 @@
"""Bcfg2 Support for RPMS"""
-__revision__ = '$Revision$'
-
import os.path
import rpm
import rpmtools
@@ -9,12 +7,6 @@ import Bcfg2.Client.Tools
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
class RPMng(Bcfg2.Client.Tools.PkgTool):
"""Support for RPM packages."""
name = 'RPMng'
@@ -461,7 +453,7 @@ class RPMng(Bcfg2.Client.Tools.PkgTool):
packages is a list of Package Entries with Instances generated
by FindExtraPackages().
-
+
"""
self.logger.debug('Running RPMng.RemovePackages()')
diff --git a/src/lib/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
index d832d98a8..1b9a29478 100644
--- a/src/lib/Client/Tools/RcUpdate.py
+++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
@@ -1,5 +1,4 @@
"""This is rc-update support."""
-__revision__ = '$Revision$'
import os
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/SMF.py b/src/lib/Bcfg2/Client/Tools/SMF.py
index 944408326..f824410ad 100644
--- a/src/lib/Client/Tools/SMF.py
+++ b/src/lib/Bcfg2/Client/Tools/SMF.py
@@ -1,5 +1,4 @@
"""SMF support for Bcfg2"""
-__revision__ = '$Revision$'
import glob
import os
diff --git a/src/lib/Client/Tools/SYSV.py b/src/lib/Bcfg2/Client/Tools/SYSV.py
index b5e1f1c59..eb4a13dfb 100644
--- a/src/lib/Client/Tools/SYSV.py
+++ b/src/lib/Bcfg2/Client/Tools/SYSV.py
@@ -1,6 +1,4 @@
-# This is the bcfg2 support for solaris sysv packages
"""This provides bcfg2 support for Solaris SYSV packages."""
-__revision__ = '$Revision$'
import tempfile
diff --git a/src/lib/Client/Tools/Systemd.py b/src/lib/Bcfg2/Client/Tools/Systemd.py
index e3f6a4169..e3f6a4169 100644
--- a/src/lib/Client/Tools/Systemd.py
+++ b/src/lib/Bcfg2/Client/Tools/Systemd.py
diff --git a/src/lib/Client/Tools/Upstart.py b/src/lib/Bcfg2/Client/Tools/Upstart.py
index 41a585c23..7afc8edd7 100644
--- a/src/lib/Client/Tools/Upstart.py
+++ b/src/lib/Bcfg2/Client/Tools/Upstart.py
@@ -1,5 +1,4 @@
"""Upstart support for Bcfg2."""
-__revision__ = '$Revision$'
import glob
import re
diff --git a/src/lib/Client/Tools/VCS.py b/src/lib/Bcfg2/Client/Tools/VCS.py
index e6081dc1c..e6081dc1c 100644
--- a/src/lib/Client/Tools/VCS.py
+++ b/src/lib/Bcfg2/Client/Tools/VCS.py
diff --git a/src/lib/Client/Tools/YUM24.py b/src/lib/Bcfg2/Client/Tools/YUM24.py
index 66768fb34..4e488b9da 100644
--- a/src/lib/Client/Tools/YUM24.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM24.py
@@ -1,5 +1,4 @@
"""This provides bcfg2 support for yum."""
-__revision__ = '$Revision: $'
import copy
import os.path
@@ -10,12 +9,6 @@ import Bcfg2.Client.Tools.RPMng
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
YAD = True
CP = ConfigParser.ConfigParser()
try:
diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Bcfg2/Client/Tools/YUMng.py
index 9b999df92..244b66cf4 100644
--- a/src/lib/Client/Tools/YUMng.py
+++ b/src/lib/Bcfg2/Client/Tools/YUMng.py
@@ -1,5 +1,4 @@
"""This provides bcfg2 support for yum."""
-__revision__ = '$Revision$'
import copy
import os.path
@@ -16,12 +15,6 @@ import Bcfg2.Client.Tools
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
def build_yname(pkgname, inst):
"""Build yum appropriate package name."""
@@ -760,7 +753,8 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
else:
self.logger.error("Second pass yum install failed.")
self.logger.debug(" %s" % restring)
- except yum.Errors.YumBaseError, e:
+ except yum.Errors.YumBaseError:
+ e = sys.exc_info()[1]
self.logger.error("Yum transaction error: %s" % str(e))
self.yb.conf.skip_broken = skipBroken
diff --git a/src/lib/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index e879d7dd6..c6cb6e239 100644
--- a/src/lib/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -1,8 +1,4 @@
"""This contains all Bcfg2 Tool modules"""
-# suppress popen2 warnings for python 2.3
-import warnings
-warnings.filterwarnings("ignore", "The popen2 module is deprecated.*",
- DeprecationWarning)
import os
import stat
import sys
@@ -10,7 +6,6 @@ from subprocess import Popen, PIPE
import time
import Bcfg2.Client.XML
-__revision__ = '$Revision$'
__all__ = [tool.split('.')[0] \
for tool in os.listdir(os.path.dirname(__file__)) \
diff --git a/src/lib/Client/Tools/launchd.py b/src/lib/Bcfg2/Client/Tools/launchd.py
index d7cbfa07f..c022d32ae 100644
--- a/src/lib/Client/Tools/launchd.py
+++ b/src/lib/Bcfg2/Client/Tools/launchd.py
@@ -1,5 +1,4 @@
"""launchd support for Bcfg2."""
-__revision__ = '$Revision$'
import os
diff --git a/src/lib/Client/Tools/rpmtools.py b/src/lib/Bcfg2/Client/Tools/rpmtools.py
index 3cd2b7014..7441b2c06 100755
--- a/src/lib/Client/Tools/rpmtools.py
+++ b/src/lib/Bcfg2/Client/Tools/rpmtools.py
@@ -18,7 +18,6 @@
Run 'rpmtools' -h for the options.
"""
-__revision__ = '$Revision$'
import grp
import optparse
diff --git a/src/lib/Client/XML.py b/src/lib/Bcfg2/Client/XML.py
index 42b1017ac..858479611 100644
--- a/src/lib/Client/XML.py
+++ b/src/lib/Bcfg2/Client/XML.py
@@ -1,5 +1,4 @@
'''XML lib compatibility layer for the Bcfg2 client'''
-__revision__ = '$Revision$'
# library will use lxml, then builtin xml.etree, then ElementTree
diff --git a/src/lib/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py
index ea60a4259..6ed37b257 100644
--- a/src/lib/Client/__init__.py
+++ b/src/lib/Bcfg2/Client/__init__.py
@@ -1,4 +1,3 @@
"""This contains all Bcfg2 Client modules"""
-__revision__ = '$Revision$'
__all__ = ["Frame", "Tools", "XML"]
diff --git a/src/lib/Component.py b/src/lib/Bcfg2/Component.py
index caea3eda9..eb9ea166a 100644
--- a/src/lib/Component.py
+++ b/src/lib/Bcfg2/Component.py
@@ -1,7 +1,5 @@
"""Cobalt component base."""
-__revision__ = '$Revision$'
-
__all__ = ["Component", "exposed", "automatic", "run_component"]
import inspect
@@ -16,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()
@@ -57,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/Logger.py b/src/lib/Bcfg2/Logger.py
index e8f9ecd47..06aae615e 100644
--- a/src/lib/Logger.py
+++ b/src/lib/Bcfg2/Logger.py
@@ -1,5 +1,4 @@
"""Bcfg2 logging support"""
-__revision__ = '$Revision$'
import copy
import fcntl
diff --git a/src/lib/Options.py b/src/lib/Bcfg2/Options.py
index 2f535397f..f6273924a 100644
--- a/src/lib/Options.py
+++ b/src/lib/Bcfg2/Options.py
@@ -1,9 +1,9 @@
"""Option parsing library for utilities."""
-__revision__ = '$Revision$'
import getopt
import os
import sys
+import shlex
import Bcfg2.Client.Tools
# Compatibility imports
from Bcfg2.Bcfg2Py3k import ConfigParser
@@ -134,7 +134,12 @@ class OptionSet(dict):
def buildHelpMessage(self):
if hasattr(self, 'hm'):
return self.hm
- return ' '.join([opt.buildHelpMessage() for opt in list(self.values())])
+ hlist = [] # list of _non-empty_ help messages
+ for opt in list(self.values()):
+ hm = opt.buildHelpMessage()
+ if hm != '':
+ hlist.append(hm)
+ return ' '.join(hlist)
def helpExit(self, msg='', code=1):
if msg:
@@ -348,7 +353,16 @@ CLIENT_SERVICE_MODE = Option('Set client service mode', default='default',
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/Proxy.py b/src/lib/Bcfg2/Proxy.py
index 977a49228..2e653f47e 100644
--- a/src/lib/Proxy.py
+++ b/src/lib/Bcfg2/Proxy.py
@@ -8,9 +8,6 @@ load_config -- read configuration files
"""
-__revision__ = '$Revision: $'
-
-
import logging
import re
import socket
@@ -315,7 +312,7 @@ class XMLRPCTransport(xmlrpclib.Transport):
408,
str(err),
self._extra_headers))
-
+
if errcode != 200:
raise ProxyError(xmlrpclib.ProtocolError(host + handler,
errcode,
diff --git a/src/lib/SSLServer.py b/src/lib/Bcfg2/SSLServer.py
index 32ab9933b..418e259cc 100644
--- a/src/lib/SSLServer.py
+++ b/src/lib/Bcfg2/SSLServer.py
@@ -1,7 +1,5 @@
"""Bcfg2 SSL server."""
-__revision__ = '$Revision$'
-
__all__ = [
"SSLServer", "XMLRPCRequestHandler", "XMLRPCServer",
]
diff --git a/src/lib/Server/Admin/Backup.py b/src/lib/Bcfg2/Server/Admin/Backup.py
index 3744abca3..3744abca3 100644
--- a/src/lib/Server/Admin/Backup.py
+++ b/src/lib/Bcfg2/Server/Admin/Backup.py
diff --git a/src/lib/Server/Admin/Bundle.py b/src/lib/Bcfg2/Server/Admin/Bundle.py
index 89c099602..89c099602 100644
--- a/src/lib/Server/Admin/Bundle.py
+++ b/src/lib/Bcfg2/Server/Admin/Bundle.py
diff --git a/src/lib/Server/Admin/Client.py b/src/lib/Bcfg2/Server/Admin/Client.py
index 4d580c54c..4d580c54c 100644
--- a/src/lib/Server/Admin/Client.py
+++ b/src/lib/Bcfg2/Server/Admin/Client.py
diff --git a/src/lib/Server/Admin/Compare.py b/src/lib/Bcfg2/Server/Admin/Compare.py
index 050dd69f8..050dd69f8 100644
--- a/src/lib/Server/Admin/Compare.py
+++ b/src/lib/Bcfg2/Server/Admin/Compare.py
diff --git a/src/lib/Server/Admin/Group.py b/src/lib/Bcfg2/Server/Admin/Group.py
index 16a773d6f..16a773d6f 100644
--- a/src/lib/Server/Admin/Group.py
+++ b/src/lib/Bcfg2/Server/Admin/Group.py
diff --git a/src/lib/Server/Admin/Init.py b/src/lib/Bcfg2/Server/Admin/Init.py
index 832190b7d..832190b7d 100644
--- a/src/lib/Server/Admin/Init.py
+++ b/src/lib/Bcfg2/Server/Admin/Init.py
diff --git a/src/lib/Server/Admin/Minestruct.py b/src/lib/Bcfg2/Server/Admin/Minestruct.py
index b929a9a8c..b929a9a8c 100644
--- a/src/lib/Server/Admin/Minestruct.py
+++ b/src/lib/Bcfg2/Server/Admin/Minestruct.py
diff --git a/src/lib/Server/Admin/Perf.py b/src/lib/Bcfg2/Server/Admin/Perf.py
index 411442698..411442698 100644
--- a/src/lib/Server/Admin/Perf.py
+++ b/src/lib/Bcfg2/Server/Admin/Perf.py
diff --git a/src/lib/Server/Admin/Pull.py b/src/lib/Bcfg2/Server/Admin/Pull.py
index daf353107..daf353107 100644
--- a/src/lib/Server/Admin/Pull.py
+++ b/src/lib/Bcfg2/Server/Admin/Pull.py
diff --git a/src/lib/Server/Admin/Query.py b/src/lib/Bcfg2/Server/Admin/Query.py
index 3dd326645..3dd326645 100644
--- a/src/lib/Server/Admin/Query.py
+++ b/src/lib/Bcfg2/Server/Admin/Query.py
diff --git a/src/lib/Server/Admin/Reports.py b/src/lib/Bcfg2/Server/Admin/Reports.py
index 974cdff9d..974cdff9d 100644
--- a/src/lib/Server/Admin/Reports.py
+++ b/src/lib/Bcfg2/Server/Admin/Reports.py
diff --git a/src/lib/Server/Admin/Snapshots.py b/src/lib/Bcfg2/Server/Admin/Snapshots.py
index 8bc56f1f1..8bc56f1f1 100644
--- a/src/lib/Server/Admin/Snapshots.py
+++ b/src/lib/Bcfg2/Server/Admin/Snapshots.py
diff --git a/src/lib/Server/Admin/Tidy.py b/src/lib/Bcfg2/Server/Admin/Tidy.py
index 82319b93e..82319b93e 100644
--- a/src/lib/Server/Admin/Tidy.py
+++ b/src/lib/Bcfg2/Server/Admin/Tidy.py
diff --git a/src/lib/Server/Admin/Viz.py b/src/lib/Bcfg2/Server/Admin/Viz.py
index 2faa423c1..2faa423c1 100644
--- a/src/lib/Server/Admin/Viz.py
+++ b/src/lib/Bcfg2/Server/Admin/Viz.py
diff --git a/src/lib/Server/Admin/Xcmd.py b/src/lib/Bcfg2/Server/Admin/Xcmd.py
index 140465468..140465468 100644
--- a/src/lib/Server/Admin/Xcmd.py
+++ b/src/lib/Bcfg2/Server/Admin/Xcmd.py
diff --git a/src/lib/Server/Admin/__init__.py b/src/lib/Bcfg2/Server/Admin/__init__.py
index a9f0b8cd6..fdb9a0972 100644
--- a/src/lib/Server/Admin/__init__.py
+++ b/src/lib/Bcfg2/Server/Admin/__init__.py
@@ -1,5 +1,3 @@
-__revision__ = '$Revision$'
-
__all__ = [
'Backup',
'Bundle',
@@ -108,7 +106,7 @@ class MetadataCore(Mode):
"""Base class for admin-modes that handle metadata."""
__plugin_whitelist__ = None
__plugin_blacklist__ = None
-
+
def __init__(self, setup):
Mode.__init__(self, setup)
if self.__plugin_whitelist__ is not None:
diff --git a/src/lib/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 38f854f66..112c484b1 100644
--- a/src/lib/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -1,5 +1,4 @@
"""Bcfg2.Server.Core provides the runtime support for Bcfg2 modules."""
-__revision__ = '$Revision$'
import atexit
import logging
@@ -7,6 +6,8 @@ import select
import sys
import threading
import time
+from traceback import format_exc
+
try:
import lxml.etree
except ImportError:
@@ -240,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)
@@ -323,7 +324,7 @@ class Core(Component):
except:
logger.error("error in BindStructure", exc_info=1)
self.validate_goals(meta, config)
-
+
sort_xml(config, key=lambda e: e.get('name'))
logger.info("Generated config for %s in %.03f seconds" % \
diff --git a/src/lib/Server/FileMonitor.py b/src/lib/Bcfg2/Server/FileMonitor.py
index d6b313e6b..d6b313e6b 100644
--- a/src/lib/Server/FileMonitor.py
+++ b/src/lib/Bcfg2/Server/FileMonitor.py
diff --git a/src/lib/Server/Hostbase/.gitignore b/src/lib/Bcfg2/Server/Hostbase/.gitignore
index 8e15b5395..8e15b5395 100644
--- a/src/lib/Server/Hostbase/.gitignore
+++ b/src/lib/Bcfg2/Server/Hostbase/.gitignore
diff --git a/src/lib/Server/Hostbase/__init__.py b/src/lib/Bcfg2/Server/Hostbase/__init__.py
index e69de29bb..e69de29bb 100644
--- a/src/lib/Server/Hostbase/__init__.py
+++ b/src/lib/Bcfg2/Server/Hostbase/__init__.py
diff --git a/src/lib/Server/Hostbase/backends.py b/src/lib/Bcfg2/Server/Hostbase/backends.py
index bf774f695..ecaf3c109 100644
--- a/src/lib/Server/Hostbase/backends.py
+++ b/src/lib/Bcfg2/Server/Hostbase/backends.py
@@ -2,8 +2,6 @@ from django.contrib.auth.models import User
#from ldapauth import *
from nisauth import *
-__revision__ = '$Revision$'
-
## class LDAPBackend(object):
## def authenticate(self,username=None,password=None):
diff --git a/src/lib/Server/Hostbase/hostbase/__init__.py b/src/lib/Bcfg2/Server/Hostbase/hostbase/__init__.py
index e69de29bb..e69de29bb 100644
--- a/src/lib/Server/Hostbase/hostbase/__init__.py
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/__init__.py
diff --git a/src/lib/Server/Hostbase/hostbase/admin.py b/src/lib/Bcfg2/Server/Hostbase/hostbase/admin.py
index 70a2233cc..70a2233cc 100644
--- a/src/lib/Server/Hostbase/hostbase/admin.py
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/admin.py
diff --git a/src/lib/Server/Hostbase/hostbase/models.py b/src/lib/Bcfg2/Server/Hostbase/hostbase/models.py
index 3f08a09a0..3f08a09a0 100644
--- a/src/lib/Server/Hostbase/hostbase/models.py
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/models.py
diff --git a/src/lib/Server/Hostbase/hostbase/sql/zone.sql b/src/lib/Bcfg2/Server/Hostbase/hostbase/sql/zone.sql
index b78187ab2..b78187ab2 100644
--- a/src/lib/Server/Hostbase/hostbase/sql/zone.sql
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/sql/zone.sql
diff --git a/src/lib/Server/Hostbase/hostbase/urls.py b/src/lib/Bcfg2/Server/Hostbase/hostbase/urls.py
index 0ee204abe..0ee204abe 100644
--- a/src/lib/Server/Hostbase/hostbase/urls.py
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/urls.py
diff --git a/src/lib/Server/Hostbase/hostbase/views.py b/src/lib/Bcfg2/Server/Hostbase/hostbase/views.py
index ff1d4710d..57ef5eff8 100644
--- a/src/lib/Server/Hostbase/hostbase/views.py
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/views.py
@@ -2,8 +2,6 @@
Contains all the views associated with the hostbase app
Also has does form validation
"""
-__revision__ = "$Revision: $"
-
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/base.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/base.html
index 1d7c5565b..1d7c5565b 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/base.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/base.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/confirm.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/confirm.html
index ca8b0cc07..ca8b0cc07 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/confirm.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/confirm.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/copy.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/copy.html
index 400ef58f2..400ef58f2 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/copy.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/copy.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/dns.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dns.html
index da179e5a1..da179e5a1 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/dns.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dns.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/dnsedit.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dnsedit.html
index 18bd7ab83..b1b71ab67 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/dnsedit.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/dnsedit.html
@@ -73,7 +73,7 @@
<td> <input name="{{ name.id }}priority" type="text" size="6">
<input name="{{ name.id }}mx" type="text"></td></tr>
{% endfor %}
- <tr> <td> <b>name</b></td>
+ <tr> <td> <b>name</b></td>
<td> <input name="{{ ip.0.ip_addr }}name" type="text">
<select name="{{ ip.0.ip_addr }}dns_view">
{% for choice in DNS_CHOICES %}
@@ -86,7 +86,7 @@
<td> <input name="{{ ip.0.ip_addr }}priority" type="text" size="6">
<input name="{{ ip.0.ip_addr }}mx" type="text"></td></tr>
<tr><td></td></tr>
- <tr><td><hr></td><td><hr></td></tr>
+ <tr><td><hr></td><td><hr></td></tr>
{% endifequal %}
{% endfor %}
{% endfor %}
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/edit.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/edit.html
index 961c9d143..961c9d143 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/edit.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/edit.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/errors.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/errors.html
index e5429b86c..e5429b86c 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/errors.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/errors.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/host.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/host.html
index d6b8873bc..d6b8873bc 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/host.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/host.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html
index 551bf3254..b5d794b50 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/host_confirm_delete.html
@@ -24,7 +24,7 @@
<col width="150">
<col width="*">
<tr> <td> <b>hostname</b></td>
- <td> {{ object.hostname }}</td></tr>
+ <td> {{ object.hostname }}</td></tr>
<tr> <td> <b>whatami</b></td>
<td> {{ object.whatami }}</td></tr>
<tr> <td> <b>netgroup</b></td>
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html
index aa9679cbd..aa9679cbd 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/log_detail.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/index.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/index.html
index 92258b648..92258b648 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/index.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/index.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/login.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/login.html
index ec24a0fc0..ec24a0fc0 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/login.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/login.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/logout.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.html
index 994f631a8..994f631a8 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/logout.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/logout.tmpl b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.tmpl
index e71e90e76..e71e90e76 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/logout.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logout.tmpl
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/logviewer.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logviewer.html
index 806ccd63d..806ccd63d 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/logviewer.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/logviewer.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/navbar.tmpl b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/navbar.tmpl
index 877d427d0..877d427d0 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/navbar.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/navbar.tmpl
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/new.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/new.html
index 2dcd6271f..2dcd6271f 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/new.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/new.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/remove.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/remove.html
index 4329200dd..4329200dd 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/remove.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/remove.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/results.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/results.html
index 45b22058d..45b22058d 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/results.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/results.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/search.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/search.html
index 409d418fe..409d418fe 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/search.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/search.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/zoneedit.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneedit.html
index ee355ee87..ee355ee87 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/zoneedit.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneedit.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/zonenew.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zonenew.html
index b59fa9e3c..b59fa9e3c 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/zonenew.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zonenew.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/zones.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zones.html
index c773e7922..c773e7922 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/zones.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zones.html
diff --git a/src/lib/Server/Hostbase/hostbase/webtemplates/zoneview.html b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneview.html
index fa12e3ec5..fa12e3ec5 100644
--- a/src/lib/Server/Hostbase/hostbase/webtemplates/zoneview.html
+++ b/src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/zoneview.html
diff --git a/src/lib/Server/Hostbase/ldapauth.py b/src/lib/Bcfg2/Server/Hostbase/ldapauth.py
index f3db26f67..f3db26f67 100644
--- a/src/lib/Server/Hostbase/ldapauth.py
+++ b/src/lib/Bcfg2/Server/Hostbase/ldapauth.py
diff --git a/src/lib/Server/Hostbase/manage.py b/src/lib/Bcfg2/Server/Hostbase/manage.py
index 5e78ea979..5e78ea979 100755
--- a/src/lib/Server/Hostbase/manage.py
+++ b/src/lib/Bcfg2/Server/Hostbase/manage.py
diff --git a/src/lib/Server/Hostbase/media/base.css b/src/lib/Bcfg2/Server/Hostbase/media/base.css
index ddbf02165..ddbf02165 100644
--- a/src/lib/Server/Hostbase/media/base.css
+++ b/src/lib/Bcfg2/Server/Hostbase/media/base.css
diff --git a/src/lib/Server/Hostbase/media/boxypastel.css b/src/lib/Bcfg2/Server/Hostbase/media/boxypastel.css
index 7ae0684ef..7ae0684ef 100644
--- a/src/lib/Server/Hostbase/media/boxypastel.css
+++ b/src/lib/Bcfg2/Server/Hostbase/media/boxypastel.css
diff --git a/src/lib/Server/Hostbase/media/global.css b/src/lib/Bcfg2/Server/Hostbase/media/global.css
index 73451e1bc..73451e1bc 100644
--- a/src/lib/Server/Hostbase/media/global.css
+++ b/src/lib/Bcfg2/Server/Hostbase/media/global.css
diff --git a/src/lib/Server/Hostbase/media/layout.css b/src/lib/Bcfg2/Server/Hostbase/media/layout.css
index 9085cc220..9085cc220 100644
--- a/src/lib/Server/Hostbase/media/layout.css
+++ b/src/lib/Bcfg2/Server/Hostbase/media/layout.css
diff --git a/src/lib/Server/Hostbase/nisauth.py b/src/lib/Bcfg2/Server/Hostbase/nisauth.py
index 9c7da8c0a..ae4c6c021 100644
--- a/src/lib/Server/Hostbase/nisauth.py
+++ b/src/lib/Bcfg2/Server/Hostbase/nisauth.py
@@ -1,10 +1,8 @@
+"""Checks with NIS to see if the current user is in the support group"""
import os
import crypt, nis
from Bcfg2.Server.Hostbase.settings import AUTHORIZED_GROUP
-"""Checks with NIS to see if the current user is in the support group"""
-
-__revision__ = "$Revision: $"
class NISAUTHError(Exception):
"""NISAUTHError is raised when somehting goes boom."""
diff --git a/src/lib/Server/Hostbase/regex.py b/src/lib/Bcfg2/Server/Hostbase/regex.py
index 41cc0f6f0..41cc0f6f0 100644
--- a/src/lib/Server/Hostbase/regex.py
+++ b/src/lib/Bcfg2/Server/Hostbase/regex.py
diff --git a/src/lib/Server/Hostbase/settings.py b/src/lib/Bcfg2/Server/Hostbase/settings.py
index 4e641f13c..4e641f13c 100644
--- a/src/lib/Server/Hostbase/settings.py
+++ b/src/lib/Bcfg2/Server/Hostbase/settings.py
diff --git a/src/lib/Server/Hostbase/templates/batchadd.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/batchadd.tmpl
index 74ea3c047..74ea3c047 100644
--- a/src/lib/Server/Hostbase/templates/batchadd.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/batchadd.tmpl
diff --git a/src/lib/Server/Hostbase/templates/dhcpd.conf.head b/src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.conf.head
index a3d19547e..a3d19547e 100644
--- a/src/lib/Server/Hostbase/templates/dhcpd.conf.head
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.conf.head
diff --git a/src/lib/Server/Hostbase/templates/dhcpd.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.tmpl
index 757b263cd..757b263cd 100644
--- a/src/lib/Server/Hostbase/templates/dhcpd.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/dhcpd.tmpl
diff --git a/src/lib/Server/Hostbase/templates/hosts.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/hosts.tmpl
index 251cb5a79..251cb5a79 100644
--- a/src/lib/Server/Hostbase/templates/hosts.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/hosts.tmpl
diff --git a/src/lib/Server/Hostbase/templates/hostsappend.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/hostsappend.tmpl
index 00e0d5d04..00e0d5d04 100644
--- a/src/lib/Server/Hostbase/templates/hostsappend.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/hostsappend.tmpl
diff --git a/src/lib/Server/Hostbase/templates/named.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/named.tmpl
index 03e054198..03e054198 100644
--- a/src/lib/Server/Hostbase/templates/named.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/named.tmpl
diff --git a/src/lib/Server/Hostbase/templates/namedviews.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/namedviews.tmpl
index 52021620e..52021620e 100644
--- a/src/lib/Server/Hostbase/templates/namedviews.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/namedviews.tmpl
diff --git a/src/lib/Server/Hostbase/templates/reverseappend.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/reverseappend.tmpl
index 6ed520c98..6ed520c98 100644
--- a/src/lib/Server/Hostbase/templates/reverseappend.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/reverseappend.tmpl
diff --git a/src/lib/Server/Hostbase/templates/reversesoa.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/reversesoa.tmpl
index d142eaf7f..d142eaf7f 100644
--- a/src/lib/Server/Hostbase/templates/reversesoa.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/reversesoa.tmpl
diff --git a/src/lib/Server/Hostbase/templates/zone.tmpl b/src/lib/Bcfg2/Server/Hostbase/templates/zone.tmpl
index aad48d179..aad48d179 100644
--- a/src/lib/Server/Hostbase/templates/zone.tmpl
+++ b/src/lib/Bcfg2/Server/Hostbase/templates/zone.tmpl
diff --git a/src/lib/Server/Hostbase/test/harness.py b/src/lib/Bcfg2/Server/Hostbase/test/harness.py
index befcff5c0..befcff5c0 100644
--- a/src/lib/Server/Hostbase/test/harness.py
+++ b/src/lib/Bcfg2/Server/Hostbase/test/harness.py
diff --git a/src/lib/Server/Hostbase/test/test_environ_settings.py b/src/lib/Bcfg2/Server/Hostbase/test/test_environ_settings.py
index ad35c624e..ad35c624e 100644
--- a/src/lib/Server/Hostbase/test/test_environ_settings.py
+++ b/src/lib/Bcfg2/Server/Hostbase/test/test_environ_settings.py
diff --git a/src/lib/Server/Hostbase/test/test_ldapauth.py b/src/lib/Bcfg2/Server/Hostbase/test/test_ldapauth.py
index 7fc009ad2..7fc009ad2 100644
--- a/src/lib/Server/Hostbase/test/test_ldapauth.py
+++ b/src/lib/Bcfg2/Server/Hostbase/test/test_ldapauth.py
diff --git a/src/lib/Server/Hostbase/test/test_settings.py b/src/lib/Bcfg2/Server/Hostbase/test/test_settings.py
index 0dfc30f38..0dfc30f38 100644
--- a/src/lib/Server/Hostbase/test/test_settings.py
+++ b/src/lib/Bcfg2/Server/Hostbase/test/test_settings.py
diff --git a/src/lib/Server/Hostbase/urls.py b/src/lib/Bcfg2/Server/Hostbase/urls.py
index 01fe97d4f..01fe97d4f 100644
--- a/src/lib/Server/Hostbase/urls.py
+++ b/src/lib/Bcfg2/Server/Hostbase/urls.py
diff --git a/src/lib/Server/Lint/Bundles.py b/src/lib/Bcfg2/Server/Lint/Bundles.py
index 472915cfd..0c95f7e72 100644
--- a/src/lib/Server/Lint/Bundles.py
+++ b/src/lib/Bcfg2/Server/Lint/Bundles.py
@@ -1,6 +1,6 @@
import lxml.etree
import Bcfg2.Server.Lint
-
+
class Bundles(Bcfg2.Server.Lint.ServerPlugin):
""" Perform various bundle checks """
@@ -41,7 +41,7 @@ class Bundles(Bcfg2.Server.Lint.ServerPlugin):
except AttributeError:
# genshi template
xdata = lxml.etree.parse(bundle.template.filepath).getroot()
-
+
fname = bundle.name.split('Bundler/')[1].split('.')[0]
bname = xdata.get('name')
if fname != bname:
diff --git a/src/lib/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py
index 19fae1b08..19e6c1167 100644
--- a/src/lib/Server/Lint/Comments.py
+++ b/src/lib/Bcfg2/Server/Lint/Comments.py
@@ -31,13 +31,13 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
fetch the list of required items from the bcfg2-lint config """
if itype not in self.config_cache:
self.config_cache[itype] = {}
-
+
if rtype not in self.config_cache[itype]:
rv = []
global_item = "global_%ss" % itype
if global_item in self.config:
rv.extend(self.config[global_item].split(","))
-
+
item = "%s_%ss" % (rtype.lower(), itype)
if item in self.config:
if self.config[item]:
@@ -128,7 +128,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
# None == found but not expanded
# True == found and expanded
found = dict((k, False) for k in self.required_keywords(rtype))
-
+
for line in lines:
# we check for both '$<keyword>:' and '$<keyword>$' to see
# if the keyword just hasn't been expanded
@@ -155,7 +155,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
# next, check for required comments. found is just
# boolean
found = dict((k, False) for k in self.required_comments(rtype))
-
+
for line in lines:
for (comment, status) in found.items():
if not status:
diff --git a/src/lib/Server/Lint/Duplicates.py b/src/lib/Bcfg2/Server/Lint/Duplicates.py
index 75f620603..afddebb71 100644
--- a/src/lib/Server/Lint/Duplicates.py
+++ b/src/lib/Bcfg2/Server/Lint/Duplicates.py
@@ -30,7 +30,7 @@ class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
self.groups_xdata = self.metadata.clients_xml.xdata
if self.has_all_xincludes("clients.xml"):
self.clients_xdata = self.metadata.clients_xml.xdata
-
+
def duplicate_groups(self):
""" find duplicate groups """
self.duplicate_entries(self.clients_xdata.xpath('//Groups/Group'),
diff --git a/src/lib/Server/Lint/Genshi.py b/src/lib/Bcfg2/Server/Lint/Genshi.py
index 552c495b2..739e829d0 100755
--- a/src/lib/Server/Lint/Genshi.py
+++ b/src/lib/Bcfg2/Server/Lint/Genshi.py
@@ -1,6 +1,6 @@
import genshi.template
import Bcfg2.Server.Lint
-
+
class Genshi(Bcfg2.Server.Lint.ServerPlugin):
""" Check Genshi templates for syntax errors """
@@ -15,13 +15,14 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
def check_files(self, entries, loader=None):
if loader is None:
loader = genshi.template.TemplateLoader()
-
+
for eset in entries.values():
for fname, sdata in list(eset.entries.items()):
if fname.endswith(".genshi") or fname.endswith(".newtxt"):
try:
loader.load(sdata.name,
cls=genshi.template.NewTextTemplate)
- except genshi.template.TemplateSyntaxError, err:
+ except genshi.template.TemplateSyntaxError:
+ err = sys.exc_info()[1]
self.LintError("genshi-syntax-error",
"Genshi syntax error: %s" % err)
diff --git a/src/lib/Server/Lint/GroupPatterns.py b/src/lib/Bcfg2/Server/Lint/GroupPatterns.py
index b69d7a5d8..2e0dffde7 100644
--- a/src/lib/Server/Lint/GroupPatterns.py
+++ b/src/lib/Bcfg2/Server/Lint/GroupPatterns.py
@@ -1,7 +1,7 @@
import sys
import Bcfg2.Server.Lint
from Bcfg2.Server.Plugins.GroupPatterns import PatternMap
-
+
class GroupPatterns(Bcfg2.Server.Lint.ServerPlugin):
""" Check Genshi templates for syntax errors """
@@ -19,7 +19,7 @@ class GroupPatterns(Bcfg2.Server.Lint.ServerPlugin):
pmap = lambda p: PatternMap(p, None, groups)
else:
pmap = lambda p: PatternMap(None, p, groups)
-
+
for el in entry.findall(ptype):
pat = el.text
try:
diff --git a/src/lib/Server/Lint/InfoXML.py b/src/lib/Bcfg2/Server/Lint/InfoXML.py
index 2054e23bf..2054e23bf 100644
--- a/src/lib/Server/Lint/InfoXML.py
+++ b/src/lib/Bcfg2/Server/Lint/InfoXML.py
diff --git a/src/lib/Server/Lint/MergeFiles.py b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
index ff6e3449a..56bca7931 100644
--- a/src/lib/Server/Lint/MergeFiles.py
+++ b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
@@ -66,4 +66,4 @@ class MergeFiles(Bcfg2.Server.Lint.ServerPlugin):
threshold))
return rv
-
+
diff --git a/src/lib/Server/Lint/Pkgmgr.py b/src/lib/Bcfg2/Server/Lint/Pkgmgr.py
index 8f099163a..8f099163a 100644
--- a/src/lib/Server/Lint/Pkgmgr.py
+++ b/src/lib/Bcfg2/Server/Lint/Pkgmgr.py
diff --git a/src/lib/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
index 55206d2ba..55206d2ba 100644
--- a/src/lib/Server/Lint/RequiredAttrs.py
+++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
diff --git a/src/lib/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py
index 952a65365..012d65894 100644
--- a/src/lib/Server/Lint/Validate.py
+++ b/src/lib/Bcfg2/Server/Lint/Validate.py
@@ -36,7 +36,7 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
def Run(self):
schemadir = self.config['schema']
-
+
for path, schemaname in self.filesets.items():
try:
filelist = self.filelists[path]
@@ -98,7 +98,7 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
self.LintError("xml-failed-to-read",
"Failed to open file %s" % filename)
return False
-
+
if not schema.validate(datafile):
cmd = ["xmllint"]
if self.files is None:
@@ -180,7 +180,7 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
included = set([ent.get('href') for ent in
xdata.findall('./{http://www.w3.org/2001/XInclude}include')])
rv = []
-
+
while included:
try:
filename = included.pop()
diff --git a/src/lib/Server/Lint/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py
index a9d57c46c..82770333a 100644
--- a/src/lib/Server/Lint/__init__.py
+++ b/src/lib/Bcfg2/Server/Lint/__init__.py
@@ -1,5 +1,3 @@
-__revision__ = '$Revision$'
-
__all__ = ['Bundles',
'Comments',
'Duplicates',
@@ -75,7 +73,7 @@ class Plugin (object):
def LintError(self, err, msg):
self.errorHandler.dispatch(err, msg)
-
+
def RenderXML(self, element):
"""render an XML element for error output -- line number
prefixed, no children"""
diff --git a/src/lib/Server/Plugin.py b/src/lib/Bcfg2/Server/Plugin.py
index 692aa8a81..11e6c5c20 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Bcfg2/Server/Plugin.py
@@ -1,5 +1,4 @@
"""This module provides the baseclass for Bcfg2 Server Plugins."""
-__revision__ = '$Revision$'
import copy
import logging
@@ -72,7 +71,7 @@ class Debuggable(object):
self.__class__.__name__)
self.debug_flag = False
self.logger = logging.getLogger(name)
-
+
def toggle_debug(self):
self.debug_flag = not self.debug_flag
@@ -85,7 +84,6 @@ class Plugin(Debuggable):
"""This is the base class for all Bcfg2 Server plugins.
Several attributes must be defined in the subclass:
name : the name of the plugin
- __version__ : a version string
__author__ : the author/contact for the plugin
Plugins can provide three basic types of functionality:
@@ -94,7 +92,6 @@ class Plugin(Debuggable):
- Data collection (overloading GetProbes/ReceiveData)
"""
name = 'Plugin'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = False
deprecated = False
@@ -107,14 +104,14 @@ class Plugin(Debuggable):
def __init__(self, core, datastore):
"""Initialize the plugin.
-
+
:param core: the Bcfg2.Server.Core initializing the plugin
:param datastore: the filesystem path of Bcfg2's repository
"""
object.__init__(self)
self.Entries = {}
self.core = core
- self.data = "%s/%s" % (datastore, self.name)
+ self.data = os.path.join(datastore, self.name)
self.running = True
Debuggable.__init__(self, name=self.name)
@@ -366,7 +363,7 @@ class FileBacked(object):
object.__init__(self)
self.data = ''
self.name = name
-
+
def HandleEvent(self, event=None):
"""Read file upon update."""
if event and event.code2str() not in ['exists', 'changed', 'created']:
@@ -401,7 +398,7 @@ class DirectoryBacked(object):
:param data: the path to the data directory that will be
monitored.
:param fam: The FileMonitor object used to receive
- notifications of changes.
+ notifications of changes.
"""
object.__init__(self)
@@ -459,7 +456,7 @@ class DirectoryBacked(object):
def HandleEvent(self, event):
"""Handle FAM/Gamin events.
-
+
This method is invoked by FAM/Gamin when it detects a change
to a filesystem object we have requsted to be monitored.
@@ -595,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:
@@ -604,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__]
@@ -628,7 +640,7 @@ class SingleXMLFileBacked(XMLFileBacked):
class StructFile(XMLFileBacked):
"""This file contains a set of structure file formatting logic."""
__identifier__ = None
-
+
def __init__(self, name):
XMLFileBacked.__init__(self, name)
@@ -661,7 +673,7 @@ class StructFile(XMLFileBacked):
for child in item.iterchildren():
rv.extend(self._match(child, metadata))
return [rv]
-
+
def Match(self, metadata):
"""Return matching fragments of independent."""
rv = []
@@ -835,7 +847,7 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
attrs = self.get_attrs(entry, metadata)
for key, val in list(attrs.items()):
entry.attrib[key] = val
-
+
def get_attrs(self, entry, metadata):
""" get a list of attributes to add to the entry during the bind """
for src in self.entries.values():
@@ -847,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:
@@ -938,7 +950,7 @@ class SpecificData(object):
logger.error("Failed to read file %s" % self.name)
-class EntrySet:
+class EntrySet(object):
"""Entry sets deal with the host- and group-specific entries."""
ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\\.genshi_include)$")
@@ -1014,7 +1026,8 @@ class EntrySet:
spec = self.specificity_from_filename(event.filename)
except SpecificityError:
if not self.ignore.match(event.filename):
- logger.error("Could not process filename %s; ignoring" % fpath)
+ logger.error("Could not process filename %s; ignoring" %
+ fpath)
return
self.entries[event.filename] = self.entry_type(fpath,
spec, self.encoding)
@@ -1094,7 +1107,6 @@ class EntrySet:
class GroupSpool(Plugin, Generator):
"""Unified interface for handling group-specific data (e.g. .G## files)."""
name = 'GroupSpool'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filename_pattern = ""
es_child_cls = object
@@ -1185,7 +1197,7 @@ class SimpleConfig(FileBacked,
ConfigParser.SafeConfigParser):
''' a simple plugin config using ConfigParser '''
_required = True
-
+
def __init__(self, plugin):
filename = os.path.join(plugin.data, plugin.name.lower() + ".conf")
self.plugin = plugin
diff --git a/src/lib/Server/Plugins/Account.py b/src/lib/Bcfg2/Server/Plugins/Account.py
index f67819b9d..f2703dccb 100644
--- a/src/lib/Server/Plugins/Account.py
+++ b/src/lib/Bcfg2/Server/Plugins/Account.py
@@ -1,5 +1,4 @@
"""This handles authentication setup."""
-__revision__ = '$Revision$'
import Bcfg2.Server.Plugin
@@ -16,7 +15,6 @@ class Account(Bcfg2.Server.Plugin.Plugin,
"""
name = 'Account'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/BB.py b/src/lib/Bcfg2/Server/Plugins/BB.py
index 137142b66..c015ec47c 100644
--- a/src/lib/Server/Plugins/BB.py
+++ b/src/lib/Bcfg2/Server/Plugins/BB.py
@@ -62,7 +62,6 @@ class BB(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector):
"""The BB plugin maps users to machines and metadata to machines."""
name = 'BB'
- version = '$Revision$'
deprecated = True
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Base.py b/src/lib/Bcfg2/Server/Plugins/Base.py
index e8d798ae4..389ca7a95 100644
--- a/src/lib/Server/Plugins/Base.py
+++ b/src/lib/Bcfg2/Server/Plugins/Base.py
@@ -1,5 +1,4 @@
"""This module sets up a base list of configuration entries."""
-__revision__ = '$Revision$'
import copy
import lxml.etree
@@ -18,7 +17,6 @@ class Base(Bcfg2.Server.Plugin.Plugin,
needed for most actual systems.
"""
name = 'Base'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = Bcfg2.Server.Plugin.StructFile
deprecated = True
diff --git a/src/lib/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py
index a58257712..ccb99481e 100644
--- a/src/lib/Server/Plugins/Bundler.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py
@@ -1,5 +1,4 @@
"""This provides bundle clauses with translation functionality."""
-__revision__ = '$Revision$'
import copy
import lxml.etree
@@ -35,7 +34,6 @@ class Bundler(Bcfg2.Server.Plugin.Plugin,
bundle/translation scheme from Bcfg1.
"""
name = 'Bundler'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
patterns = re.compile('^(?P<name>.*)\.(xml|genshi)$')
diff --git a/src/lib/Server/Plugins/Bzr.py b/src/lib/Bcfg2/Server/Plugins/Bzr.py
index a9a5eb814..a71021cb5 100644
--- a/src/lib/Server/Plugins/Bzr.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bzr.py
@@ -10,7 +10,6 @@ class Bzr(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Bzr is a version plugin for dealing with Bcfg2 repos."""
name = 'Bzr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Bcfg2/Server/Plugins/Cfg.py
index ed08d454d..81904d082 100644
--- a/src/lib/Server/Plugins/Cfg.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg.py
@@ -1,5 +1,4 @@
"""This module implements a config file repository."""
-__revision__ = '$Revision$'
import binascii
import logging
@@ -64,7 +63,7 @@ def process_delta(data, delta):
basefile.write(data)
basefile.close()
os.close(basehandle)
-
+
cmd = ["patch", "-u", "-f", basefile.name]
patch = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stderr = patch.communicate(input=delta.data)[1]
@@ -120,8 +119,9 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
base_files = [matching.index(m) for m in matching
if not m.specific.delta]
if not base_files:
- logger.error("No base file found for %s" % entry.get('name'))
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "No base file found for %s" % entry.get('name')
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
base = min(base_files)
used = matching[:base + 1]
used.reverse()
@@ -133,15 +133,16 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
basefile = used.pop(0)
if entry.get('perms').lower() == 'inherit':
# use on-disk permissions
- fname = "%s/%s" % (self.path, entry.get('name'))
+ fname = os.path.join(self.path, entry.get('name'))
entry.set('perms',
str(oct(stat.S_IMODE(os.stat(fname).st_mode))))
if entry.tag == 'Path':
entry.set('type', 'file')
if basefile.name.endswith(".genshi"):
if not have_genshi:
- logger.error("Cfg: Genshi is not available")
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: Genshi is not available: %s" % entry.get("name")
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
try:
template_cls = NewTextTemplate
loader = TemplateLoader()
@@ -159,13 +160,15 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
if data == '':
entry.set('empty', 'true')
except Exception:
- e = sys.exc_info()[1]
- logger.error("Cfg: genshi exception: %s" % e)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: genshi exception (%s): %s" % (entry.get("name"),
+ sys.exc_info()[1])
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
elif basefile.name.endswith(".cheetah"):
if not have_cheetah:
- logger.error("Cfg: Cheetah is not available")
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: Cheetah is not available: %s" % entry.get("name")
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
try:
fname = entry.get('realname', entry.get('name'))
s = {'useStackFrames': False}
@@ -178,9 +181,10 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
if data == '':
entry.set('empty', 'true')
except Exception:
- e = sys.exc_info()[1]
- logger.error("Cfg: cheetah exception: %s" % e)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: cheetah exception (%s): %s" % (entry.get("name"),
+ sys.exc_info()[1])
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
else:
data = basefile.data
for delta in used:
@@ -191,17 +195,18 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
try:
entry.text = u_str(data, self.encoding)
except UnicodeDecodeError:
- e = sys.exc_info()[1]
- logger.error("Failed to decode %s: %s" % (entry.get('name'), e))
+ msg = "Failed to decode %s: %s" % (entry.get('name'),
+ sys.exc_info()[1])
+ logger.error(msg)
logger.error("Please verify you are using the proper encoding.")
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
except ValueError:
- e = sys.exc_info()[1]
- logger.error("Error in specification for %s" % entry.get('name'))
- logger.error("%s" % e)
+ msg = "Error in specification for %s: %s" % (entry.get('name'),
+ sys.exc_info()[1])
+ logger.error(msg)
logger.error("You need to specify base64 encoding for %s." %
entry.get('name'))
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
if entry.text in ['', None]:
entry.set('empty', 'true')
@@ -228,16 +233,20 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
if 'text' in new_entry:
name = self.build_filename(specific)
if os.path.exists("%s.genshi" % name):
- logger.error("Cfg: Unable to pull data for genshi types")
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: Unable to pull data for genshi types"
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
elif os.path.exists("%s.cheetah" % name):
- logger.error("Cfg: Unable to pull data for cheetah types")
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: Unable to pull data for cheetah types"
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
try:
etext = new_entry['text'].encode(self.encoding)
except:
- logger.error("Cfg: Cannot encode content of %s as %s" % (name, self.encoding))
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "Cfg: Cannot encode content of %s as %s" % (name,
+ self.encoding)
+ logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
open(name, 'w').write(etext)
self.debug_log("Wrote file %s" % name, flag=log)
badattr = [attr for attr in ['owner', 'group', 'perms']
@@ -271,7 +280,6 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
Bcfg2.Server.Plugin.PullTarget):
"""This generator in the configuration file repository for Bcfg2."""
name = 'Cfg'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
es_cls = CfgEntrySet
es_child_cls = Bcfg2.Server.Plugin.SpecificData
diff --git a/src/lib/Server/Plugins/Cvs.py b/src/lib/Bcfg2/Server/Plugins/Cvs.py
index ea898c023..6ce72acd2 100644
--- a/src/lib/Server/Plugins/Cvs.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cvs.py
@@ -10,7 +10,6 @@ class Cvs(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""CVS is a version plugin for dealing with Bcfg2 repository."""
name = 'Cvs'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/DBStats.py b/src/lib/Bcfg2/Server/Plugins/DBStats.py
index 8761d282d..95395f74e 100644
--- a/src/lib/Server/Plugins/DBStats.py
+++ b/src/lib/Bcfg2/Server/Plugins/DBStats.py
@@ -22,7 +22,6 @@ class DBStats(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.ThreadedStatistics,
Bcfg2.Server.Plugin.PullSource):
name = 'DBStats'
- __version__ = '$Id$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Darcs.py b/src/lib/Bcfg2/Server/Plugins/Darcs.py
index eb34a52c4..9fb9ff4f1 100644
--- a/src/lib/Server/Plugins/Darcs.py
+++ b/src/lib/Bcfg2/Server/Plugins/Darcs.py
@@ -10,7 +10,6 @@ class Darcs(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Darcs is a version plugin for dealing with Bcfg2 repos."""
name = 'Darcs'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Decisions.py b/src/lib/Bcfg2/Server/Plugins/Decisions.py
index 556f75502..b432474f2 100644
--- a/src/lib/Server/Plugins/Decisions.py
+++ b/src/lib/Bcfg2/Server/Plugins/Decisions.py
@@ -50,7 +50,6 @@ class Decisions(DecisionSet,
Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Decision):
name = 'Decisions'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Defaults.py b/src/lib/Bcfg2/Server/Plugins/Defaults.py
index b208c1126..718192e2a 100644
--- a/src/lib/Server/Plugins/Defaults.py
+++ b/src/lib/Bcfg2/Server/Plugins/Defaults.py
@@ -1,5 +1,4 @@
"""This generator provides rule-based entry mappings."""
-__revision__ = '$Revision$'
import re
import Bcfg2.Server.Plugin
@@ -9,7 +8,6 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
Bcfg2.Server.Plugin.StructureValidator):
"""Set default attributes on bound entries"""
name = 'Defaults'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
# Rules is a Generator that happens to implement all of the
diff --git a/src/lib/Server/Plugins/Deps.py b/src/lib/Bcfg2/Server/Plugins/Deps.py
index 482d457af..9b848baae 100644
--- a/src/lib/Server/Plugins/Deps.py
+++ b/src/lib/Bcfg2/Server/Plugins/Deps.py
@@ -1,5 +1,4 @@
"""This plugin provides automatic dependency handling."""
-__revision__ = '$Revision$'
import lxml.etree
@@ -45,7 +44,6 @@ class DepXMLSrc(Bcfg2.Server.Plugin.XMLSrc):
class Deps(Bcfg2.Server.Plugin.PrioDir,
Bcfg2.Server.Plugin.StructureValidator):
name = 'Deps'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = DepXMLSrc
diff --git a/src/lib/Server/Plugins/Editor.py b/src/lib/Bcfg2/Server/Plugins/Editor.py
index 76a03a325..c0d2cfbad 100644
--- a/src/lib/Server/Plugins/Editor.py
+++ b/src/lib/Bcfg2/Server/Plugins/Editor.py
@@ -59,7 +59,6 @@ class EditEntrySet(Bcfg2.Server.Plugin.EntrySet):
class Editor(Bcfg2.Server.Plugin.GroupSpool,
Bcfg2.Server.Plugin.Probing):
name = 'Editor'
- __version__ = '$Id$'
__author__ = 'bcfg2-dev@mcs.anl.gov'
filename_pattern = 'edits'
es_child_cls = EditDirectives
diff --git a/src/lib/Server/Plugins/FileProbes.py b/src/lib/Bcfg2/Server/Plugins/FileProbes.py
index c58040b65..5beec7be0 100644
--- a/src/lib/Server/Plugins/FileProbes.py
+++ b/src/lib/Bcfg2/Server/Plugins/FileProbes.py
@@ -3,7 +3,6 @@ added to the specification. On subsequent runs, the file will be
replaced on the client if it is missing; if it has changed on the
client, it can either be updated in the specification or replaced on
the client """
-__revision__ = '$Revision: 1465 $'
import os
import sys
@@ -55,7 +54,6 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
name = 'FileProbes'
experimental = True
- __version__ = '$Id$'
__author__ = 'chris.a.st.pierre@gmail.com'
def __init__(self, core, datastore):
@@ -133,7 +131,7 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
else:
entrydata = entry.text
- if create:
+ if create:
self.logger.info("Writing new probed file %s" % fileloc)
self.write_file(fileloc, contents)
self.verify_file(filename, contents, metadata)
@@ -193,7 +191,7 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
except Bcfg2.Server.Plugin.PluginExecutionError:
tries += 1
continue
-
+
# get current entry data
if entry.get("encoding") == "base64":
entrydata = binascii.a2b_base64(entry.text)
@@ -202,12 +200,12 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
if entrydata == contents:
updated = True
tries += 1
-
+
def write_infoxml(self, infoxml, entry, data):
""" write an info.xml for the file """
if os.path.exists(infoxml):
return
-
+
self.logger.info("Writing info.xml at %s for %s" %
(infoxml, data.get("name")))
info = \
@@ -220,7 +218,7 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Options.MDATA_PERMS.value),
encoding=entry.get("encoding",
Bcfg2.Options.ENCODING.value))
-
+
root = lxml.etree.Element("FileInfo")
root.append(info)
try:
diff --git a/src/lib/Server/Plugins/Fossil.py b/src/lib/Bcfg2/Server/Plugins/Fossil.py
index 57d427673..1b1627688 100644
--- a/src/lib/Server/Plugins/Fossil.py
+++ b/src/lib/Bcfg2/Server/Plugins/Fossil.py
@@ -10,7 +10,6 @@ class Fossil(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Fossil is a version plugin for dealing with Bcfg2 repos."""
name = 'Fossil'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index aaeac12ae..8f8ea87f1 100644
--- a/src/lib/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -13,7 +13,6 @@ class Git(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Git is a version plugin for dealing with Bcfg2 repos."""
name = 'Git'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py
index 58b4d4afb..58b4d4afb 100644
--- a/src/lib/Server/Plugins/GroupPatterns.py
+++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py
diff --git a/src/lib/Server/Plugins/Guppy.py b/src/lib/Bcfg2/Server/Plugins/Guppy.py
index b217378d6..eea92f30f 100644
--- a/src/lib/Server/Plugins/Guppy.py
+++ b/src/lib/Bcfg2/Server/Plugins/Guppy.py
@@ -12,7 +12,7 @@ python -c "from guppy import hpy;hpy().monitor()"
For example:
# python -c "from guppy import hpy;hpy().monitor()"
-<Monitor>
+<Monitor>
*** Connection 1 opened ***
<Monitor> lc
CID PID ARGV
@@ -21,7 +21,7 @@ CID PID ARGV
Remote connection 1. To return to Monitor, type <Ctrl-C> or .<RETURN>
<Annex> int
Remote interactive console. To return to Annex, type '-'.
->>> hp.heap()
+>>> hp.heap()
...
@@ -32,7 +32,6 @@ import Bcfg2.Server.Plugin
class Guppy(Bcfg2.Server.Plugin.Plugin):
"""Guppy is a debugging plugin to help trace memory leaks"""
name = 'Guppy'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Hg.py b/src/lib/Bcfg2/Server/Plugins/Hg.py
index 70e33ef1f..0c3537613 100644
--- a/src/lib/Server/Plugins/Hg.py
+++ b/src/lib/Bcfg2/Server/Plugins/Hg.py
@@ -10,7 +10,6 @@ class Hg(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Mercurial is a version plugin for dealing with Bcfg2 repository."""
name = 'Mercurial'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Hostbase.py b/src/lib/Bcfg2/Server/Plugins/Hostbase.py
index 4180fd716..e9c1c1cff 100644
--- a/src/lib/Server/Plugins/Hostbase.py
+++ b/src/lib/Bcfg2/Server/Plugins/Hostbase.py
@@ -2,7 +2,6 @@
This file provides the Hostbase plugin.
It manages dns/dhcp/nis host information
"""
-__revision__ = '$Revision$'
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.Server.Hostbase.settings'
@@ -23,7 +22,6 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Generator):
"""The Hostbase plugin handles host/network info."""
name = 'Hostbase'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filepath = '/my/adm/hostbase/files/bind'
diff --git a/src/lib/Server/Plugins/Ldap.py b/src/lib/Bcfg2/Server/Plugins/Ldap.py
index f1e2198d2..29abf5b13 100644
--- a/src/lib/Server/Plugins/Ldap.py
+++ b/src/lib/Bcfg2/Server/Plugins/Ldap.py
@@ -50,7 +50,7 @@ class ConfigFile(Bcfg2.Server.Plugin.FileBacked):
def Index(self):
"""
Reregisters the queries in the config file
-
+
The config will take care of actually registering the queries,
so we just load it once and don't keep it.
"""
@@ -63,24 +63,23 @@ class Ldap(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector):
The Ldap plugin allows adding data from an LDAP server to your metadata.
"""
name = "Ldap"
- version = "$Revision: $"
experimental = True
debug_flag = False
-
+
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Connector.__init__(self)
self.config = ConfigFile(self.data + "/config.py", core.fam)
-
+
def debug_log(self, message, flag = None):
if (flag is None) and self.debug_flag or flag:
self.logger.error(message)
-
+
def get_additional_data(self, metadata):
query = None
try:
data = {}
- self.debug_log("LdapPlugin debug: found queries " +
+ self.debug_log("LdapPlugin debug: found queries " +
str(LDAP_QUERIES))
for QueryClass in LDAP_QUERIES:
query = QueryClass()
@@ -95,14 +94,14 @@ class Ldap(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector):
except Exception:
if hasattr(query, "name"):
Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
- "Exception during processing of query named '" +
+ "Exception during processing of query named '" +
str(query.name) +
- "', query results will be empty" +
+ "', query results will be empty" +
" and may cause bind failures")
for line in traceback.format_exception(sys.exc_info()[0],
sys.exc_info()[1],
sys.exc_info()[2]):
- Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
+ Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
line.replace("\n", ""))
return {}
@@ -110,23 +109,23 @@ class LdapConnection(object):
"""
Connection to an LDAP server.
"""
- def __init__(self, host = "localhost", port = 389,
+ def __init__(self, host = "localhost", port = 389,
binddn = None, bindpw = None):
self.host = host
self.port = port
self.binddn = binddn
self.bindpw = bindpw
self.conn = None
-
+
def __del__(self):
if self.conn:
self.conn.unbind()
-
+
def init_conn(self):
self.conn = ldap.initialize(self.url)
if self.binddn is not None and self.bindpw is not None:
self.conn.simple_bind_s(self.binddn, self.bindpw)
-
+
def run_query(self, query):
result = None
for attempt in range(RETRY_COUNT + 1):
@@ -148,17 +147,17 @@ class LdapConnection(object):
self.conn = None
time.sleep(RETRY_DELAY)
return result
-
+
@property
def url(self):
return "ldap://" + self.host + ":" + str(self.port)
-
+
class LdapQuery(object):
"""
Query referencing an LdapConnection and providing several
methods for query manipulation.
"""
-
+
name = "unknown"
base = ""
scope = "sub"
@@ -166,10 +165,10 @@ class LdapQuery(object):
attrs = None
connection = None
result = None
-
+
def __unicode__(self):
return "LdapQuery:" + self.name
-
+
def is_applicable(self, metadata):
"""
Overrideable method to determine if the query is to be executed for
@@ -177,27 +176,27 @@ class LdapQuery(object):
Defaults to true.
"""
return True
-
+
def prepare_query(self, metadata):
"""
Overrideable method to alter the query based on metadata.
Defaults to doing nothing.
In most cases, you will do something like
-
+
self.filter = "(cn=" + metadata.hostname + ")"
-
+
here.
"""
pass
-
+
def process_result(self, metadata):
"""
Overrideable method to post-process the query result.
Defaults to returning the unaltered result.
"""
return self.result
-
+
def get_result(self, metadata):
"""
Method to handle preparing, executing and processing the query.
@@ -208,7 +207,7 @@ class LdapQuery(object):
self.result = self.process_result(metadata)
return self.result
else:
- Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
+ Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
"No valid connection defined for query " + str(self))
return None
@@ -224,14 +223,14 @@ class LdapSubQuery(LdapQuery):
Defaults to doing nothing.
"""
pass
-
+
def process_result(self, metadata, **kwargs):
"""
Overrideable method to post-process the query result.
Defaults to returning the unaltered result.
"""
return self.result
-
+
def get_result(self, metadata, **kwargs):
"""
Method to handle preparing, executing and processing the query.
@@ -241,6 +240,6 @@ class LdapSubQuery(LdapQuery):
self.result = self.connection.run_query(self)
return self.process_result(metadata, **kwargs)
else:
- Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
+ Bcfg2.Server.Plugin.logger.error("LdapPlugin error: " +
"No valid connection defined for query " + str(self))
return None
diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index f837b1305..970126b80 100644
--- a/src/lib/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -2,8 +2,6 @@
This file stores persistent metadata for the Bcfg2 Configuration Repository.
"""
-__revision__ = '$Revision$'
-
import copy
import fcntl
import lxml.etree
@@ -38,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
@@ -64,43 +65,40 @@ 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("%s/%s" % (self.basedir, fname),
- self.metadata)
+ self.metadata.core.fam.AddMonitor(fpath, self.metadata)
self.extras.append(fname)
def load_xml(self):
"""Load changes from XML"""
try:
- xdata = lxml.etree.parse("%s/%s" % (self.basedir, self.basefile))
+ xdata = lxml.etree.parse(os.path.join(self.basedir, self.basefile))
except lxml.etree.XMLSyntaxError:
- self.logger.error('Failed to parse %s' % (self.basefile))
+ 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:
- self.logger.error("Failed to process XInclude for file %s" % self.basefile)
+ self.logger.error("Failed to process XInclude for file %s" %
+ self.basefile)
self.data = xdata
def write(self):
"""Write changes to xml back to disk."""
- self.write_xml("%s/%s" % (self.basedir, self.basefile),
+ self.write_xml(os.path.join(self.basedir, self.basefile),
self.basedata)
def write_xml(self, fname, xmltree):
"""Write changes to xml back to disk."""
tmpfile = "%s.new" % fname
try:
- datafile = open("%s" % tmpfile, 'w')
+ datafile = open(tmpfile, 'w')
except IOError:
e = sys.exc_info()[1]
self.logger.error("Failed to write %s: %s" % (tmpfile, e))
@@ -116,40 +114,42 @@ class XMLMetadataConfig(object):
datafile.write(newcontents)
except:
fcntl.lockf(fd, fcntl.LOCK_UN)
- self.logger.error("Metadata: Failed to write new xml data to %s" % tmpfile, exc_info=1)
- os.unlink("%s" % tmpfile)
+ self.logger.error("Metadata: Failed to write new xml data to %s" %
+ tmpfile, exc_info=1)
+ os.unlink(tmpfile)
raise MetadataRuntimeError
datafile.close()
# check if clients.xml is a symlink
- xmlfile = "%s" % fname
- if os.path.islink(xmlfile):
- xmlfile = os.readlink(xmlfile)
+ if os.path.islink(fname):
+ fname = os.readlink(fname)
try:
- os.rename("%s" % tmpfile, xmlfile)
+ os.rename(tmpfile, fname)
except:
self.logger.error("Metadata: Failed to rename %s" % tmpfile)
raise MetadataRuntimeError
def find_xml_for_xpath(self, xpath):
- """Find and load xml data containing the xpath query"""
+ """Find and load xml file containing the xpath query"""
if self.pseudo_monitor:
# Reload xml if we don't have a real monitor
self.load_xml()
cli = self.basedata.xpath(xpath)
if len(cli) > 0:
- return {'filename': "%s/%s" % (self.basedir, self.basefile),
+ return {'filename': os.path.join(self.basedir, self.basefile),
'xmltree': self.basedata,
'xquery': cli}
else:
"""Try to find the data in included files"""
for included in self.extras:
try:
- xdata = lxml.etree.parse("%s/%s" % (self.basedir, included))
+ xdata = lxml.etree.parse(os.path.join(self.basedir,
+ included))
cli = xdata.xpath(xpath)
if len(cli) > 0:
- return {'filename': "%s/%s" % (self.basedir, included),
+ return {'filename': os.path.join(self.basedir,
+ included),
'xmltree': xdata,
'xquery': cli}
except lxml.etree.XMLSyntaxError:
@@ -221,7 +221,6 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Metadata,
Bcfg2.Server.Plugin.Statistics):
"""This class contains data for bcfg2 server metadata."""
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
name = "Metadata"
sort_order = 500
@@ -232,8 +231,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Statistics.__init__(self)
if watch_clients:
try:
- core.fam.AddMonitor("%s/%s" % (self.data, "groups.xml"), self)
- core.fam.AddMonitor("%s/%s" % (self.data, "clients.xml"), self)
+ core.fam.AddMonitor(os.path.join(self.data, "groups.xml"), self)
+ core.fam.AddMonitor(os.path.join(self.data, "clients.xml"), self)
except:
print("Unable to add file monitor for groups.xml or clients.xml")
raise Bcfg2.Server.Plugin.PluginInitError
@@ -274,269 +273,241 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
@classmethod
def init_repo(cls, repo, groups, os_selection, clients):
- path = '%s/%s' % (repo, cls.name)
+ path = os.path.join(repo, cls.name)
os.makedirs(path)
- open("%s/Metadata/groups.xml" %
- repo, "w").write(groups % os_selection)
- open("%s/Metadata/clients.xml" %
- repo, "w").write(clients % socket.getfqdn())
+ open(os.path.join(repo, "Metadata", "groups.xml"),
+ "w").write(groups % os_selection)
+ open(os.path.join(repo, "Metadata", "clients.xml"),
+ "w").write(clients % socket.getfqdn())
def get_groups(self):
'''return groups xml tree'''
- groups_tree = lxml.etree.parse(self.data + "/groups.xml")
+ groups_tree = lxml.etree.parse(os.path.join(self.data, "groups.xml"))
root = groups_tree.getroot()
return root
- def search_group(self, group_name, tree):
- """Find a group."""
- for node in tree.findall("//Group"):
- if node.get("name") == group_name:
+ def _search_xdata(self, tag, name, tree, alias=False):
+ for node in tree.findall("//%s" % tag):
+ if node.get("name") == name:
return node
- for child in node:
- if child.tag == "Alias" and child.attrib["name"] == group_name:
- return node
+ elif alias:
+ for child in node:
+ if (child.tag == "Alias" and
+ child.attrib["name"] == name):
+ return node
return None
- def add_group(self, group_name, attribs):
- """Add group to groups.xml."""
+ def search_group(self, group_name, tree):
+ """Find a group."""
+ return self._search_xdata("Group", group_name, tree)
+
+ def search_bundle(self, bundle_name, tree):
+ """Find a bundle."""
+ return self._search_xdata("Bundle", bundle_name, tree)
+
+ def search_client(self, client_name, tree):
+ return self._search_xdata("Client", client_name, tree, alias=True)
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def _add_xdata(self, config, tag, name, attribs=None, alias=False):
+ node = self._search_xdata(tag, name, config.xdata, alias=alias)
if node != None:
- self.logger.error("Group \"%s\" already exists" % (group_name))
+ self.logger.error("%s \"%s\" already exists" % (tag, name))
raise MetadataConsistencyError
+ element = lxml.etree.SubElement(config.base_xdata.getroot(),
+ tag, name=name)
+ if attribs:
+ for key, val in list(attribs.items()):
+ element.set(key, val)
+ config.write()
- element = lxml.etree.SubElement(self.groups_xml.base_xdata.getroot(),
- "Group", name=group_name)
- for key, val in list(attribs.items()):
- element.set(key, val)
- self.groups_xml.write()
+ def add_group(self, group_name, attribs):
+ """Add group to groups.xml."""
+ return self._add_xdata(self.groups_xml, "Group", group_name,
+ attribs=attribs)
- def update_group(self, group_name, attribs):
- """Update a groups attributes."""
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def add_bundle(self, bundle_name):
+ """Add bundle to groups.xml."""
+ return self._add_xdata(self.groups_xml, "Bundle", bundle_name)
+
+ def add_client(self, client_name, attribs):
+ """Add client to clients.xml."""
+ return self._add_xdata(self.clients_xml, "Client", client_name,
+ attribs=attribs, alias=True)
+
+ def _update_xdata(self, config, tag, name, attribs, alias=False):
+ node = self._search_xdata(tag, name, config.xdata, alias=alias)
if node == None:
- self.logger.error("Group \"%s\" does not exist" % (group_name))
+ self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise MetadataConsistencyError
- xdict = self.groups_xml.find_xml_for_xpath('.//Group[@name="%s"]' % (node.get('name')))
+ xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
+ (tag, node.get('name')))
if not xdict:
- self.logger.error("Unexpected error finding group")
+ self.logger.error("Unexpected error finding %s \"%s\"" %
+ (tag, name))
raise MetadataConsistencyError
-
for key, val in list(attribs.items()):
xdict['xquery'][0].set(key, val)
- self.groups_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ config.write_xml(xdict['filename'], xdict['xmltree'])
- def remove_group(self, group_name):
- """Remove a group."""
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def update_group(self, group_name, attribs):
+ """Update a groups attributes."""
+ return self._update_xdata(self.groups_xml, "Group", group_name, attribs)
+
+ def update_client(self, client_name, attribs):
+ """Update a clients attributes."""
+ return self._update_xdata(self.clients_xml, "Client", client_name,
+ attribs, alias=True)
+
+ def _remove_xdata(self, config, tag, name, alias=False):
+ node = self._search_xdata(tag, name, config.xdata)
if node == None:
- self.logger.error("Group \"%s\" does not exist" % (group_name))
+ self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise MetadataConsistencyError
- xdict = self.groups_xml.find_xml_for_xpath('.//Group[@name="%s"]' % (node.get('name')))
+ xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
+ (tag, node.get('name')))
if not xdict:
- self.logger.error("Unexpected error finding group")
+ self.logger.error("Unexpected error finding %s \"%s\"" %
+ (tag, name))
raise MetadataConsistencyError
xdict['xquery'][0].getparent().remove(xdict['xquery'][0])
self.groups_xml.write_xml(xdict['filename'], xdict['xmltree'])
- def add_bundle(self, bundle_name):
- """Add bundle to groups.xml."""
- tree = lxml.etree.parse(self.data + "/groups.xml")
- root = tree.getroot()
- element = lxml.etree.Element("Bundle", name=bundle_name)
- node = self.search_group(bundle_name, tree)
- if node != None:
- self.logger.error("Bundle \"%s\" already exists" % (bundle_name))
- raise MetadataConsistencyError
- root.append(element)
- group_tree = open(self.data + "/groups.xml", "w")
- fd = group_tree.fileno()
- while True:
- try:
- fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- continue
- else:
- break
- tree.write(group_tree)
- fcntl.lockf(fd, fcntl.LOCK_UN)
- group_tree.close()
+ def remove_group(self, group_name):
+ """Remove a group."""
+ return self._remove_xdata(self.groups_xml, "Group", group_name)
def remove_bundle(self, bundle_name):
"""Remove a bundle."""
- tree = lxml.etree.parse(self.data + "/groups.xml")
- root = tree.getroot()
- node = self.search_group(bundle_name, tree)
- if node == None:
- self.logger.error("Bundle \"%s\" not found" % (bundle_name))
- raise MetadataConsistencyError
- root.remove(node)
- group_tree = open(self.data + "/groups.xml", "w")
- fd = group_tree.fileno()
- while True:
- try:
- fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- continue
- else:
- break
- tree.write(group_tree)
- fcntl.lockf(fd, fcntl.LOCK_UN)
- group_tree.close()
-
- def search_client(self, client_name, tree):
- """Find a client."""
- for node in tree.findall("//Client"):
- if node.get("name") == client_name:
- return node
- for child in node:
- if child.tag == "Alias" and child.attrib["name"] == client_name:
- return node
- return None
-
- def add_client(self, client_name, attribs):
- """Add client to clients.xml."""
- node = self.search_client(client_name, self.clients_xml.xdata)
- if node != None:
- self.logger.error("Client \"%s\" already exists" % (client_name))
- raise MetadataConsistencyError
-
- element = lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- "Client", name=client_name)
- for key, val in list(attribs.items()):
- element.set(key, val)
- self.clients_xml.write()
-
- def update_client(self, client_name, attribs):
- """Update a clients attributes."""
- node = self.search_client(client_name, self.clients_xml.xdata)
- if node == None:
- self.logger.error("Client \"%s\" does not exist" % (client_name))
- raise MetadataConsistencyError
-
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (node.get('name')))
- if not xdict:
- self.logger.error("Unexpected error finding client")
- raise MetadataConsistencyError
+ return self._remove_xdata(self.groups_xml, "Bundle", bundle_name)
- node = xdict['xquery'][0]
- [node.set(key, value) for key, value in list(attribs.items())]
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ def _handle_clients_xml_event(self, event):
+ xdata = self.clients_xml.xdata
+ self.clients = {}
+ self.aliases = {}
+ self.raliases = {}
+ self.bad_clients = {}
+ self.secure = []
+ self.floating = []
+ self.addresses = {}
+ self.raddresses = {}
+ for client in xdata.findall('.//Client'):
+ clname = client.get('name').lower()
+ if 'address' in client.attrib:
+ caddr = client.get('address')
+ if caddr in self.addresses:
+ self.addresses[caddr].append(clname)
+ else:
+ self.addresses[caddr] = [clname]
+ if clname not in self.raddresses:
+ self.raddresses[clname] = set()
+ self.raddresses[clname].add(caddr)
+ if 'auth' in client.attrib:
+ self.auth[client.get('name')] = client.get('auth',
+ 'cert+password')
+ if 'uuid' in client.attrib:
+ self.uuid[client.get('uuid')] = clname
+ if client.get('secure', 'false') == 'true':
+ self.secure.append(clname)
+ if client.get('location', 'fixed') == 'floating':
+ self.floating.append(clname)
+ if 'password' in client.attrib:
+ self.passwords[clname] = client.get('password')
+
+ self.raliases[clname] = set()
+ for alias in client.findall('Alias'):
+ self.aliases.update({alias.get('name'): clname})
+ self.raliases[clname].add(alias.get('name'))
+ if 'address' not in alias.attrib:
+ continue
+ if alias.get('address') in self.addresses:
+ self.addresses[alias.get('address')].append(clname)
+ else:
+ self.addresses[alias.get('address')] = [clname]
+ if clname not in self.raddresses:
+ self.raddresses[clname] = set()
+ self.raddresses[clname].add(alias.get('address'))
+ self.clients.update({clname: client.get('profile')})
+ self.states['clients.xml'] = True
+
+ def _handle_groups_xml_event(self, event):
+ xdata = self.groups_xml.xdata
+ self.public = []
+ self.private = []
+ self.profiles = []
+ self.groups = {}
+ grouptmp = {}
+ self.categories = {}
+ groupseen = list()
+ for group in xdata.xpath('//Groups/Group'):
+ if group.get('name') not in groupseen:
+ groupseen.append(group.get('name'))
+ else:
+ self.logger.error("Metadata: Group %s defined multiply" %
+ group.get('name'))
+ grouptmp[group.get('name')] = \
+ ([item.get('name') for item in group.findall('./Bundle')],
+ [item.get('name') for item in group.findall('./Group')])
+ grouptmp[group.get('name')][1].append(group.get('name'))
+ if group.get('default', 'false') == 'true':
+ self.default = group.get('name')
+ if group.get('profile', 'false') == 'true':
+ self.profiles.append(group.get('name'))
+ if group.get('public', 'false') == 'true':
+ self.public.append(group.get('name'))
+ elif group.get('public', 'true') == 'false':
+ self.private.append(group.get('name'))
+ if 'category' in group.attrib:
+ self.categories[group.get('name')] = group.get('category')
+
+ for group in grouptmp:
+ # self.groups[group] => (bundles, groups, categories)
+ self.groups[group] = (set(), set(), {})
+ tocheck = [group]
+ group_cat = self.groups[group][2]
+ while tocheck:
+ now = tocheck.pop()
+ self.groups[group][1].add(now)
+ if now in grouptmp:
+ (bundles, groups) = grouptmp[now]
+ for ggg in groups:
+ if ggg in self.groups[group][1]:
+ continue
+ if (ggg not in self.categories or \
+ self.categories[ggg] not in self.groups[group][2]):
+ self.groups[group][1].add(ggg)
+ tocheck.append(ggg)
+ if ggg in self.categories:
+ group_cat[self.categories[ggg]] = ggg
+ elif ggg in self.categories:
+ self.logger.info("Group %s: %s cat-suppressed %s" % \
+ (group,
+ group_cat[self.categories[ggg]],
+ ggg))
+ [self.groups[group][0].add(bund) for bund in bundles]
+ self.states['groups.xml'] = True
def HandleEvent(self, event):
"""Handle update events for data files."""
if self.clients_xml.HandleEvent(event):
- xdata = self.clients_xml.xdata
- self.clients = {}
- self.aliases = {}
- self.raliases = {}
- self.bad_clients = {}
- self.secure = []
- self.floating = []
- self.addresses = {}
- self.raddresses = {}
- for client in xdata.findall('.//Client'):
- clname = client.get('name').lower()
- if 'address' in client.attrib:
- caddr = client.get('address')
- if caddr in self.addresses:
- self.addresses[caddr].append(clname)
- else:
- self.addresses[caddr] = [clname]
- if clname not in self.raddresses:
- self.raddresses[clname] = set()
- self.raddresses[clname].add(caddr)
- if 'auth' in client.attrib:
- self.auth[client.get('name')] = client.get('auth',
- 'cert+password')
- if 'uuid' in client.attrib:
- self.uuid[client.get('uuid')] = clname
- if client.get('secure', 'false') == 'true':
- self.secure.append(clname)
- if client.get('location', 'fixed') == 'floating':
- self.floating.append(clname)
- if 'password' in client.attrib:
- self.passwords[clname] = client.get('password')
- for alias in [alias for alias in client.findall('Alias')\
- if 'address' in alias.attrib]:
- if alias.get('address') in self.addresses:
- self.addresses[alias.get('address')].append(clname)
- else:
- self.addresses[alias.get('address')] = [clname]
- if clname not in self.raddresses:
- self.raddresses[clname] = set()
- self.raddresses[clname].add(alias.get('address'))
- self.clients.update({clname: client.get('profile')})
- [self.aliases.update({alias.get('name'): clname}) \
- for alias in client.findall('Alias')]
- self.raliases[clname] = set()
- [self.raliases[clname].add(alias.get('name')) for alias \
- in client.findall('Alias')]
- self.states['clients.xml'] = True
+ self._handle_clients_xml_event(event)
elif self.groups_xml.HandleEvent(event):
- xdata = self.groups_xml.xdata
- self.public = []
- self.private = []
- self.profiles = []
- self.groups = {}
- grouptmp = {}
- self.categories = {}
- groupseen = list()
- for group in xdata.xpath('//Groups/Group'):
- if group.get('name') not in groupseen:
- groupseen.append(group.get('name'))
- else:
- self.logger.error("Metadata: Group %s defined multiply" % (group.get('name')))
- grouptmp[group.get('name')] = tuple([[item.get('name') for item in group.findall(spec)]
- for spec in ['./Bundle', './Group']])
- grouptmp[group.get('name')][1].append(group.get('name'))
- if group.get('default', 'false') == 'true':
- self.default = group.get('name')
- if group.get('profile', 'false') == 'true':
- self.profiles.append(group.get('name'))
- if group.get('public', 'false') == 'true':
- self.public.append(group.get('name'))
- elif group.get('public', 'true') == 'false':
- self.private.append(group.get('name'))
- if 'category' in group.attrib:
- self.categories[group.get('name')] = group.get('category')
- for group in grouptmp:
- # self.groups[group] => (bundles, groups, categories)
- self.groups[group] = (set(), set(), {})
- tocheck = [group]
- group_cat = self.groups[group][2]
- while tocheck:
- now = tocheck.pop()
- self.groups[group][1].add(now)
- if now in grouptmp:
- (bundles, groups) = grouptmp[now]
- for ggg in [ggg for ggg in groups if ggg not in self.groups[group][1]]:
- if ggg not in self.categories or \
- self.categories[ggg] not in self.groups[group][2]:
- self.groups[group][1].add(ggg)
- tocheck.append(ggg)
- if ggg in self.categories:
- group_cat[self.categories[ggg]] = ggg
- elif ggg in self.categories:
- self.logger.info("Group %s: %s cat-suppressed %s" % \
- (group,
- group_cat[self.categories[ggg]],
- ggg))
- [self.groups[group][0].add(bund) for bund in bundles]
- self.states['groups.xml'] = True
+ self._handle_groups_xml_event(event)
+
if False not in list(self.states.values()):
# check that all client groups are real and complete
real = list(self.groups.keys())
for client in list(self.clients.keys()):
if self.clients[client] not in self.profiles:
- self.logger.error("Client %s set as nonexistent or incomplete group %s" \
- % (client, self.clients[client]))
- self.logger.error("Removing client mapping for %s" % (client))
+ self.logger.error("Client %s set as nonexistent or "
+ "incomplete group %s" %
+ (client, self.clients[client]))
+ self.logger.error("Removing client mapping for %s" % client)
self.bad_clients[client] = self.clients[client]
del self.clients[client]
for bclient in list(self.bad_clients.keys()):
if self.bad_clients[bclient] in self.profiles:
- self.logger.info("Restored profile mapping for client %s" % bclient)
+ self.logger.info("Restored profile mapping for client %s" %
+ bclient)
self.clients[bclient] = self.bad_clients[bclient]
del self.bad_clients[bclient]
@@ -546,42 +517,37 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if False in list(self.states.values()):
raise MetadataRuntimeError
if profile not in self.public:
- self.logger.error("Failed to set client %s to private group %s" % (client, profile))
+ self.logger.error("Failed to set client %s to private group %s" %
+ (client, profile))
raise MetadataConsistencyError
if client in self.clients:
- self.logger.info("Changing %s group from %s to %s" % (client, self.clients[client], profile))
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (client))
- if not xdict:
- self.logger.error("Metadata: Unable to update profile for client %s. Use of Xinclude?" % client)
- raise MetadataConsistencyError
- xdict['xquery'][0].set('profile', profile)
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ self.logger.info("Changing %s group from %s to %s" %
+ (client, self.clients[client], profile))
+ self.update_client(client, dict(profile=profile))
else:
- self.logger.info("Creating new client: %s, profile %s" % \
+ self.logger.info("Creating new client: %s, profile %s" %
(client, profile))
if addresspair in self.session_cache:
# we are working with a uuid'd client
- lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- 'Client',
- name=self.session_cache[addresspair][1],
- uuid=client, profile=profile,
- address=addresspair[0])
+ self.add_client(self.session_cache[addresspair][1],
+ dict(uuid=client, profile=profile,
+ address=addresspair[0]))
else:
- lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- 'Client', name=client,
- profile=profile)
+ self.add_client(client, dict(profile=profile))
self.clients[client] = profile
self.clients_xml.write()
def resolve_client(self, addresspair, cleanup_cache=False):
"""Lookup address locally or in DNS to get a hostname."""
if addresspair in self.session_cache:
- # client _was_ cached, so there can be some expired entries
- # we need to clean them up to avoid potentially infinite memory swell
+ # client _was_ cached, so there can be some expired
+ # entries. we need to clean them up to avoid potentially
+ # infinite memory swell
cache_ttl = 90
if cleanup_cache:
- # remove entries for this client's IP address with _any_ port numbers
- # - perhaps a priority queue could be faster?
+ # remove entries for this client's IP address with
+ # _any_ port numbers - perhaps a priority queue could
+ # be faster?
curtime = time.time()
for addrpair in self.session_cache.keys():
if addresspair[0] == addrpair[0]:
@@ -589,13 +555,18 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if curtime - stamp > cache_ttl:
del self.session_cache[addrpair]
# return the cached data
- (stamp, uuid) = self.session_cache[addresspair]
- if time.time() - stamp < cache_ttl:
- return self.session_cache[addresspair][1]
+ try:
+ (stamp, uuid) = self.session_cache[addresspair]
+ if time.time() - stamp < cache_ttl:
+ return self.session_cache[addresspair][1]
+ except KeyError:
+ # we cleaned all cached data for this client in cleanup_cache
+ pass
address = addresspair[0]
if address in self.addresses:
if len(self.addresses[address]) != 1:
- self.logger.error("Address %s has multiple reverse assignments; a uuid must be used" % (address))
+ self.logger.error("Address %s has multiple reverse assignments; "
+ "a uuid must be used" % (address))
raise MetadataConsistencyError
return self.addresses[address][0]
try:
@@ -620,7 +591,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
(bundles, groups, categories) = self.groups[profile]
else:
if self.default == None:
- self.logger.error("Cannot set group for client %s; no default group set" % (client))
+ self.logger.error("Cannot set group for client %s; "
+ "no default group set" % client)
raise MetadataConsistencyError
self.set_profile(client, self.default, (None, None))
profile = self.default
@@ -635,7 +607,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
password = self.passwords[client]
else:
password = None
- uuids = [item for item, value in list(self.uuid.items()) if value == client]
+ uuids = [item for item, value in list(self.uuid.items())
+ if value == client]
if uuids:
uuid = uuids[0]
else:
@@ -649,7 +622,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
[newgroups.add(g) for g in ngroups if g not in newgroups]
newcategories.update(ncategories)
return ClientMetadata(client, profile, newgroups, newbundles, aliases,
- addresses, newcategories, uuid, password, self.query)
+ addresses, newcategories, uuid, password,
+ self.query)
def get_all_group_names(self):
all_groups = set()
@@ -673,22 +647,27 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def merge_additional_groups(self, imd, groups):
for group in groups:
- if group in self.categories and \
- self.categories[group] in imd.categories:
+ if (group in self.categories and
+ self.categories[group] in imd.categories):
continue
- nb, ng, _ = self.groups.get(group, (list(), [group], dict()))
- for b in nb:
- if b not in imd.bundles:
- imd.bundles.add(b)
- for g in ng:
- if g not in imd.groups:
- if g in self.categories and \
- self.categories[g] in imd.categories:
+ newbundles, newgroups, _ = self.groups.get(group,
+ (list(),
+ [group],
+ dict()))
+ for newbundle in newbundles:
+ if newbundle not in imd.bundles:
+ imd.bundles.add(newbundle)
+ for newgroup in newgroups:
+ if newgroup not in imd.groups:
+ if (newgroup in self.categories and
+ self.categories[newgroup] in imd.categories):
continue
- if g in self.private:
- self.logger.error("Refusing to add dynamic membership in private group %s for client %s" % (g, imd.hostname))
+ if newgroup in self.private:
+ self.logger.error("Refusing to add dynamic membership "
+ "in private group %s for client %s" %
+ (newgroup, imd.hostname))
continue
- imd.groups.add(g)
+ imd.groups.add(newgroup)
def merge_additional_data(self, imd, source, data):
if not hasattr(imd, source):
@@ -703,18 +682,19 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
return True
if address in self.addresses:
if client in self.addresses[address]:
- self.debug_log("Client %s matches address %s" % (client, address))
+ self.debug_log("Client %s matches address %s" %
+ (client, address))
return True
else:
- self.logger.error("Got request for non-float client %s from %s" \
- % (client, address))
+ self.logger.error("Got request for non-float client %s from %s" %
+ (client, address))
return False
resolved = self.resolve_client(addresspair)
if resolved.lower() == client.lower():
return True
else:
- self.logger.error("Got request for %s from incorrect address %s" \
- % (client, address))
+ self.logger.error("Got request for %s from incorrect address %s" %
+ (client, address))
self.logger.error("Resolved to %s" % resolved)
return False
@@ -732,7 +712,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
try:
client = self.resolve_client(address)
except MetadataConsistencyError:
- self.logger.error("Client %s failed to resolve; metadata problem" % (address[0]))
+ self.logger.error("Client %s failed to resolve; metadata problem"
+ % address[0])
return False
else:
id_method = 'uuid'
@@ -764,10 +745,12 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if client not in self.passwords:
if client in self.secure:
- self.logger.error("Client %s in secure mode but has no password" % (address[0]))
+ self.logger.error("Client %s in secure mode but has no password"
+ % address[0])
return False
if password != self.password:
- self.logger.error("Client %s used incorrect global password" % (address[0]))
+ self.logger.error("Client %s used incorrect global password" %
+ address[0])
return False
if client not in self.secure:
if client in self.passwords:
@@ -775,14 +758,14 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
else:
plist = [self.password]
if password not in plist:
- self.logger.error("Client %s failed to use either allowed password" % \
- (address[0]))
+ self.logger.error("Client %s failed to use either allowed "
+ "password" % address[0])
return False
else:
# client in secure mode and has a client password
if password != self.passwords[client]:
- self.logger.error("Client %s failed to use client password in secure mode" % \
- (address[0]))
+ self.logger.error("Client %s failed to use client password in "
+ "secure mode" % address[0])
return False
# populate the session cache
if user.decode('utf-8') != 'root':
@@ -793,13 +776,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
"""Hook into statistics interface to toggle clients in bootstrap mode."""
client = meta.hostname
if client in self.auth and self.auth[client] == 'bootstrap':
- self.logger.info("Asserting client %s auth mode to cert" % client)
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (client))
- if not xdict:
- self.logger.error("Metadata: Unable to update profile for client %s. Use of Xinclude?" % client)
- raise MetadataConsistencyError
- xdict['xquery'][0].set('auth', 'cert')
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ self.update_client(client, dict(auth='cert'))
def viz(self, hosts, bundles, key, only_client, colors):
"""Admin mode viz support."""
@@ -814,15 +791,16 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def include_group(group):
return not only_client or group in clientmeta.groups
-
- groups_tree = lxml.etree.parse(self.data + "/groups.xml")
+
+ groups_tree = lxml.etree.parse(os.path.join(self.data, "groups.xml"))
try:
groups_tree.xinclude()
except lxml.etree.XIncludeError:
- self.logger.error("Failed to process XInclude for file %s" % dest)
+ self.logger.error("Failed to process XInclude for file %s: %s" %
+ (dest, sys.exc_info()[1]))
groups = groups_tree.getroot()
categories = {'default': 'grey83'}
- viz_str = ""
+ viz_str = []
egroups = groups.findall("Group") + groups.findall('.//Groups/Group')
for group in egroups:
if not group.get('category') in categories:
@@ -842,10 +820,10 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
instances[profile] = [client]
for profile, clist in list(instances.items()):
clist.sort()
- viz_str += '''\t"%s-instances" [ label="%s", shape="record" ];\n''' \
- % (profile, '|'.join(clist))
- viz_str += '''\t"%s-instances" -> "group-%s";\n''' \
- % (profile, profile)
+ viz_str.append('"%s-instances" [ label="%s", shape="record" ];' %
+ (profile, '|'.join(clist)))
+ viz_str.append('"%s-instances" -> "group-%s";' %
+ (profile, profile))
if bundles:
bundles = []
[bundles.append(bund.get('name')) \
@@ -854,8 +832,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
and include_bundle(bund.get('name'))]
bundles.sort()
for bundle in bundles:
- viz_str += '''\t"bundle-%s" [ label="%s", shape="septagon"];\n''' \
- % (bundle, bundle)
+ viz_str.append('"bundle-%s" [ label="%s", shape="septagon"];' %
+ (bundle, bundle))
gseen = []
for group in egroups:
if group.get('profile', 'false') == 'true':
@@ -864,24 +842,25 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
style = "filled"
gseen.append(group.get('name'))
if include_group(group.get('name')):
- viz_str += '\t"group-%s" [label="%s", style="%s", fillcolor=%s];\n' % \
- (group.get('name'), group.get('name'), style, group.get('color'))
+ viz_str.append('"group-%s" [label="%s", style="%s", fillcolor=%s];' %
+ (group.get('name'), group.get('name'), style,
+ group.get('color')))
if bundles:
for bundle in group.findall('Bundle'):
- viz_str += '\t"group-%s" -> "bundle-%s";\n' % \
- (group.get('name'), bundle.get('name'))
- gfmt = '\t"group-%s" [label="%s", style="filled", fillcolor="grey83"];\n'
+ viz_str.append('"group-%s" -> "bundle-%s";' %
+ (group.get('name'), bundle.get('name')))
+ gfmt = '"group-%s" [label="%s", style="filled", fillcolor="grey83"];'
for group in egroups:
for parent in group.findall('Group'):
if parent.get('name') not in gseen and include_group(parent.get('name')):
- viz_str += gfmt % (parent.get('name'), parent.get('name'))
+ viz_str.append(gfmt % (parent.get('name'),
+ parent.get('name')))
gseen.append(parent.get("name"))
if include_group(group.get('name')):
- viz_str += '\t"group-%s" -> "group-%s" ;\n' % \
- (group.get('name'), parent.get('name'))
+ viz_str.append('"group-%s" -> "group-%s";' %
+ (group.get('name'), parent.get('name')))
if key:
for category in categories:
- viz_str += '''\t"''' + category + '''" [label="''' + category + \
- '''", shape="record", style="filled", fillcolor=''' + \
- categories[category] + '''];\n'''
- return viz_str
+ viz_str.append('"%s" [label="%s", shape="record", style="filled", fillcolor="%s"];' %
+ (category, category, categories[category]))
+ return "\n".join("\t" + s for s in viz_str)
diff --git a/src/lib/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
index 8a76c130d..4dbd57d16 100644
--- a/src/lib/Server/Plugins/NagiosGen.py
+++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
@@ -20,14 +20,13 @@ class NagiosGenConfig(Bcfg2.Server.Plugin.SingleXMLFileBacked,
Bcfg2.Server.Plugin.SingleXMLFileBacked.__init__(self, filename, fam)
Bcfg2.Server.Plugin.StructFile.__init__(self, filename)
-
+
class NagiosGen(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Generator):
"""NagiosGen is a Bcfg2 plugin that dynamically generates
Nagios configuration file based on Bcfg2 data.
"""
name = 'NagiosGen'
- __version__ = '0.7'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
@@ -131,14 +130,14 @@ class NagiosGen(Bcfg2.Server.Plugin.Plugin,
group_data = []
for host in host_configs:
host_data.append(open(host, 'r').read())
-
+
for group in group_configs:
group_name = re.sub("(-group.cfg|.*/(?=[^/]+))", "", group)
if "\n".join(host_data).find(group_name) != -1:
groupfile = open(group, 'r')
group_data.append(groupfile.read())
groupfile.close()
-
+
entry.text = "%s\n\n%s" % ("\n".join(group_data), "\n".join(host_data))
[entry.attrib.__setitem__(key, value)
for (key, value) in list(self.server_attrib.items())]
diff --git a/src/lib/Server/Plugins/Ohai.py b/src/lib/Bcfg2/Server/Plugins/Ohai.py
index 5fff20d98..5fff20d98 100644
--- a/src/lib/Server/Plugins/Ohai.py
+++ b/src/lib/Bcfg2/Server/Plugins/Ohai.py
diff --git a/src/lib/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
index f76bf7fa1..cbe2b4f2c 100644
--- a/src/lib/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -12,6 +12,7 @@ class AptCollection(Collection):
class AptSource(Source):
basegroups = ['apt', 'debian', 'ubuntu', 'nexenta']
ptype = 'deb'
+ essentialpkgs = set()
def __init__(self, basepath, xsource, config):
Source.__init__(self, basepath, xsource, config)
@@ -50,10 +51,9 @@ class AptSource(Source):
def read_files(self):
bdeps = dict()
bprov = dict()
+ depfnames = ['Depends', 'Pre-Depends']
if self.recommended:
- depfnames = ['Depends', 'Pre-Depends', 'Recommends']
- else:
- depfnames = ['Depends', 'Pre-Depends']
+ depfnames.append('Recommends')
for fname in self.files:
if not self.rawurl:
barch = [x
@@ -77,6 +77,8 @@ class AptSource(Source):
pkgname = words[1].strip().rstrip()
self.pkgnames.add(pkgname)
bdeps[barch][pkgname] = []
+ elif words[0] == 'Essential' and self.essential:
+ self.essentialpkgs.add(pkgname)
elif words[0] in depfnames:
vindex = 0
for dep in words[1].split(','):
diff --git a/src/lib/Server/Plugins/Packages/Collection.py b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
index 32eeda1ec..959dac03b 100644
--- a/src/lib/Server/Plugins/Packages/Collection.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
@@ -97,6 +97,12 @@ class Collection(Bcfg2.Server.Plugin.Debuggable):
return source.get_deps(self.metadata, package)
return []
+ def get_essential(self):
+ essential = set()
+ for source in self.sources:
+ essential |= source.essentialpkgs
+ return essential
+
def get_provides(self, package):
for source in self.sources:
providers = source.get_provides(self.metadata, package)
@@ -295,7 +301,7 @@ def factory(metadata, sources, basepath, debug=False):
# if sources.xml has not received a FAM event yet, defer;
# instantiate a dummy Collection object
return Collection(metadata, [], basepath)
-
+
sclasses = set()
relevant = list()
diff --git a/src/lib/Server/Plugins/Packages/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
index 9db6b0535..99a090739 100644
--- a/src/lib/Server/Plugins/Packages/Pac.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
@@ -6,7 +6,7 @@ from Bcfg2.Server.Plugins.Packages.Source import Source
class PacCollection(Collection):
def get_group(self, group):
- self.logger.warning("Packages: Package groups are not supported by APT")
+ self.logger.warning("Packages: Package groups are not supported by Pacman")
return []
class PacSource(Source):
@@ -51,10 +51,9 @@ class PacSource(Source):
bdeps = dict()
bprov = dict()
+ depfnames = ['Depends', 'Pre-Depends']
if self.recommended:
- depfnames = ['Depends', 'Pre-Depends', 'Recommends']
- else:
- depfnames = ['Depends', 'Pre-Depends']
+ depfnames.append('Recommends')
for fname in self.files:
if not self.rawurl:
@@ -63,7 +62,7 @@ class PacSource(Source):
# RawURL entries assume that they only have one <Arch></Arch>
# element and that it is the architecture of the source.
barch = self.arches[0]
-
+
if barch not in bdeps:
bdeps[barch] = dict()
bprov[barch] = dict()
diff --git a/src/lib/Server/Plugins/Packages/PackagesConfig.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesConfig.py
index 7950f15e6..3846c06ce 100644
--- a/src/lib/Server/Plugins/Packages/PackagesConfig.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesConfig.py
@@ -2,7 +2,7 @@ import Bcfg2.Server.Plugin
class PackagesConfig(Bcfg2.Server.Plugin.SimpleConfig):
_required = False
-
+
def Index(self):
""" Build local data structures """
Bcfg2.Server.Plugin.SimpleConfig.Index(self)
diff --git a/src/lib/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
index da79c00e9..8d0067b6a 100644
--- a/src/lib/Server/Plugins/Packages/PackagesSources.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
@@ -8,7 +8,7 @@ class PackagesSources(Bcfg2.Server.Plugin.SingleXMLFileBacked,
Bcfg2.Server.Plugin.StructFile,
Bcfg2.Server.Plugin.Debuggable):
__identifier__ = None
-
+
def __init__(self, filename, cachepath, fam, packages, config):
Bcfg2.Server.Plugin.Debuggable.__init__(self)
try:
@@ -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/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
index f13267da1..a7c246940 100644
--- a/src/lib/Server/Plugins/Packages/Source.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
@@ -54,8 +54,9 @@ class Source(Bcfg2.Server.Plugin.Debuggable):
self.gpgkeys = [el.text for el in xsource.findall("GPGKey")]
+ self.essential = xsource.get('essential', 'true').lower() == 'true'
self.recommended = xsource.get('recommended', 'false').lower() == 'true'
-
+
self.rawurl = xsource.get('rawurl', '')
if self.rawurl and not self.rawurl.endswith("/"):
self.rawurl += "/"
@@ -112,7 +113,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable):
if os.path.exists(self.cachefile):
try:
self.load_state()
- should_read = False
+ should_read = False
except:
self.logger.error("Packages: Cachefile %s load failed; "
"falling back to file read" % self.cachefile)
diff --git a/src/lib/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index e13b28251..b39b6aed2 100644
--- a/src/lib/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -68,10 +68,10 @@ def _setup_pulp(config):
logger.error("Packages: Required option not found in "
"Packages/packages.conf: %s" % err)
raise Bcfg2.Server.Plugin.PluginInitError
-
+
PULPCONFIG = ConsumerConfig()
serveropts = PULPCONFIG.server
-
+
PULPSERVER = server.PulpServer(serveropts['host'],
int(serveropts['port']),
serveropts['scheme'],
@@ -85,7 +85,7 @@ class YumCollection(Collection):
# options that are included in the [yum] section but that should
# not be included in the temporary yum.conf we write out
option_blacklist = ["use_yum_libraries", "helper"]
-
+
def __init__(self, metadata, sources, basepath, debug=False):
Collection.__init__(self, metadata, sources, basepath, debug=debug)
self.keypath = os.path.join(self.basepath, "keys")
@@ -103,7 +103,7 @@ class YumCollection(Collection):
"cache-%s" % self.cachekey)
if not os.path.exists(self.cachefile):
os.mkdir(self.cachefile)
-
+
self.configdir = os.path.join(self.basepath, "yum")
if not os.path.exists(self.configdir):
os.mkdir(self.configdir)
@@ -120,7 +120,7 @@ class YumCollection(Collection):
if not os.path.exists(self.cfgfile):
yumconf = self.get_config(raw=True)
yumconf.add_section("main")
-
+
mainopts = dict(cachedir=self.cachefile,
keepcache="0",
sslverify="0",
@@ -160,7 +160,7 @@ class YumCollection(Collection):
else:
rid = 1
reponame = "%s-%d" % (basereponame, rid)
-
+
config.set(reponame, "name", reponame)
config.set(reponame, "baseurl", url_map['url'])
config.set(reponame, "enabled", "1")
@@ -220,7 +220,7 @@ class YumCollection(Collection):
remotekey = os.path.join(keydir, os.path.basename(key))
localkey = os.path.join(self.keypath, os.path.basename(key))
kdata = open(localkey).read()
-
+
# copy the key to the client
keypath = lxml.etree.Element("BoundPath", name=remotekey,
encoding='ascii',
@@ -293,12 +293,12 @@ class YumCollection(Collection):
# include gpg key data
if not has_yum:
return
-
+
try:
kinfo = yum.misc.getgpgkeyinfo(keydata)
version = yum.misc.keyIdToRPMVer(kinfo['keyid'])
release = yum.misc.keyIdToRPMVer(kinfo['timestamp'])
-
+
lxml.etree.SubElement(keyentry, 'Instance',
version=version,
release=release,
@@ -378,7 +378,7 @@ class YumCollection(Collection):
unknown = set([str(p) for p in result['unknown']])
self.filter_unknown(unknown)
-
+
return packages, unknown
def call_helper(self, command, input=None):
@@ -404,7 +404,7 @@ class YumCollection(Collection):
self.logger.error("Packages: Failed to execute %s: %s" %
(" ".join(cmd), err))
return None
-
+
if input:
idata = json.dumps(input)
(stdout, stderr) = helper.communicate(idata)
@@ -436,7 +436,7 @@ class YumCollection(Collection):
os.unlink(self.cfgfile)
self.write_config()
-
+
if force_update:
self.call_helper("clean")
@@ -450,7 +450,7 @@ class YumSource(Source):
self.pulp_id = None
if has_pulp and xsource.get("pulp_id"):
self.pulp_id = xsource.get("pulp_id")
-
+
_setup_pulp(self.config)
repoapi = RepositoryAPI()
try:
@@ -478,7 +478,7 @@ class YumSource(Source):
self.rawurl = "%s/%s" % (PULPCONFIG.cds['baseurl'],
self.repo['relative_path'])
self.arches = [self.repo['arch']]
-
+
if not self.rawurl:
self.baseurl = self.url + "%(version)s/%(component)s/%(arch)s/"
else:
@@ -500,7 +500,7 @@ class YumSource(Source):
cPickle.dump((self.packages, self.deps, self.provides,
self.filemap, self.url_map), cache, 2)
cache.close()
-
+
def load_state(self):
if not self.use_yum:
@@ -516,7 +516,7 @@ class YumSource(Source):
usettings = [{'version':self.version, 'component':comp,
'arch':arch}
for comp in self.components]
- else: # rawurl given
+ else: # rawurl given
usettings = [{'version':self.version, 'component':None,
'arch':arch}]
@@ -534,7 +534,7 @@ class YumSource(Source):
def _get_urls_from_repodata(self, url, arch):
if self.use_yum:
return [url]
-
+
rmdurl = '%srepodata/repomd.xml' % url
try:
repomd = fetch_url(rmdurl)
@@ -651,7 +651,7 @@ class YumSource(Source):
def get_vpkgs(self, metadata):
if self.use_yum:
return dict()
-
+
rv = Source.get_vpkgs(self, metadata)
for arch, fmdata in list(self.filemap.items()):
if arch not in metadata.groups and arch != 'global':
diff --git a/src/lib/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
index da5832e90..e4793a28d 100644
--- a/src/lib/Server/Plugins/Packages/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
@@ -88,6 +88,9 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
if entry.tag == 'Package':
collection = self._get_collection(metadata)
entry.set('version', 'auto')
+ entry.set('version', self.config.get("global",
+ "version",
+ default="auto"))
entry.set('type', collection.ptype)
elif entry.tag == 'Path':
if (entry.get("name") == self.config.get("global", "yum_config",
@@ -143,6 +146,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
initial = set()
# base is the set of initial packages with groups expanded
base = set()
+ # essential pkgs are those marked as such by the distribution
+ essential = collection.get_essential()
to_remove = []
for struct in structures:
for pkg in struct.xpath('//Package | //BoundPackage'):
@@ -164,7 +169,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
else:
self.logger.error("Packages: Malformed Package: %s" %
lxml.etree.tostring(pkg))
- base.update(initial)
+ base.update(initial | essential)
for el in to_remove:
el.getparent().remove(el)
@@ -178,8 +183,9 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
newpkgs.sort()
for pkg in newpkgs:
lxml.etree.SubElement(independent, 'BoundPackage', name=pkg,
- version='auto', type=collection.ptype,
- origin='Packages')
+ version=self.config.get("global", "version",
+ default="auto"),
+ type=collection.ptype, origin='Packages')
def Refresh(self):
'''Packages.Refresh() => True|False\nReload configuration
diff --git a/src/lib/Server/Plugins/Pkgmgr.py b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py
index bc11bfdcf..e9254cdcc 100644
--- a/src/lib/Server/Plugins/Pkgmgr.py
+++ b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py
@@ -1,5 +1,4 @@
'''This module implements a package management scheme for all images'''
-__revision__ = '$Revision$'
import logging
import re
@@ -135,7 +134,6 @@ class PkgSrc(Bcfg2.Server.Plugin.XMLSrc):
class Pkgmgr(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles package assignments."""
name = 'Pkgmgr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = PkgSrc
__element__ = 'Package'
diff --git a/src/lib/Server/Plugins/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py
index ae1ed4c2b..af908eee8 100644
--- a/src/lib/Server/Plugins/Probes.py
+++ b/src/lib/Bcfg2/Server/Plugins/Probes.py
@@ -51,10 +51,10 @@ class ProbeData(object):
def __str__(self):
return str(self.data)
-
+
def __repr__(self):
return repr(self.data)
-
+
def __getattr__(self, name):
""" make ProbeData act like a str object """
return getattr(self.data, name)
@@ -181,7 +181,6 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector):
"""A plugin to gather information from a client machine."""
name = 'Probes'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Bcfg2/Server/Plugins/Properties.py
index 76945b3a0..680881858 100644
--- a/src/lib/Server/Plugins/Properties.py
+++ b/src/lib/Bcfg2/Server/Plugins/Properties.py
@@ -60,7 +60,6 @@ class Properties(Bcfg2.Server.Plugin.Plugin,
files into client metadata instances.
"""
name = 'Properties'
- version = '$Revision$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Rules.py b/src/lib/Bcfg2/Server/Plugins/Rules.py
index c66276179..b80ef351a 100644
--- a/src/lib/Server/Plugins/Rules.py
+++ b/src/lib/Bcfg2/Server/Plugins/Rules.py
@@ -1,5 +1,4 @@
"""This generator provides rule-based entry mappings."""
-__revision__ = '$Revision$'
import re
import Bcfg2.Server.Plugin
@@ -10,7 +9,6 @@ class RulesConfig(Bcfg2.Server.Plugin.SimpleConfig):
class Rules(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
name = 'Rules'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
@@ -52,6 +50,6 @@ class Rules(Bcfg2.Server.Plugin.PrioDir):
if self._regex_cache[rule].match(entry.get('name')):
return True
return False
-
+
def _regex_enabled(self):
return self.config.getboolean("rules", "regex", default=False)
diff --git a/src/lib/Server/Plugins/SGenshi.py b/src/lib/Bcfg2/Server/Plugins/SGenshi.py
index f6a98c141..0ba08125e 100644
--- a/src/lib/Server/Plugins/SGenshi.py
+++ b/src/lib/Bcfg2/Server/Plugins/SGenshi.py
@@ -1,5 +1,4 @@
'''This module implements a templating generator based on Genshi'''
-__revision__ = '$Revision$'
import genshi.input
import genshi.template
@@ -84,7 +83,6 @@ class SGenshi(SGenshiEntrySet,
Bcfg2.Server.Plugin.Structure):
"""The SGenshi plugin provides templated structures."""
name = 'SGenshi'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
deprecated = True
diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
index eb91bea39..ac281ad1a 100644
--- a/src/lib/Server/Plugins/SSHbase.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
@@ -1,5 +1,4 @@
'''This module manages ssh key files for bcfg2'''
-__revision__ = '$Revision$'
import binascii
import re
@@ -23,7 +22,7 @@ class KeyData(Bcfg2.Server.Plugin.SpecificData):
Bcfg2.Server.Plugin.SpecificData.__init__(self, name, specific,
encoding)
self.encoding = encoding
-
+
def bind_entry(self, entry, metadata):
entry.set('type', 'file')
if entry.get('encoding') == 'base64':
@@ -99,7 +98,6 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
"""
name = 'SSHbase'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
keypatterns = ["ssh_host_dsa_key",
@@ -148,7 +146,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
for s in list(self.static.values())]
mquery = self.core.metadata.query
-
+
# build hostname cache
names = dict()
for cmeta in mquery.all():
@@ -206,13 +204,13 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
# have no clients yet. don't warn about
# this.
continue
-
+
if key not in self.badnames:
self.badnames[key] = True
self.logger.info("Ignoring key for unknown %s %s" %
(ktype, key))
continue
-
+
skn.append("%s %s" % (','.join(hostnames),
entry.data.decode().rstrip()))
@@ -230,7 +228,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
action = event.code2str()
if action == "endExist" or event.filename == self.data:
return
-
+
for entry in list(self.entries.values()):
if entry.specific.match(event.filename):
entry.handle_event(event)
@@ -371,7 +369,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
else:
self.logger.error("Unknown key filename: %s" % filename)
return
-
+
fileloc = "%s/%s" % (self.data, hostkey)
publoc = self.data + '/' + ".".join([hostkey.split('.')[0], 'pub',
"H_%s" % client])
diff --git a/src/lib/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
index 7b4a08ae1..0072dc62d 100644
--- a/src/lib/Server/Plugins/SSLCA.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
@@ -16,7 +16,6 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
management of ssl certificates and their keys.
"""
name = 'SSLCA'
- __version__ = '$Id:$'
__author__ = 'g.hagger@gmail.com'
__child__ = Bcfg2.Server.Plugin.FileBacked
key_specs = {}
diff --git a/src/lib/Server/Plugins/Snapshots.py b/src/lib/Bcfg2/Server/Plugins/Snapshots.py
index aeb3b9f74..aeb3b9f74 100644
--- a/src/lib/Server/Plugins/Snapshots.py
+++ b/src/lib/Bcfg2/Server/Plugins/Snapshots.py
diff --git a/src/lib/Server/Plugins/Statistics.py b/src/lib/Bcfg2/Server/Plugins/Statistics.py
index 9dbbeec28..265ef95a8 100644
--- a/src/lib/Server/Plugins/Statistics.py
+++ b/src/lib/Bcfg2/Server/Plugins/Statistics.py
@@ -1,5 +1,4 @@
'''This file manages the statistics collected by the BCFG2 Server'''
-__revision__ = '$Revision$'
import binascii
import copy
@@ -117,7 +116,6 @@ class Statistics(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.ThreadedStatistics,
Bcfg2.Server.Plugin.PullSource):
name = 'Statistics'
- __version__ = '$Id$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Svcmgr.py b/src/lib/Bcfg2/Server/Plugins/Svcmgr.py
index 6d25c1a6d..f4232ad5c 100644
--- a/src/lib/Server/Plugins/Svcmgr.py
+++ b/src/lib/Bcfg2/Server/Plugins/Svcmgr.py
@@ -1,5 +1,4 @@
"""This generator provides service mappings."""
-__revision__ = '$Revision$'
import Bcfg2.Server.Plugin
@@ -7,6 +6,5 @@ import Bcfg2.Server.Plugin
class Svcmgr(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
name = 'Svcmgr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
deprecated = True
diff --git a/src/lib/Server/Plugins/Svn.py b/src/lib/Bcfg2/Server/Plugins/Svn.py
index 9fd6f1051..ae43388ea 100644
--- a/src/lib/Server/Plugins/Svn.py
+++ b/src/lib/Bcfg2/Server/Plugins/Svn.py
@@ -12,7 +12,6 @@ class Svn(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Svn is a version plugin for dealing with Bcfg2 repos."""
name = 'Svn'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Svn2.py b/src/lib/Bcfg2/Server/Plugins/Svn2.py
index b8a8e6b7e..e4df9574f 100644
--- a/src/lib/Server/Plugins/Svn2.py
+++ b/src/lib/Bcfg2/Server/Plugins/Svn2.py
@@ -9,7 +9,6 @@ class Svn2(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Svn is a version plugin for dealing with Bcfg2 repos."""
name = 'Svn2'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
conflicts = ['Svn']
@@ -72,7 +71,8 @@ class Svn2(Bcfg2.Server.Plugin.Plugin,
self.revision = self.client.update(self.datastore, recurse=True)[0]
self.logger.info("Svn2: Commited changes. At %s" %
self.revision.number)
- except Exception, err:
+ except Exception:
+ err = sys.exc_info()[1]
# try to be smart about the error we got back
details = None
if "callback_ssl_server_trust_prompt" in str(err):
diff --git a/src/lib/Server/Plugins/TCheetah.py b/src/lib/Bcfg2/Server/Plugins/TCheetah.py
index 49be88881..8879fdef1 100644
--- a/src/lib/Server/Plugins/TCheetah.py
+++ b/src/lib/Bcfg2/Server/Plugins/TCheetah.py
@@ -1,5 +1,4 @@
'''This module implements a templating generator based on Cheetah'''
-__revision__ = '$Revision$'
import binascii
import logging
@@ -76,7 +75,6 @@ class TemplateFile:
class TCheetah(Bcfg2.Server.Plugin.GroupSpool):
"""The TCheetah generator implements a templating mechanism for configuration files."""
name = 'TCheetah'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filename_pattern = 'template'
es_child_cls = TemplateFile
diff --git a/src/lib/Server/Plugins/TGenshi.py b/src/lib/Bcfg2/Server/Plugins/TGenshi.py
index bc5e00400..c4dd40614 100644
--- a/src/lib/Server/Plugins/TGenshi.py
+++ b/src/lib/Bcfg2/Server/Plugins/TGenshi.py
@@ -1,5 +1,4 @@
"""This module implements a templating generator based on Genshi."""
-__revision__ = '$Revision$'
import binascii
import logging
@@ -115,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):
@@ -131,7 +130,6 @@ class TGenshi(Bcfg2.Server.Plugin.GroupSpool):
"""
name = 'TGenshi'
- __version__ = '$Id$'
__author__ = 'jeff@ocjtech.us'
filename_pattern = 'template\.(txt|newtxt|xml)'
es_child_cls = TemplateFile
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
new file mode 100644
index 000000000..2c0ee03e0
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -0,0 +1,83 @@
+import re
+import imp
+import sys
+import logging
+import Bcfg2.Server.Plugin
+
+logger = logging.getLogger(__name__)
+
+class HelperModule(Bcfg2.Server.Plugin.SpecificData):
+ _module_name_re = re.compile(r'([^/]+?)\.py')
+
+ def __init__(self, name, specific, encoding):
+ Bcfg2.Server.Plugin.SpecificData.__init__(self, name, specific,
+ encoding)
+ match = self._module_name_re.search(self.name)
+ if match:
+ self._module_name = match.group(1)
+ else:
+ self._module_name = name
+ self._attrs = []
+
+ def handle_event(self, event):
+ Bcfg2.Server.Plugin.SpecificData.handle_event(self, event)
+ try:
+ module = imp.load_source(self._module_name, self.name)
+ except:
+ err = sys.exc_info()[1]
+ logger.error("TemplateHelper: Failed to import %s: %s" %
+ (self.name, err))
+ return
+
+ if not hasattr(module, "__export__"):
+ logger.error("TemplateHelper: %s has no __export__ list" %
+ self.name)
+ return
+
+ for sym in module.__export__:
+ if sym not in self._attrs and hasattr(self, sym):
+ logger.warning("TemplateHelper: %s: %s is a reserved keyword, "
+ "skipping export" % (self.name, sym))
+ setattr(self, sym, getattr(module, sym))
+ # remove old exports
+ for sym in set(self._attrs) - set(module.__export__):
+ delattr(self, sym)
+
+ self._attrs = module.__export__
+
+
+class HelperSet(Bcfg2.Server.Plugin.EntrySet):
+ ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\.py[co])$")
+
+ def __init__(self, path, fam, encoding, plugin_name):
+ fpattern = '[0-9A-Za-z_\-]+\.py'
+ self.plugin_name = plugin_name
+ Bcfg2.Server.Plugin.EntrySet.__init__(self, fpattern, path,
+ HelperModule, encoding)
+ fam.AddMonitor(path, self)
+
+ def HandleEvent(self, event):
+ if (event.filename != self.path and
+ not self.ignore.match(event.filename)):
+ return self.handle_event(event)
+
+
+class TemplateHelper(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector):
+ """ A plugin to provide helper classes and functions to templates """
+ name = 'TemplateHelper'
+ __author__ = 'chris.a.st.pierre@gmail.com'
+
+ def __init__(self, core, datastore):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
+
+ try:
+ self.helpers = HelperSet(self.data, core.fam, core.encoding,
+ self.name)
+ except:
+ raise Bcfg2.Server.Plugin.PluginInitError
+
+ def get_additional_data(self, metadata):
+ return dict([(h._module_name, h)
+ for h in list(self.helpers.entries.values())])
diff --git a/src/lib/Server/Plugins/Trigger.py b/src/lib/Bcfg2/Server/Plugins/Trigger.py
index eb3310a4e..b0d21545c 100644
--- a/src/lib/Server/Plugins/Trigger.py
+++ b/src/lib/Bcfg2/Server/Plugins/Trigger.py
@@ -17,7 +17,6 @@ class Trigger(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Statistics):
"""Trigger is a plugin that calls external scripts (on the server)."""
name = 'Trigger'
- __version__ = '$Id'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/__init__.py b/src/lib/Bcfg2/Server/Plugins/__init__.py
index c69c37452..f9f1b4e52 100644
--- a/src/lib/Server/Plugins/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/__init__.py
@@ -1,5 +1,4 @@
"""Imports for Bcfg2.Server.Plugins."""
-__revision__ = '$Revision$'
__all__ = [
'Account',
@@ -8,7 +7,7 @@ __all__ = [
'Bzr',
'Cfg',
'Cvs',
- 'Darcs',
+ 'Darcs',
'Decisions',
'Fossil',
'Git',
diff --git a/src/lib/Server/Reports/__init__.py b/src/lib/Bcfg2/Server/Reports/__init__.py
index bdf908f4a..bdf908f4a 100644
--- a/src/lib/Server/Reports/__init__.py
+++ b/src/lib/Bcfg2/Server/Reports/__init__.py
diff --git a/src/lib/Server/Reports/backends.py b/src/lib/Bcfg2/Server/Reports/backends.py
index 85241932f..85241932f 100644
--- a/src/lib/Server/Reports/backends.py
+++ b/src/lib/Bcfg2/Server/Reports/backends.py
diff --git a/src/lib/Server/Reports/importscript.py b/src/lib/Bcfg2/Server/Reports/importscript.py
index 7dfac6fae..ea89c70cd 100755
--- a/src/lib/Server/Reports/importscript.py
+++ b/src/lib/Bcfg2/Server/Reports/importscript.py
@@ -3,7 +3,6 @@
Imports statistics.xml and clients.xml files in to database backend for
new statistics engine
"""
-__revision__ = '$Revision$'
import binascii
import os
@@ -118,8 +117,6 @@ def load_stats(cdata, sdata, encoding, vlevel, logger, quick=False, location='')
default="unknown"),
repo_rev_code=statistics.get('revision',
default="unknown"),
- client_version=statistics.get('client_version',
- default="unknown"),
goodcount=statistics.get('good',
default="0"),
totalcount=statistics.get('total',
diff --git a/src/lib/Server/Reports/manage.py b/src/lib/Bcfg2/Server/Reports/manage.py
index 858bddeca..858bddeca 100755
--- a/src/lib/Server/Reports/manage.py
+++ b/src/lib/Bcfg2/Server/Reports/manage.py
diff --git a/src/lib/Server/Reports/nisauth.py b/src/lib/Bcfg2/Server/Reports/nisauth.py
index 6fc346f1e..b3e37113b 100644
--- a/src/lib/Server/Reports/nisauth.py
+++ b/src/lib/Bcfg2/Server/Reports/nisauth.py
@@ -4,8 +4,6 @@ from Bcfg2.Server.Reports.settings import AUTHORIZED_GROUP
"""Checks with NIS to see if the current user is in the support group"""
-__revision__ = "$Revision: $"
-
class NISAUTHError(Exception):
"""NISAUTHError is raised when somehting goes boom."""
diff --git a/src/lib/Server/Reports/reports/__init__.py b/src/lib/Bcfg2/Server/Reports/reports/__init__.py
index ccdce8943..ccdce8943 100644
--- a/src/lib/Server/Reports/reports/__init__.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/__init__.py
diff --git a/src/lib/Server/Reports/reports/fixtures/initial_version.xml b/src/lib/Bcfg2/Server/Reports/reports/fixtures/initial_version.xml
index 919265d48..bde236989 100644
--- a/src/lib/Server/Reports/reports/fixtures/initial_version.xml
+++ b/src/lib/Bcfg2/Server/Reports/reports/fixtures/initial_version.xml
@@ -36,4 +36,8 @@
<field type='IntegerField' name='version'>18</field>
<field type='DateTimeField' name='updated'>2011-06-30 00:00:00</field>
</object>
+ <object pk="8" model="reports.internaldatabaseversion">
+ <field type='IntegerField' name='version'>19</field>
+ <field type='DateTimeField' name='updated'>2012-03-28 00:00:00</field>
+ </object>
</django-objects>
diff --git a/src/lib/Server/Reports/reports/models.py b/src/lib/Bcfg2/Server/Reports/reports/models.py
index 870239641..d141846a2 100644
--- a/src/lib/Server/Reports/reports/models.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/models.py
@@ -156,7 +156,6 @@ class Interaction(models.Model):
timestamp = models.DateTimeField() # Timestamp for this record
state = models.CharField(max_length=32) # good/bad/modified/etc
repo_rev_code = models.CharField(max_length=64) # repo revision at time of interaction
- client_version = models.CharField(max_length=32) # Client Version
goodcount = models.IntegerField() # of good config-items
totalcount = models.IntegerField() # of total config-items
server = models.CharField(max_length=256) # Name of the server used for the interaction
diff --git a/src/lib/Server/Reports/reports/sql/client.sql b/src/lib/Bcfg2/Server/Reports/reports/sql/client.sql
index 8c63754c9..8c63754c9 100644
--- a/src/lib/Server/Reports/reports/sql/client.sql
+++ b/src/lib/Bcfg2/Server/Reports/reports/sql/client.sql
diff --git a/src/lib/Server/Reports/reports/templates/404.html b/src/lib/Bcfg2/Server/Reports/reports/templates/404.html
index 168bd9fec..168bd9fec 100644
--- a/src/lib/Server/Reports/reports/templates/404.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/404.html
diff --git a/src/lib/Server/Reports/reports/templates/base-timeview.html b/src/lib/Bcfg2/Server/Reports/reports/templates/base-timeview.html
index 842de36f0..842de36f0 100644
--- a/src/lib/Server/Reports/reports/templates/base-timeview.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/base-timeview.html
diff --git a/src/lib/Server/Reports/reports/templates/base.html b/src/lib/Bcfg2/Server/Reports/reports/templates/base.html
index f541c0d2b..f541c0d2b 100644
--- a/src/lib/Server/Reports/reports/templates/base.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/base.html
diff --git a/src/lib/Server/Reports/reports/templates/clients/detail.html b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/detail.html
index dd4295f21..dd4295f21 100644
--- a/src/lib/Server/Reports/reports/templates/clients/detail.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/detail.html
diff --git a/src/lib/Server/Reports/reports/templates/clients/detailed-list.html b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/detailed-list.html
index 0c1fae8d5..0c1fae8d5 100644
--- a/src/lib/Server/Reports/reports/templates/clients/detailed-list.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/detailed-list.html
diff --git a/src/lib/Server/Reports/reports/templates/clients/history.html b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/history.html
index 01d4ec2f4..01d4ec2f4 100644
--- a/src/lib/Server/Reports/reports/templates/clients/history.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/history.html
diff --git a/src/lib/Server/Reports/reports/templates/clients/index.html b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/index.html
index e0c0d2d7a..e0c0d2d7a 100644
--- a/src/lib/Server/Reports/reports/templates/clients/index.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/index.html
diff --git a/src/lib/Server/Reports/reports/templates/clients/manage.html b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/manage.html
index 5725ae577..5725ae577 100644
--- a/src/lib/Server/Reports/reports/templates/clients/manage.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/clients/manage.html
diff --git a/src/lib/Server/Reports/reports/templates/config_items/item.html b/src/lib/Bcfg2/Server/Reports/reports/templates/config_items/item.html
index cc99ef503..cc99ef503 100644
--- a/src/lib/Server/Reports/reports/templates/config_items/item.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/config_items/item.html
diff --git a/src/lib/Server/Reports/reports/templates/config_items/listing.html b/src/lib/Bcfg2/Server/Reports/reports/templates/config_items/listing.html
index 9b1026a08..9b1026a08 100644
--- a/src/lib/Server/Reports/reports/templates/config_items/listing.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/config_items/listing.html
diff --git a/src/lib/Server/Reports/reports/templates/displays/summary.html b/src/lib/Bcfg2/Server/Reports/reports/templates/displays/summary.html
index b9847cf96..b9847cf96 100644
--- a/src/lib/Server/Reports/reports/templates/displays/summary.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/displays/summary.html
diff --git a/src/lib/Server/Reports/reports/templates/displays/timing.html b/src/lib/Bcfg2/Server/Reports/reports/templates/displays/timing.html
index 47accb2cb..ff775ded5 100644
--- a/src/lib/Server/Reports/reports/templates/displays/timing.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/displays/timing.html
@@ -20,7 +20,7 @@
<td>Install</td>
<td>Config</td>
<td>Total</td>
- </tr>
+ </tr>
{% for metric in metrics|dictsort:"name" %}
<tr class='{% cycle listview,listview_alt %}'>
<td><a style='font-size: 100%'
diff --git a/src/lib/Server/Reports/reports/templates/widgets/filter_bar.html b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/filter_bar.html
index 6b57baf6a..6fbe585ab 100644
--- a/src/lib/Server/Reports/reports/templates/widgets/filter_bar.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/filter_bar.html
@@ -2,7 +2,7 @@
{% if filters %}
{% for filter, filter_url in filters %}
{% if forloop.first %}
- <div class="filter_bar">Active filters (click to remove):
+ <div class="filter_bar">Active filters (click to remove):
{% endif %}
<a href='{{ filter_url }}'>{{ filter|capfirst }}</a>{% if not forloop.last %}, {% endif %}
{% if forloop.last %}
diff --git a/src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/interaction_list.inc
index 8f2dec1dc..8f2dec1dc 100644
--- a/src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/interaction_list.inc
diff --git a/src/lib/Server/Reports/reports/templates/widgets/page_bar.html b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/page_bar.html
index aa0def83e..aa0def83e 100644
--- a/src/lib/Server/Reports/reports/templates/widgets/page_bar.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/widgets/page_bar.html
diff --git a/src/lib/Server/Reports/reports/templatetags/__init__.py b/src/lib/Bcfg2/Server/Reports/reports/templatetags/__init__.py
index e69de29bb..e69de29bb 100644
--- a/src/lib/Server/Reports/reports/templatetags/__init__.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/templatetags/__init__.py
diff --git a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py b/src/lib/Bcfg2/Server/Reports/reports/templatetags/bcfg2_tags.py
index 629984f26..f738f7bdd 100644
--- a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/templatetags/bcfg2_tags.py
@@ -81,7 +81,7 @@ def page_navigator(context):
fragment['pager'] = pager
fragment['page_limits'] = page_limits
-
+
except Resolver404:
path = "404"
except NoReverseMatch:
@@ -155,7 +155,7 @@ def isstale(timestamp, entry_max=None):
"""
Check for a stale timestamp
- Compares two timestamps and returns True if the
+ Compares two timestamps and returns True if the
difference is greater then 24 hours.
"""
if not entry_max:
diff --git a/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py b/src/lib/Bcfg2/Server/Reports/reports/templatetags/syntax_coloring.py
index 2e30125f9..2e30125f9 100644
--- a/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/templatetags/syntax_coloring.py
diff --git a/src/lib/Server/Reports/reports/urls.py b/src/lib/Bcfg2/Server/Reports/reports/urls.py
index 434ce07b7..434ce07b7 100644
--- a/src/lib/Server/Reports/reports/urls.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/urls.py
diff --git a/src/lib/Server/Reports/reports/views.py b/src/lib/Bcfg2/Server/Reports/reports/views.py
index ccd71a60e..ccd71a60e 100644
--- a/src/lib/Server/Reports/reports/views.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/views.py
diff --git a/src/lib/Server/Reports/settings.py b/src/lib/Bcfg2/Server/Reports/settings.py
index 952e3eae6..4d567f1a2 100644
--- a/src/lib/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:
@@ -124,8 +124,8 @@ except ImportError:
django.contrib.auth.LOGIN_URL = '/login'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
-
-
+
+
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates".
diff --git a/src/lib/Server/Reports/updatefix.py b/src/lib/Bcfg2/Server/Reports/updatefix.py
index 7cebaaca9..90eaacf89 100644
--- a/src/lib/Server/Reports/updatefix.py
+++ b/src/lib/Bcfg2/Server/Reports/updatefix.py
@@ -1,8 +1,9 @@
import Bcfg2.Server.Reports.settings
-from django.db import connection
+from django.db import connection, DatabaseError
import django.core.management
import logging
+import sys
import traceback
from Bcfg2.Server.Reports.reports.models import InternalDatabaseVersion, \
TYPE_BAD, TYPE_MODIFIED, TYPE_EXTRA
@@ -16,9 +17,9 @@ def _merge_database_table_entries():
find_cursor = connection.cursor()
cursor.execute("""
Select name, kind from reports_bad
- union
+ union
select name, kind from reports_modified
- union
+ union
select name, kind from reports_extra
""")
# this fetch could be better done
@@ -51,7 +52,7 @@ def _merge_database_table_entries():
def _interactions_constraint_or_idx():
- '''sqlite doesn't support alter tables.. or constraints'''
+ """sqlite doesn't support alter tables.. or constraints"""
cursor = connection.cursor()
try:
cursor.execute('alter table reports_interaction add constraint reports_interaction_20100601 unique (client_id,timestamp)')
@@ -59,6 +60,72 @@ def _interactions_constraint_or_idx():
cursor.execute('create unique index reports_interaction_20100601 on reports_interaction (client_id,timestamp)')
+def _remove_table_column(tbl, col):
+ """sqlite doesn't support deleting a column via alter table"""
+ cursor = connection.cursor()
+ try:
+ cursor.execute('alter table %s '
+ 'drop column %s;' % (tbl, col))
+ except DatabaseError:
+ # sqlite wants us to create a new table containing the columns we want
+ # and copy into it http://www.sqlite.org/faq.html#q11
+
+ tmptbl_name = "t_backup"
+ _tmptbl_create = \
+"""create temporary table "%s" (
+ "id" integer NOT NULL PRIMARY KEY,
+ "client_id" integer NOT NULL REFERENCES "reports_client" ("id"),
+ "timestamp" datetime NOT NULL,
+ "state" varchar(32) NOT NULL,
+ "repo_rev_code" varchar(64) NOT NULL,
+ "goodcount" integer NOT NULL,
+ "totalcount" integer NOT NULL,
+ "server" varchar(256) NOT NULL,
+ "bad_entries" integer NOT NULL,
+ "modified_entries" integer NOT NULL,
+ "extra_entries" integer NOT NULL,
+ UNIQUE ("client_id", "timestamp")
+);""" % tmptbl_name
+ _newtbl_create = \
+"""create table "%s" (
+ "id" integer NOT NULL PRIMARY KEY,
+ "client_id" integer NOT NULL REFERENCES "reports_client" ("id"),
+ "timestamp" datetime NOT NULL,
+ "state" varchar(32) NOT NULL,
+ "repo_rev_code" varchar(64) NOT NULL,
+ "goodcount" integer NOT NULL,
+ "totalcount" integer NOT NULL,
+ "server" varchar(256) NOT NULL,
+ "bad_entries" integer NOT NULL,
+ "modified_entries" integer NOT NULL,
+ "extra_entries" integer NOT NULL,
+ UNIQUE ("client_id", "timestamp")
+);""" % tbl
+ new_cols = "id,\
+ client_id,\
+ timestamp,\
+ state,\
+ repo_rev_code,\
+ goodcount,\
+ totalcount,\
+ server,\
+ bad_entries,\
+ modified_entries,\
+ extra_entries"
+
+ delete_col = [_tmptbl_create,
+ "insert into %s select %s from %s;" % (tmptbl_name, new_cols, tbl),
+ "drop table %s" % tbl,
+ _newtbl_create,
+ "create index reports_interaction_client_id on %s (client_id);" % tbl,
+ "insert into %s select %s from %s;" % (tbl, new_cols,
+ tmptbl_name),
+ "drop table %s;" % tmptbl_name]
+
+ for sql in delete_col:
+ cursor.execute(sql)
+
+
def _populate_interaction_entry_counts():
'''Populate up the type totals for the interaction table'''
cursor = connection.cursor()
@@ -103,6 +170,7 @@ _fixes = [_merge_database_table_entries,
_interactions_constraint_or_idx,
'alter table reports_reason add is_binary bool NOT NULL default False;',
'alter table reports_reason add is_sensitive bool NOT NULL default False;',
+ _remove_table_column('reports_interaction', 'client_version'),
]
# this will calculate the last possible version of the database
@@ -110,7 +178,7 @@ lastversion = len(_fixes)
def rollupdate(current_version):
- """ function responsible to coordinates all the updates
+ """function responsible to coordinates all the updates
need current_version as integer
"""
ret = None
@@ -122,8 +190,10 @@ def rollupdate(current_version):
else:
_fixes[i]()
except:
- logger.error("Failed to perform db update %s" % (_fixes[i]), exc_info=1)
- # since array start at 0 but version start at 1 we add 1 to the normal count
+ logger.error("Failed to perform db update %s" % (_fixes[i]),
+ exc_info=1)
+ # since the array starts at 0 but version
+ # starts at 1 we add 1 to the normal count
ret = InternalDatabaseVersion.objects.create(version=i + 1)
return ret
else:
@@ -135,16 +205,19 @@ def dosync():
# try to detect if it's a fresh new database
try:
cursor = connection.cursor()
- # If this table goes missing then don't forget to change it to the new one
+ # If this table goes missing,
+ # don't forget to change it to the new one
cursor.execute("Select * from reports_client")
# if we get here with no error then the database has existing tables
fresh = False
except:
- logger.debug("there was an error while detecting the freshness of the database")
+ logger.debug("there was an error while detecting "
+ "the freshness of the database")
#we should get here if the database is new
fresh = True
- # ensure database connection are close, so that the management can do it's job right
+ # ensure database connections are closed
+ # so that the management can do its job right
try:
cursor.close()
connection.close()
@@ -169,7 +242,8 @@ def dosync():
def update_database():
- ''' methode to search where we are in the revision of the database models and update them '''
+ """method to search where we are in the revision
+ of the database models and update them"""
try:
logger.debug("Running upgrade of models to the new one")
dosync()
diff --git a/src/lib/Server/Reports/urls.py b/src/lib/Bcfg2/Server/Reports/urls.py
index d7ff1eee5..d7ff1eee5 100644
--- a/src/lib/Server/Reports/urls.py
+++ b/src/lib/Bcfg2/Server/Reports/urls.py
diff --git a/src/lib/Server/Reports/utils.py b/src/lib/Bcfg2/Server/Reports/utils.py
index e0b6ead59..e0b6ead59 100755
--- a/src/lib/Server/Reports/utils.py
+++ b/src/lib/Bcfg2/Server/Reports/utils.py
diff --git a/src/lib/Server/Snapshots/__init__.py b/src/lib/Bcfg2/Server/Snapshots/__init__.py
index 7c901adb2..7c901adb2 100644
--- a/src/lib/Server/Snapshots/__init__.py
+++ b/src/lib/Bcfg2/Server/Snapshots/__init__.py
diff --git a/src/lib/Server/Snapshots/model.py b/src/lib/Bcfg2/Server/Snapshots/model.py
index 5d7973c16..5d7973c16 100644
--- a/src/lib/Server/Snapshots/model.py
+++ b/src/lib/Bcfg2/Server/Snapshots/model.py
diff --git a/src/lib/Server/__init__.py b/src/lib/Bcfg2/Server/__init__.py
index 25f397565..96777b0bf 100644
--- a/src/lib/Server/__init__.py
+++ b/src/lib/Bcfg2/Server/__init__.py
@@ -1,6 +1,4 @@
-# $Id$
"""This is the set of modules for Bcfg2.Server."""
-__revision__ = '$Revision$'
__all__ = ["Admin", "Core", "FileMonitor", "Plugin", "Plugins",
"Hostbase", "Reports", "Snapshots"]
diff --git a/src/lib/Statistics.py b/src/lib/Bcfg2/Statistics.py
index a0cb8f39b..a0cb8f39b 100644
--- a/src/lib/Statistics.py
+++ b/src/lib/Bcfg2/Statistics.py
diff --git a/src/lib/__init__.py b/src/lib/Bcfg2/__init__.py
index d36c0a00a..357f66f6d 100644
--- a/src/lib/__init__.py
+++ b/src/lib/Bcfg2/__init__.py
@@ -1,4 +1,3 @@
"""Base modules definition."""
-__revision__ = '$Revision$'
__all__ = ['Server', 'Client', 'Component', 'Logger', 'Options', 'Proxy', 'Statistics']
diff --git a/src/lib/Bcfg2Py3Incompat.py b/src/lib/Bcfg2Py3Incompat.py
deleted file mode 100644
index 6b66e72b0..000000000
--- a/src/lib/Bcfg2Py3Incompat.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def fprint(s, f):
- print(s, file=f)
diff --git a/src/lib/Client/Tools/Portage.py b/src/lib/Client/Tools/Portage.py
deleted file mode 100644
index 17163afa9..000000000
--- a/src/lib/Client/Tools/Portage.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""This is the Bcfg2 tool for the Gentoo Portage system."""
-__revision__ = '$Revision$'
-
-import re
-import Bcfg2.Client.Tools
-
-
-class Portage(Bcfg2.Client.Tools.PkgTool):
- """The Gentoo toolset implements package and service operations and
- inherits the rest from Toolset.Toolset."""
- name = 'Portage'
- __execs__ = ['/usr/bin/emerge', '/usr/bin/equery']
- __handles__ = [('Package', 'ebuild')]
- __req__ = {'Package': ['name', 'version']}
- pkgtype = 'ebuild'
- # requires a working PORTAGE_BINHOST in make.conf
- pkgtool = ('emerge --getbinpkgonly %s', ('=%s-%s', ['name', 'version']))
-
- def __init__(self, logger, cfg, setup):
- Bcfg2.Client.Tools.PkgTool.__init__(self, logger, cfg, setup)
- self.__important__ = self.__important__ + ['/etc/make.conf']
- self.cfg = cfg
- self.installed = {}
- self.RefreshPackages()
-
- def RefreshPackages(self):
- """Refresh memory hashes of packages."""
- ret, cache = self.cmd.run("equery -q list '*'")
- if ret == 2:
- cache = self.cmd.run("equery -q list '*'")[1]
- pattern = re.compile('(.*)-(\d.*)')
- self.installed = {}
- for pkg in cache:
- if pattern.match(pkg):
- name = pattern.match(pkg).group(1)
- version = pattern.match(pkg).group(2)
- self.installed[name] = version
- else:
- self.logger.info("Failed to parse pkg name %s" % pkg)
-
- def VerifyPackage(self, entry, modlist):
- """Verify package for entry."""
- if not 'version' in entry.attrib:
- self.logger.info("Cannot verify unversioned package %s" %
- (entry.attrib['name']))
- return False
- if entry.attrib['name'] in self.installed:
- if self.installed[entry.attrib['name']] == entry.attrib['version']:
- if not self.setup['quick'] and \
- entry.get('verify', 'true') == 'true':
- output = self.cmd.run("/usr/bin/equery -N check '=%s-%s' 2>&1 "
- "| grep '!!!' | awk '{print $2}'" \
- % (entry.get('name'), entry.get('version')))[1]
- if [filename for filename in output \
- if filename not in modlist]:
- return False
- return True
- else:
- entry.set('current_version', self.installed[entry.get('name')])
- return False
- entry.set('current_exists', 'false')
- return False
-
- def RemovePackages(self, packages):
- """Deal with extra configuration detected."""
- pkgnames = " ".join([pkg.get('name') for pkg in packages])
- if len(packages) > 0:
- self.logger.info('Removing packages:')
- self.logger.info(pkgnames)
- self.cmd.run("emerge --unmerge --quiet %s" % " ".join(pkgnames.split(' ')))
- self.RefreshPackages()
- self.extra = self.FindExtraPackages()
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 1d1cc8424..fb34e627b 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""Bcfg2 Client"""
-__revision__ = '$Revision$'
import fcntl
import logging
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 5cb69d747..d3b06733f 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -50,8 +50,7 @@ def main():
}
setup = Bcfg2.Options.OptionParser(optinfo)
# override default help message to include description of all modes
- setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(),
- create_description())
+ setup.hm = "%s\n%s" % (setup.buildHelpMessage(), create_description())
setup.parse(sys.argv[1:])
log_args = dict(to_syslog=False, to_console=logging.WARNING)
diff --git a/src/sbin/bcfg2-build-reports b/src/sbin/bcfg2-build-reports
index 7122fb300..7fa08110a 100755
--- a/src/sbin/bcfg2-build-reports
+++ b/src/sbin/bcfg2-build-reports
@@ -4,8 +4,6 @@
bcfg2-build-reports generates & distributes reports of statistic
information for Bcfg2."""
-__revision__ = '$Revision$'
-
import copy
import getopt
import re
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 3c1a0a3da..f4d199001 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-
"""This tool loads the Bcfg2 core into an interactive debugger."""
-__revision__ = '$Revision$'
from code import InteractiveConsole
import cmd
@@ -51,8 +49,7 @@ profile <command> <args> - Profile a single bcfg2-info command
quit - Exit the bcfg2-info command line
showentries <hostname> <type> - Show abstract configuration entries for a given host
showclient <client1> <client2> - Show metadata for given hosts
-update - Process pending file events
-version - Print version of this tool"""
+update - Process pending file events"""
BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir>
@@ -167,8 +164,13 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
print("Dropping to python interpreter; press ^D to resume")
try:
import IPython
- shell = IPython.Shell.IPShell(argv=[], user_ns=locals())
- shell.mainloop()
+ if hasattr(IPython, "Shell"):
+ shell = IPython.Shell.IPShell(argv=[], user_ns=locals())
+ shell.mainloop()
+ elif hasattr(IPython, "embed"):
+ IPython.embed(user_ns=locals())
+ else:
+ raise ImportError
except ImportError:
sh.interact()
@@ -192,10 +194,6 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
"""Process pending filesystem events."""
self.fam.handle_events_in_interval(0.1)
- def do_version(self, _):
- """Print out code version."""
- print(__revision__)
-
def do_build(self, args):
"""Build client configuration."""
alist = args.split()
@@ -528,7 +526,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:
@@ -552,27 +550,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-lint b/src/sbin/bcfg2-lint
index 2d371f4aa..ad6b6139c 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""This tool examines your Bcfg2 specifications for errors."""
-__revision__ = '$Revision$'
import sys
import inspect
diff --git a/src/sbin/bcfg2-ping-sweep b/src/sbin/bcfg2-ping-sweep
index 70f718690..be8994be3 100755
--- a/src/sbin/bcfg2-ping-sweep
+++ b/src/sbin/bcfg2-ping-sweep
@@ -3,8 +3,6 @@
"""Generates hostinfo.xml at a regular interval."""
-__revision__ = '$Revision$'
-
from os import dup2, execl, fork, uname, wait
import sys
import time
diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports
index 6acdd27e3..1f101b9a7 100755
--- a/src/sbin/bcfg2-reports
+++ b/src/sbin/bcfg2-reports
@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Query reporting system for client status."""
-__revision__ = '$Revision$'
import os
import sys
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index 546d5a249..89bc7c331 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""The XML-RPC Bcfg2 server."""
-__revision__ = '$Revision$'
import logging
import os.path
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/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper
index dc46bb81a..2da7c6336 100755
--- a/src/sbin/bcfg2-yum-helper
+++ b/src/sbin/bcfg2-yum-helper
@@ -5,8 +5,6 @@ the right way to get around that in long-running processes it to have
a short-lived helper. No, seriously -- check out the yum-updatesd
code. It's pure madness. """
-__revision__ = '$Revision$'
-
import os
import sys
import yum
diff --git a/testsuite/TestFrame.py b/testsuite/TestFrame.py
deleted file mode 100644
index a76c97eec..000000000
--- a/testsuite/TestFrame.py
+++ /dev/null
@@ -1,104 +0,0 @@
-import lxml.etree
-
-import Bcfg2.Client.Frame
-import Bcfg2.Client.Tools
-
-c1 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
-
-c2 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
-
-
-class DriverInitFail(object):
- def __init__(self, *args):
- raise Bcfg2.Client.Tools.toolInstantiationError
-
-
-class DriverInventoryFail(object):
- __name__ = 'dif'
-
- def __init__(self, logger, setup, config):
- self.config = config
- self.handled = []
- self.modified = []
- self.extra = []
-
- def Inventory(self):
- raise Error
-
-
-class TestFrame(object):
- def test__init(self):
- setup = {}
- times = {}
- config = lxml.etree.Element('Configuration')
- frame = Bcfg2.Client.Frame.Frame(config, setup, times, [], False)
- assert frame.tools == []
-
- def test__init2(self):
- setup = {}
- times = {}
- frame2 = Bcfg2.Client.Frame.Frame(c1, setup, times, ['POSIX'], False)
- assert len(frame2.tools) == 1
-
- def test__init3(self):
- setup = {}
- times = {}
- frame3 = Bcfg2.Client.Frame.Frame(c2, setup, times, ['foo'], False)
- assert len(frame3.tools) == 0
-
- def test__init4(self):
- setup = {}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times, [DriverInitFail], False)
- assert len(frame.tools) == 0
-
- def test__Decide_Inventory(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist)
-
- def test__Decide_Bundle(self):
- setup = {'remove': 'none',
- 'bundle': ['bar'],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist) == 0
-
- def test__Decide_Dryrun(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], True)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist) == 0
-
- def test__GenerateStats(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- frame.Inventory()
- frame.Decide()
- stats = frame.GenerateStats()
- assert len(stats.findall('.//Bad')[0].getchildren()) != 0
diff --git a/testsuite/TestOptions.py b/testsuite/TestOptions.py
deleted file mode 100644
index 735455d45..000000000
--- a/testsuite/TestOptions.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-import sys
-
-import Bcfg2.Options
-
-
-class TestOption(object):
- def test__init(self):
- o = Bcfg2.Options.Option('foo', False, cmd='-F')
- try:
- p = Bcfg2.Options.Option('foo', False, cmd='--F')
- assert False
- except Bcfg2.Options.OptionFailure:
- pass
-
- def test_parse(self):
- o = Bcfg2.Options.Option('foo', 'test4', cmd='-F', env='TEST2',
- odesc='bar', cf=('communication', 'password'))
- o.parse([], ['-F', 'test'])
- assert o.value == 'test'
- o.parse([('-F', 'test2')], [])
- assert o.value == 'test2'
- os.environ['TEST2'] = 'test3'
- o.parse([], [])
- assert o.value == 'test3'
- del os.environ['TEST2']
- o.parse([], [])
- print(o.value)
- assert o.value == 'foobat'
- o.cf = ('communication', 'pwd')
- o.parse([], [])
- print(o.value)
- assert o.value == 'test4'
- o.cf = False
- o.parse([], [])
- assert o.value == 'test4'
-
- def test_cook(self):
- # check that default value isn't cooked
- o1 = Bcfg2.Options.Option('foo', 'test4', cook=Bcfg2.Options.bool_cook)
- o1.parse([], [])
- assert o1.value == 'test4'
- o2 = Bcfg2.Options.Option('foo', False, cmd='-F')
- o2.parse([('-F', '')], [])
- assert o2.value == True
-
-
-class TestOptionSet(object):
- def test_buildGetopt(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os = Bcfg2.Options.OptionSet(opts)
- res = os.buildGetopt()
- assert 'H:' in res and 'G' in res and len(res) == 3
-
- def test_buildLongGetopt(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='--H',
- odesc='1', long_arg=True))]
- os = Bcfg2.Options.OptionSet(opts)
- res = os.buildLongGetopt()
- print(res)
- assert 'H=' in res and len(res) == 1
-
- def test_parse(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os = Bcfg2.Options.OptionSet(opts)
- try:
- os.parse(['-G', '-H'])
- assert False
- except SystemExit:
- pass
- os2 = Bcfg2.Options.OptionSet(opts)
- try:
- os2.parse(['-h'])
- assert False
- except SystemExit:
- pass
- os3 = Bcfg2.Options.OptionSet(opts)
- os3.parse(['-G'])
- assert os3['foo'] == True
-
-
-class TestOptionParser(object):
- def test__init(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-h')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os1 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/etc/bcfg2.conf'
- sys.argv = ['foo', '-C', '/usr/local/etc/bcfg2.conf']
- os2 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/usr/local/etc/bcfg2.conf'
- sys.argv = []
- os3 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/etc/bcfg2.conf'
diff --git a/testsuite/TestPlugin.py b/testsuite/TestPlugin.py
deleted file mode 100644
index aa619249a..000000000
--- a/testsuite/TestPlugin.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import gamin
-import lxml.etree
-import os
-
-import Bcfg2.Server.Core
-from Bcfg2.Server.Plugin import EntrySet
-
-
-class es_testtype(object):
- def __init__(self, name, properties, specific):
- self.name = name
- self.properties = properties
- self.specific = specific
- self.handled = 0
- self.built = 0
-
- def handle_event(self, event):
- self.handled += 1
-
- def bind_entry(self, entry, metadata):
- entry.set('bound', '1')
- entry.set('name', self.name)
- self.built += 1
-
-
-class metadata(object):
- def __init__(self, hostname):
- self.hostname = hostname
- self.groups = ['base', 'debian']
-
-#FIXME add test_specific
-
-
-class test_entry_set(object):
- def __init__(self):
- self.dirname = '/tmp/estest-%d' % os.getpid()
- os.path.isdir(self.dirname) or os.mkdir(self.dirname)
- self.metadata = metadata('testhost')
- self.es = EntrySet('template', self.dirname, None, es_testtype)
- self.e = Bcfg2.Server.Core.GaminEvent(1, 'template',
- gamin.GAMExists)
-
- def test_init(self):
- es = self.es
- e = self.e
- e.action = 'exists'
- es.handle_event(e)
- es.handle_event(e)
- assert len(es.entries) == 1
- assert list(es.entries.values())[0].handled == 2
- e.action = 'changed'
- es.handle_event(e)
- assert list(es.entries.values())[0].handled == 3
-
- def test_info(self):
- """Test info and info.xml handling."""
- es = self.es
- e = self.e
- dirname = self.dirname
- metadata = self.metadata
-
- # test 'info' handling
- assert es.metadata['group'] == 'root'
- self.mk_info(dirname)
- e.filename = 'info'
- e.action = 'exists'
- es.handle_event(e)
- assert es.metadata['group'] == 'sys'
- e.action = 'deleted'
- es.handle_event(e)
- assert es.metadata['group'] == 'root'
-
- # test 'info.xml' handling
- assert es.infoxml == None
- self.mk_info_xml(dirname)
- e.filename = 'info.xml'
- e.action = 'exists'
- es.handle_event(e)
- assert es.infoxml
- e.action = 'deleted'
- es.handle_event(e)
- assert es.infoxml == None
-
- def test_file_building(self):
- """Test file building."""
- self.test_init()
- ent = lxml.etree.Element('foo')
- self.es.bind_entry(ent, self.metadata)
- print(list(self.es.entries.values())[0])
- assert list(self.es.entries.values())[0].built == 1
-
- def test_host_specific_file_building(self):
- """Add a host-specific template and build it."""
- self.e.filename = 'template.H_%s' % self.metadata.hostname
- self.e.action = 'exists'
- self.es.handle_event(self.e)
- assert len(self.es.entries) == 1
- ent = lxml.etree.Element('foo')
- self.es.bind_entry(ent, self.metadata)
- # FIXME need to test that it built the _right_ file here
-
- def test_deletion(self):
- """Test deletion of files."""
- self.test_init()
- self.e.filename = 'template'
- self.e.action = 'deleted'
- self.es.handle_event(self.e)
- assert len(self.es.entries) == 0
-
- # TODO - how to clean up the temp dir & files after tests done?
-
- def mk_info(self, dir):
- i = open("%s/info" % dir, 'w')
- i.write('owner: root\n')
- i.write('group: sys\n')
- i.write('perms: 0600\n')
- i.close
-
- def mk_info_xml(self, dir):
- i = open("%s/info.xml" % dir, 'w')
- i.write('<FileInfo><Info owner="root" group="other" perms="0600" /></FileInfo>\n')
- i.close
diff --git a/testsuite/Testlib/TestOptions.py b/testsuite/Testlib/TestOptions.py
new file mode 100644
index 000000000..f5833a54a
--- /dev/null
+++ b/testsuite/Testlib/TestOptions.py
@@ -0,0 +1,124 @@
+import os
+import sys
+import unittest
+from mock import Mock, patch
+import Bcfg2.Options
+
+class TestOption(unittest.TestCase):
+ def test__init(self):
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='f')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='--f')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='-foo')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='-foo', long_arg=True)
+
+ @patch('ConfigParser.ConfigParser')
+ @patch('__builtin__.open')
+ def test_getCFP(self, mock_open, mock_cp):
+ mock_cp.return_value = Mock()
+ o = Bcfg2.Options.Option('foo', False, cmd='-f')
+ self.assertFalse(o._Option__cfp)
+ o.getCFP()
+ mock_cp.assert_any_call()
+ mock_open.assert_any_call(Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
+ self.assertTrue(mock_cp.return_value.readfp.called)
+
+ @patch('Bcfg2.Options.Option.cfp')
+ def test_parse(self, mock_cfp):
+ cf = ('communication', 'password')
+ o = Bcfg2.Options.Option('foo', 'test4', cmd='-F', env='TEST2',
+ odesc='bar', cf=cf)
+ o.parse([], ['-F', 'test'])
+ self.assertEqual(o.value, 'test')
+ o.parse([('-F', 'test2')], [])
+ self.assertEqual(o.value, 'test2')
+
+ os.environ['TEST2'] = 'test3'
+ o.parse([], [])
+ self.assertEqual(o.value, 'test3')
+ del os.environ['TEST2']
+
+ mock_cfp.get = Mock()
+ mock_cfp.get.return_value = 'test5'
+ o.parse([], [])
+ self.assertEqual(o.value, 'test5')
+ mock_cfp.get.assert_any_call(*cf)
+
+ o.cf = False
+ o.parse([], [])
+ assert o.value == 'test4'
+
+ def test_cook(self):
+ # check that default value isn't cooked
+ o1 = Bcfg2.Options.Option('foo', 'test4', cook=Bcfg2.Options.bool_cook)
+ o1.parse([], [])
+ assert o1.value == 'test4'
+ o2 = Bcfg2.Options.Option('foo', False, cmd='-F')
+ o2.parse([('-F', '')], [])
+ assert o2.value == True
+
+
+class TestOptionSet(unittest.TestCase):
+ def test_buildGetopt(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ res = oset.buildGetopt()
+ self.assertIn('H:', res)
+ self.assertIn('G', res)
+ self.assertEqual(len(res), 3)
+
+ def test_buildLongGetopt(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='--H',
+ odesc='1', long_arg=True))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ res = oset.buildLongGetopt()
+ self.assertIn('H=', res)
+ self.assertEqual(len(res), 1)
+
+ def test_parse(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ self.assertRaises(SystemExit,
+ oset.parse,
+ ['-G', '-H'])
+ oset2 = Bcfg2.Options.OptionSet(opts)
+ self.assertRaises(SystemExit,
+ oset2.parse,
+ ['-h'])
+ oset3 = Bcfg2.Options.OptionSet(opts)
+ oset3.parse(['-G'])
+ self.assertTrue(oset3['foo'])
+
+
+class TestOptionParser(unittest.TestCase):
+ def test__init(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-h')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset1 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
+ sys.argv = ['foo', '-C', '/usr/local/etc/bcfg2.conf']
+ oset2 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ '/usr/local/etc/bcfg2.conf')
+ sys.argv = []
+ oset3 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
diff --git a/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
new file mode 100644
index 000000000..455731d00
--- /dev/null
+++ b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
@@ -0,0 +1,905 @@
+import os
+import copy
+import time
+import socket
+import unittest
+import lxml.etree
+from mock import Mock, patch
+import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugins.Metadata import *
+
+XI_NAMESPACE = "http://www.w3.org/2001/XInclude"
+XI = "{%s}" % XI_NAMESPACE
+
+clients_test_tree = lxml.etree.XML('''
+<Clients>
+ <Client name="client1" address="1.2.3.1" auth="cert"
+ location="floating" password="password2" profile="group1"/>
+ <Client name="client2" address="1.2.3.2" secure="true" profile="group2"/>
+ <Client name="client3" address="1.2.3.3" uuid="uuid1" profile="group1"
+ password="password2">
+ <Alias name="alias1"/>
+ </Client>
+ <Client name="client4" profile="group1">
+ <Alias name="alias2" address="1.2.3.2"/>
+ <Alias name="alias3"/>
+ </Client>
+ <Client name="client5" profile="group1"/>
+ <Client name="client6" profile="group1" auth="bootstrap"/>
+ <Client name="client7" profile="group1" auth="cert" address="1.2.3.4"/>
+ <Client name="client8" profile="group1" auth="cert+password"
+ address="1.2.3.5"/>
+ <Client name="client9" profile="group2" secure="true" password="password3"/>
+</Clients>''').getroottree()
+
+groups_test_tree = lxml.etree.XML('''
+<Groups xmlns:xi="http://www.w3.org/2001/XInclude">
+ <Group name="group1" default="true" profile="true" public="true"
+ category="category1"/>
+ <Group name="group2" profile="true" public="true" category="category1">
+ <Bundle name="bundle1"/>
+ <Bundle name="bundle2"/>
+ <Group name="group1"/>
+ <Group name="group4"/>
+ </Group>
+ <Group name="group3" category="category2" public="false"/>
+ <Group name="group4" category="category1">
+ <Group name="group1"/>
+ <Group name="group6"/>
+ </Group>
+ <Group name="group5"/>
+ <Group name="group7">
+ <Bundle name="bundle3"/>
+ </Group>
+ <Group name="group8">
+ <Group name="group9"/>
+ </Group>
+</Groups>''').getroottree()
+
+datastore = "/"
+
+def get_metadata_object(core=None, watch_clients=False):
+ if core is None:
+ core = Mock()
+ metadata = Metadata(core, datastore, watch_clients=watch_clients)
+ #metadata.logger = Mock()
+ return metadata
+
+
+class TestXMLMetadataConfig(unittest.TestCase):
+ 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
+ try:
+ config.xdata
+ except MetadataRuntimeError:
+ pass
+ except:
+ assert False
+ config.data = "<test/>"
+ self.assertEqual(config.xdata, "<test/>")
+
+ def test_base_xdata(self):
+ config = self.get_config_object()
+ # we can't use assertRaises here because base_xdata is a property
+ try:
+ config.base_xdata
+ except MetadataRuntimeError:
+ pass
+ except:
+ assert False
+ config.basedata = "<test/>"
+ self.assertEqual(config.base_xdata, "<test/>")
+
+ 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(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(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")
+ def test_load_xml(self, mock_parse, mock_add_monitor):
+ config = self.get_config_object("clients.xml")
+ mock_parse.side_effect = lxml.etree.XMLSyntaxError(None, None, None,
+ None)
+ config.load_xml()
+ self.assertIsNone(config.data)
+ self.assertIsNone(config.basedata)
+
+ config.data = None
+ config.basedata = None
+ mock_parse.side_effect = None
+ mock_parse.return_value.findall = Mock(return_value=[])
+ config.load_xml()
+ self.assertIsNotNone(config.data)
+ self.assertIsNotNone(config.basedata)
+
+ config.data = None
+ config.basedata = None
+
+ 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")
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.write_xml")
+ def test_write(self, mock_write_xml):
+ config = self.get_config_object("clients.xml")
+ config.basedata = "<test/>"
+ config.write()
+ mock_write_xml.assert_called_with(os.path.join(self.metadata.data,
+ "clients.xml"),
+ "<test/>")
+
+ @patch('Bcfg2.Server.Plugins.Metadata.locked', Mock(return_value=False))
+ @patch('fcntl.lockf', Mock())
+ @patch('__builtin__.open')
+ @patch('os.unlink')
+ @patch('os.rename')
+ @patch('os.path.islink')
+ @patch('os.readlink')
+ def test_write_xml(self, mock_readlink, mock_islink, mock_rename,
+ mock_unlink, mock_open):
+ fname = "clients.xml"
+ config = self.get_config_object(fname)
+ 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)
+ mock_islink.assert_called_with(fpath)
+ mock_rename.assert_called_with(tmpfile, fpath)
+
+ mock_islink.return_value = True
+ mock_readlink.return_value = linkdest
+ config.write_xml(fpath, clients_test_tree)
+ mock_rename.assert_called_with(tmpfile, linkdest)
+
+ mock_rename.side_effect = OSError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+
+ mock_open.return_value.write.side_effect = IOError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+ mock_unlink.assert_called_with(tmpfile)
+
+ mock_open.side_effect = IOError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch('lxml.etree.parse')
+ def test_find_xml_for_xpath(self, mock_parse):
+ config = self.get_config_object("groups.xml")
+ config.basedata = groups_test_tree
+ xpath = "//Group[@name='group1']"
+ self.assertItemsEqual(config.find_xml_for_xpath(xpath),
+ dict(filename=os.path.join(self.metadata.data,
+ "groups.xml"),
+ xmltree=groups_test_tree,
+ xquery=groups_test_tree.xpath(xpath)))
+
+ self.assertEqual(config.find_xml_for_xpath("//boguselement"), dict())
+
+ config.extras = ["foo.xml", "bar.xml", "clients.xml"]
+
+ def parse_side_effect(fname):
+ if fname == os.path.join(self.metadata.data, "clients.xml"):
+ return clients_test_tree
+ else:
+ return lxml.etree.XML("<null/>").getroottree()
+
+ mock_parse.side_effect = parse_side_effect
+ xpath = "//Client[@secure='true']"
+ self.assertItemsEqual(config.find_xml_for_xpath(xpath),
+ dict(filename=os.path.join(self.metadata.data,
+ "clients.xml"),
+ xmltree=clients_test_tree,
+ xquery=clients_test_tree.xpath(xpath)))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_HandleEvent(self, mock_load_xml):
+ config = self.get_config_object("groups.xml")
+ evt = Mock()
+ evt.filename = os.path.join(self.metadata.data, "groups.xml")
+ evt.code2str = Mock(return_value="changed")
+ self.assertTrue(config.HandleEvent(evt))
+ mock_load_xml.assert_called_with()
+
+
+class TestClientMetadata(unittest.TestCase):
+ def test_inGroup(self):
+ cm = ClientMetadata("client1", "group1", ["group1", "group2"],
+ ["bundle1"], [], [], [], None, None, None)
+ self.assertTrue(cm.inGroup("group1"))
+ self.assertFalse(cm.inGroup("group3"))
+
+
+class TestMetadata(unittest.TestCase):
+ def test__init_no_fam(self):
+ # test with watch_clients=False
+ core = Mock()
+ metadata = get_metadata_object(core=core)
+ self.check_metadata_object(metadata)
+ self.assertEqual(metadata.states, dict())
+
+ def test__init_with_fam(self):
+ # test with watch_clients=True
+ core = Mock()
+ core.fam = Mock()
+ metadata = get_metadata_object(core=core, watch_clients=True)
+ self.assertEqual(len(metadata.states), 2)
+ core.fam.AddMonitor.assert_any_call(os.path.join(metadata.data,
+ "groups.xml"),
+ metadata)
+ core.fam.AddMonitor.assert_any_call(os.path.join(metadata.data,
+ "clients.xml"),
+ metadata)
+
+ core.fam.reset_mock()
+ core.fam.AddMonitor = Mock(side_effect=IOError)
+ self.assertRaises(Bcfg2.Server.Plugin.PluginInitError,
+ get_metadata_object,
+ core=core, watch_clients=True)
+
+ def check_metadata_object(self, metadata):
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Plugin)
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Metadata)
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Statistics)
+ self.assertIsInstance(metadata.clients_xml, XMLMetadataConfig)
+ self.assertIsInstance(metadata.groups_xml, XMLMetadataConfig)
+ self.assertIsInstance(metadata.query, MetadataQuery)
+
+ @patch('os.makedirs', Mock())
+ @patch('__builtin__.open')
+ def test_init_repo(self, mock_open):
+ groups = "groups %s"
+ os_selection = "os"
+ clients = "clients %s"
+ Metadata.init_repo(datastore, groups, os_selection, clients)
+ mock_open.assert_any_call(os.path.join(datastore, "Metadata",
+ "groups.xml"), "w")
+ mock_open.assert_any_call(os.path.join(datastore, "Metadata",
+ "clients.xml"), "w")
+
+ @patch('lxml.etree.parse')
+ def test_get_groups(self, mock_parse):
+ metadata = get_metadata_object()
+ mock_parse.return_value = groups_test_tree
+ groups = metadata.get_groups()
+ 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()
+ tree = groups_test_tree
+ res = metadata._search_xdata("Group", "group1", tree)
+ self.assertIsInstance(res, lxml.etree._Element)
+ self.assertEqual(res.get("name"), "group1")
+
+ def test_search_xdata_alias(self):
+ # test finding a node with the wrong name but correct alias
+ metadata = get_metadata_object()
+ tree = clients_test_tree
+ res = metadata._search_xdata("Client", "alias3", tree, alias=True)
+ self.assertIsInstance(res, lxml.etree._Element)
+ self.assertNotEqual(res.get("name"), "alias3")
+
+ def test_search_xdata_not_found(self):
+ # test failure finding a node
+ metadata = get_metadata_object()
+ tree = clients_test_tree
+ res = metadata._search_xdata("Client", "bogus_client", tree, alias=True)
+ self.assertIsNone(res)
+
+ def search_xdata(self, tag, name, tree, alias=False):
+ metadata = get_metadata_object()
+ res = metadata._search_xdata(tag, name, tree, alias=alias)
+ self.assertIsInstance(res, lxml.etree._Element)
+ if not alias:
+ self.assertEqual(res.get("name"), name)
+
+ def test_search_group(self):
+ # test finding a group with the proper name
+ tree = groups_test_tree
+ self.search_xdata("Group", "group1", tree)
+
+ def test_search_bundle(self):
+ # test finding a bundle with the proper name
+ tree = groups_test_tree
+ self.search_xdata("Bundle", "bundle1", tree)
+
+ def test_search_client(self):
+ # test finding a client with the proper name
+ tree = clients_test_tree
+ self.search_xdata("Client", "client1", tree, alias=True)
+ self.search_xdata("Client", "alias1", tree, alias=True)
+
+ def test_add_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write = Mock()
+ metadata.groups_xml.data = lxml.etree.XML('<Groups/>').getroottree()
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_group("test1", dict())
+ metadata.groups_xml.write.assert_any_call()
+ grp = metadata.search_group("test1", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_group("test2", dict(foo='bar'))
+ metadata.groups_xml.write.assert_any_call()
+ grp = metadata.search_group("test2", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test2', foo='bar'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.groups_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_group,
+ "test1", dict())
+ self.assertFalse(metadata.groups_xml.write.called)
+
+ def test_update_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.update_group("group1", dict(foo="bar"))
+ grp = metadata.search_group("group1", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertIn("foo", grp.attrib)
+ self.assertEqual(grp.get("foo"), "bar")
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.update_group,
+ "bogus_group", dict())
+
+ def test_remove_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.remove_group("group5")
+ grp = metadata.search_group("group5", metadata.groups_xml.base_xdata)
+ self.assertIsNone(grp)
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.remove_group,
+ "bogus_group")
+
+ def test_add_bundle(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write = Mock()
+ metadata.groups_xml.data = lxml.etree.XML('<Groups/>').getroottree()
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_bundle("bundle1")
+ metadata.groups_xml.write.assert_any_call()
+ bundle = metadata.search_bundle("bundle1",
+ metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(bundle)
+ self.assertEqual(bundle.attrib, dict(name='bundle1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.groups_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_bundle,
+ "bundle1")
+ self.assertFalse(metadata.groups_xml.write.called)
+
+ def test_remove_bundle(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.remove_bundle("bundle1")
+ grp = metadata.search_bundle("bundle1", metadata.groups_xml.base_xdata)
+ self.assertIsNone(grp)
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.remove_bundle,
+ "bogus_bundle")
+
+ def test_add_client(self):
+ metadata = get_metadata_object()
+ metadata.clients_xml.write = Mock()
+ metadata.clients_xml.data = lxml.etree.XML('<Clients/>').getroottree()
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.add_client("test1", dict())
+ metadata.clients_xml.write.assert_any_call()
+ grp = metadata.search_client("test1", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.add_client("test2", dict(foo='bar'))
+ metadata.clients_xml.write.assert_any_call()
+ grp = metadata.search_client("test2", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test2', foo='bar'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.clients_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_client,
+ "test1", dict())
+ self.assertFalse(metadata.clients_xml.write.called)
+
+ def test_update_client(self):
+ metadata = get_metadata_object()
+ metadata.clients_xml.write_xml = Mock()
+ metadata.clients_xml.data = copy.deepcopy(clients_test_tree)
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.update_client("client1", dict(foo="bar"))
+ grp = metadata.search_client("client1", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertIn("foo", grp.attrib)
+ self.assertEqual(grp.get("foo"), "bar")
+ self.assertTrue(metadata.clients_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.update_client,
+ "bogus_client", dict())
+
+ def load_clients_data(self, metadata=None, xdata=None):
+ if metadata is None:
+ metadata = get_metadata_object()
+ metadata.clients_xml.data = xdata or copy.deepcopy(clients_test_tree)
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+ evt = Mock()
+ evt.filename = os.path.join(datastore, "Metadata", "clients.xml")
+ evt.code2str = Mock(return_value="changed")
+ metadata.HandleEvent(evt)
+ return metadata
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_clients_xml_event(self, mock_load_xml):
+ metadata = get_metadata_object()
+ metadata.profiles = ["group1", "group2"]
+ self.load_clients_data(metadata=metadata)
+ mock_load_xml.assert_any_call()
+ self.assertItemsEqual(metadata.clients,
+ dict([(c.get("name"), c.get("profile"))
+ for c in clients_test_tree.findall("//Client")]))
+ aliases = dict([(a.get("name"), a.getparent().get("name"))
+ for a in clients_test_tree.findall("//Alias")])
+ self.assertItemsEqual(metadata.aliases, aliases)
+
+ raliases = dict([(c.get("name"), set())
+ for c in clients_test_tree.findall("//Client")])
+ for alias in clients_test_tree.findall("//Alias"):
+ raliases[alias.getparent().get("name")].add(alias.get("name"))
+ self.assertItemsEqual(metadata.raliases, raliases)
+
+ self.assertEqual(metadata.bad_clients, dict())
+ self.assertEqual(metadata.secure,
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@secure='true']")])
+ self.assertEqual(metadata.floating, ["client1"])
+
+ addresses = dict([(c.get("address"), [])
+ for c in clients_test_tree.findall("//*[@address]")])
+ raddresses = dict()
+ for client in clients_test_tree.findall("//Client[@address]"):
+ addresses[client.get("address")].append(client.get("name"))
+ try:
+ raddresses[client.get("name")].append(client.get("address"))
+ except KeyError:
+ raddresses[client.get("name")] = [client.get("address")]
+ for alias in clients_test_tree.findall("//Alias[@address]"):
+ addresses[alias.get("address")].append(alias.getparent().get("name"))
+ try:
+ raddresses[alias.getparent().get("name")].append(alias.get("address"))
+ except KeyError:
+ raddresses[alias.getparent().get("name")] = alias.get("address")
+
+ self.assertItemsEqual(metadata.addresses, addresses)
+ self.assertItemsEqual(metadata.raddresses, raddresses)
+ self.assertTrue(metadata.states['clients.xml'])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_clients_xml_event_bad_clients(self):
+ metadata = get_metadata_object()
+ metadata.profiles = ["group2"]
+ self.load_clients_data(metadata=metadata)
+ clients = dict()
+ badclients = dict()
+ for client in clients_test_tree.findall("//Client"):
+ if client.get("profile") in metadata.profiles:
+ clients[client.get("name")] = client.get("profile")
+ else:
+ badclients[client.get("name")] = client.get("profile")
+ self.assertItemsEqual(metadata.clients, clients)
+ self.assertItemsEqual(metadata.bad_clients, badclients)
+
+ def load_groups_data(self, metadata=None, xdata=None):
+ if metadata is None:
+ metadata = get_metadata_object()
+ metadata.groups_xml.data = xdata or copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+ evt = Mock()
+ evt.filename = os.path.join(datastore, "Metadata", "groups.xml")
+ evt.code2str = Mock(return_value="changed")
+ metadata.HandleEvent(evt)
+ return metadata
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_groups_xml_event(self, mock_load_xml):
+ dup_data = copy.deepcopy(groups_test_tree)
+ lxml.etree.SubElement(dup_data.getroot(),
+ "Group", name="group1")
+ metadata = self.load_groups_data(xdata=dup_data)
+ mock_load_xml.assert_any_call()
+ self.assertEqual(metadata.public, ["group1", "group2"])
+ self.assertEqual(metadata.private, ["group3"])
+ self.assertEqual(metadata.profiles, ["group1", "group2"])
+ self.assertItemsEqual(metadata.groups.keys(),
+ [g.get("name")
+ for g in groups_test_tree.findall("/Group")])
+ self.assertEqual(metadata.categories,
+ dict(group1="category1",
+ group2="category1",
+ group3="category2",
+ group4="category1"))
+ self.assertEqual(metadata.default, "group1")
+ self.assertTrue(metadata.states['groups.xml'])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.add_client")
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.update_client")
+ def test_set_profile(self, mock_update_client, mock_add_client):
+ metadata = get_metadata_object()
+ metadata.states['clients.xml'] = False
+ self.assertRaises(MetadataRuntimeError,
+ metadata.set_profile,
+ None, None, None)
+
+ self.load_groups_data(metadata=metadata)
+ self.load_clients_data(metadata=metadata)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.set_profile,
+ "client1", "group5", None)
+
+ metadata.clients_xml.write = Mock()
+ metadata.set_profile("client1", "group2", None)
+ mock_update_client.assert_called_with("client1", dict(profile="group2"))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["client1"], "group2")
+
+ metadata.clients_xml.write.reset_mock()
+ metadata.set_profile("client_new", "group1", None)
+ mock_add_client.assert_called_with("client_new", dict(profile="group1"))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["client_new"], "group1")
+
+ metadata.session_cache[('1.2.3.6', None)] = (None, 'client_new2')
+ metadata.clients_xml.write.reset_mock()
+ metadata.set_profile("uuid_new", "group1", ('1.2.3.6', None))
+ mock_add_client.assert_called_with("client_new2",
+ dict(uuid='uuid_new',
+ profile="group1",
+ address='1.2.3.6'))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["uuid_new"], "group1")
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("socket.gethostbyaddr")
+ def test_resolve_client(self, mock_gethostbyaddr):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ metadata.session_cache[('1.2.3.3', None)] = (time.time(), 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None)), 'client3')
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.resolve_client,
+ ('1.2.3.2', None))
+ self.assertEqual(metadata.resolve_client(('1.2.3.1', None)), 'client1')
+
+ metadata.session_cache[('1.2.3.3', None)] = (time.time() - 100,
+ 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None)), 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None),
+ cleanup_cache=True), 'client3')
+ self.assertEqual(metadata.session_cache, dict())
+
+ mock_gethostbyaddr.return_value = ('client6', [], ['1.2.3.6'])
+ self.assertEqual(metadata.resolve_client(('1.2.3.6', None)), 'client6')
+ mock_gethostbyaddr.assert_called_with('1.2.3.6')
+
+ mock_gethostbyaddr.reset_mock()
+ mock_gethostbyaddr.return_value = ('alias3', [], ['1.2.3.7'])
+ self.assertEqual(metadata.resolve_client(('1.2.3.7', None)), 'client4')
+ mock_gethostbyaddr.assert_called_with('1.2.3.7')
+
+ mock_gethostbyaddr.reset_mock()
+ mock_gethostbyaddr.return_value = None
+ mock_gethostbyaddr.side_effect = socket.herror
+ self.assertRaises(MetadataConsistencyError,
+ metadata.resolve_client,
+ ('1.2.3.8', None))
+ mock_gethostbyaddr.assert_called_with('1.2.3.8')
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.write_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.ClientMetadata")
+ def test_get_initial_metadata(self, mock_clientmetadata):
+ metadata = get_metadata_object()
+ metadata.states['clients.xml'] = False
+ self.assertRaises(MetadataRuntimeError,
+ metadata.get_initial_metadata, None)
+
+ self.load_groups_data(metadata=metadata)
+ self.load_clients_data(metadata=metadata)
+
+ metadata.get_initial_metadata("client1")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client1", "group1", set(["group1"]), set(), set(),
+ set(["1.2.3.1"]), dict(), None, 'password2'))
+
+ metadata.get_initial_metadata("client2")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client2", "group2", set(["group1", "group2"]),
+ 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(),
+ set(['alias1']), set(["1.2.3.3"]), dict(), 'uuid1',
+ 'password2'))
+
+ imd = metadata.get_initial_metadata("client_new")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client_new", "group1", set(["group1"]), set(),
+ set(), set(), dict(), None, None))
+
+ metadata.default = None
+ 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):
+ metadata = self.load_groups_data()
+ self.assertItemsEqual(metadata.get_all_group_names(),
+ set([g.get("name")
+ for g in groups_test_tree.findall("//Group")]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_all_groups_in_category(self):
+ metadata = self.load_groups_data()
+ self.assertItemsEqual(metadata.get_all_groups_in_category("category1"),
+ set([g.get("name")
+ for g in groups_test_tree.findall("//Group[@category='category1']")]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_client_names_by_profiles(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ self.assertItemsEqual(metadata.get_client_names_by_profiles("group2"),
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@profile='group2']")])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_client_names_by_groups(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ # this is not the best test in the world, since we mock
+ # core.build_metadata to just build _initial_ metadata, which
+ # is not at all the same thing. it turns out that mocking
+ # this out without starting a Bcfg2 server is pretty
+ # non-trivial, so this works-ish
+ metadata.core.build_metadata = Mock()
+ metadata.core.build_metadata.side_effect = \
+ lambda c: metadata.get_initial_metadata(c)
+ self.assertItemsEqual(metadata.get_client_names_by_groups(["group2"]),
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@profile='group2']")])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_merge_additional_groups(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ imd = metadata.get_initial_metadata("client2")
+
+ # test adding a group excluded by categories
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group4"])
+ self.assertEqual(imd.groups, oldgroups)
+
+ # test adding a private group
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group3"])
+ self.assertEqual(imd.groups, oldgroups)
+
+ # test adding groups with bundles
+ oldgroups = imd.groups
+ oldbundles = imd.bundles
+ metadata.merge_additional_groups(imd, ["group7"])
+ self.assertEqual(imd.groups, oldgroups.union(["group7"]))
+ self.assertEqual(imd.bundles, oldbundles.union(["bundle3"]))
+
+ # test adding multiple groups
+ imd = metadata.get_initial_metadata("client2")
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group6", "group8"])
+ self.assertItemsEqual(imd.groups,
+ oldgroups.union(["group6", "group8", "group9"]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_merge_additional_data(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ imd = metadata.get_initial_metadata("client1")
+
+ # we need to use a unique attribute name for this test. this
+ # is probably overkill, but it works
+ pattern = "connector%d"
+ for i in range(0, 100):
+ connector = pattern % i
+ if not hasattr(imd, connector):
+ break
+ self.assertFalse(hasattr(imd, connector),
+ "Could not find unique connector name to test "
+ "merge_additional_data()")
+
+ metadata.merge_additional_data(imd, connector, "test data")
+ self.assertEqual(getattr(imd, connector), "test data")
+ self.assertIn(connector, imd.connectors)
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.resolve_client")
+ def test_validate_client_address(self, mock_resolve_client):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ self.assertTrue(metadata.validate_client_address("client1",
+ (None, None)))
+ self.assertTrue(metadata.validate_client_address("client2",
+ ("1.2.3.2", None)))
+ self.assertFalse(metadata.validate_client_address("client2",
+ ("1.2.3.8", None)))
+ self.assertTrue(metadata.validate_client_address("client4",
+ ("1.2.3.2", None)))
+ # this is upper case to ensure that case is folded properly in
+ # validate_client_address()
+ mock_resolve_client.return_value = "CLIENT4"
+ self.assertTrue(metadata.validate_client_address("client4",
+ ("1.2.3.7", None)))
+ mock_resolve_client.assert_called_with(("1.2.3.7", None))
+
+ mock_resolve_client.reset_mock()
+ self.assertFalse(metadata.validate_client_address("client5",
+ ("1.2.3.5", None)))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.validate_client_address")
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.resolve_client")
+ def test_AuthenticateConnection(self, mock_resolve_client,
+ mock_validate_client_address):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ metadata.password = "password1"
+
+ cert = dict(subject=[[("commonName", "client1")]])
+ mock_validate_client_address.return_value = False
+ self.assertFalse(metadata.AuthenticateConnection(cert, "root", None,
+ "1.2.3.1"))
+ mock_validate_client_address.return_value = True
+ self.assertTrue(metadata.AuthenticateConnection(cert, "root", None,
+ "1.2.3.1"))
+ # 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"))
+ # non-floating cert-auth clients do not add themselves to the cache
+ self.assertNotIn("1.2.3.4", metadata.session_cache)
+
+ cert = dict(subject=[[("commonName", "client8")]])
+
+ mock_resolve_client.return_value = "client5"
+ self.assertTrue(metadata.AuthenticateConnection(None, "root",
+ "password1", "1.2.3.8"))
+
+ mock_resolve_client.side_effect = MetadataConsistencyError
+ self.assertFalse(metadata.AuthenticateConnection(None, "root",
+ "password1",
+ "1.2.3.8"))
+
+ # secure mode, no password
+ self.assertFalse(metadata.AuthenticateConnection(None, 'client2', None,
+ "1.2.3.2"))
+
+ self.assertTrue(metadata.AuthenticateConnection(None, 'uuid1',
+ "password1", "1.2.3.3"))
+ # non-root, non-cert clients populate session cache
+ self.assertIn("1.2.3.3", metadata.session_cache)
+ self.assertEqual(metadata.session_cache["1.2.3.3"][1], "client3")
+
+ # use alternate password
+ self.assertTrue(metadata.AuthenticateConnection(None, 'client3',
+ "password2", "1.2.3.3"))
+
+ # test secure mode
+ self.assertFalse(metadata.AuthenticateConnection(None, 'client9',
+ "password1",
+ "1.2.3.9"))
+ self.assertTrue(metadata.AuthenticateConnection(None, 'client9',
+ "password3", "1.2.3.9"))
+
+ self.assertFalse(metadata.AuthenticateConnection(None, "client5",
+ "password2",
+ "1.2.3.7"))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.update_client")
+ def test_process_statistics(self, mock_update_client):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ md = Mock()
+ md.hostname = "client6"
+ metadata.process_statistics(md, None)
+ mock_update_client.assert_called_with(md.hostname,
+ dict(auth='cert'))
+
+ mock_update_client.reset_mock()
+ md.hostname = "client5"
+ metadata.process_statistics(md, None)
+ self.assertFalse(mock_update_client.called)
+
+ def test_viz(self):
+ pass
diff --git a/tools/bcfg2-export-config b/tools/bcfg2-export-config
index 4e7790a22..f7e939a3e 100755
--- a/tools/bcfg2-export-config
+++ b/tools/bcfg2-export-config
@@ -16,7 +16,6 @@ The metadata information get stored in an index file in the output directory.
The format of the index file is:
user,group,permission,/path/to/file
'''
-__revision__ = '$Revision: 221 $'
import logging, lxml.etree, Bcfg2.Logging, Bcfg2.Server.Core
import Bcfg2.Server.Plugins.Metadata, Bcfg2.Server.Plugin
diff --git a/tools/create-debian-pkglist.py b/tools/create-debian-pkglist.py
index 8e1210582..4b268c6ee 100755
--- a/tools/create-debian-pkglist.py
+++ b/tools/create-debian-pkglist.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
'''Build debian/ubuntu package indexes'''
-__revision__ = '$Id$'
# Original code from Bcfg2 sources
diff --git a/tools/encap-util-count.sh b/tools/encap-util-count.sh
index 848a8d648..460bb689b 100755
--- a/tools/encap-util-count.sh
+++ b/tools/encap-util-count.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This shows a count of encap packages per directory
# Can be useful to make sure you have everything
diff --git a/tools/encap-util-expand.sh b/tools/encap-util-expand.sh
index 2bb3f7f62..5764bdc79 100755
--- a/tools/encap-util-expand.sh
+++ b/tools/encap-util-expand.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This gets the encaps out of a makeself .run file
diff --git a/tools/encap-util-place.sh b/tools/encap-util-place.sh
index c3e96ebd8..dedf14448 100755
--- a/tools/encap-util-place.sh
+++ b/tools/encap-util-place.sh
@@ -1,5 +1,4 @@
#!/bin/bash
-# $Id$
# This puts encaps in the right directories, creating the
# directories if needed.
diff --git a/tools/encap-util-xml.sh b/tools/encap-util-xml.sh
index 0fdd75fe4..ae762637f 100755
--- a/tools/encap-util-xml.sh
+++ b/tools/encap-util-xml.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This builds the XML Pkgmgr files for the encap directory
# structure created by the place script. It assumes the
diff --git a/tools/hostbasepush.py b/tools/hostbasepush.py
index 070711c82..02b7a582f 100755
--- a/tools/hostbasepush.py
+++ b/tools/hostbasepush.py
@@ -1,7 +1,5 @@
#!/usr/bin/python
-__revision__ = "$Revision: $"
-
import os
import Bcfg2.Client.Proxy
diff --git a/tools/pkgmgr_gen.py b/tools/pkgmgr_gen.py
index 03d36dfc0..7101ba17d 100755
--- a/tools/pkgmgr_gen.py
+++ b/tools/pkgmgr_gen.py
@@ -10,7 +10,6 @@
bcfg2 client drivers. The output can also contain the PackageList
and nested group headers.
"""
-__revision__ = '$Revision: $'
import collections
import datetime
import glob