summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-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/configuration/mrepo.txt26
-rw-r--r--doc/appendix/contributors.txt8
-rw-r--r--doc/appendix/guides/centos.txt114
-rw-r--r--doc/appendix/guides/gentoo.txt73
-rw-r--r--doc/appendix/guides/using-bcfg2-with-centos.txt8
-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/getting_started/index.txt2
-rw-r--r--doc/installation/distributions.txt19
-rw-r--r--doc/server/admin/bundle.txt10
-rw-r--r--doc/server/plugins/connectors/templatehelper.txt74
-rw-r--r--doc/server/plugins/generators/packages.txt83
-rw-r--r--doc/server/plugins/probes/group.txt2
-rw-r--r--doc/server/plugins/probes/index.txt2
-rw-r--r--examples/TemplateHelper/include.py97
-rw-r--r--gentoo/bcfg2-1.2.2.ebuild43
-rw-r--r--misc/bcfg2.spec111
-rw-r--r--redhat/bcfg2.spec.in2
-rw-r--r--redhat/systemd/bcfg2-server.service2
-rw-r--r--redhat/systemd/bcfg2.service2
-rw-r--r--schemas/base.xsd1
-rw-r--r--schemas/clients.xsd1
-rw-r--r--schemas/decisions.xsd1
-rw-r--r--schemas/defaults.xsd1
-rw-r--r--schemas/genshi.xsd1
-rw-r--r--schemas/metadata.xsd1
-rw-r--r--schemas/pathentry.xsd1
-rw-r--r--schemas/pkglist.xsd1
-rw-r--r--schemas/pkgtype.xsd1
-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.py80
-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)23
-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)15
-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)4
-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)3
-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)21
-rw-r--r--src/lib/Bcfg2/Server/Lint/Comments.py (renamed from src/lib/Server/Lint/Comments.py)15
-rw-r--r--src/lib/Bcfg2/Server/Lint/Deltas.py5
-rw-r--r--src/lib/Bcfg2/Server/Lint/Duplicates.py (renamed from src/lib/Server/Lint/Duplicates.py)10
-rwxr-xr-xsrc/lib/Bcfg2/Server/Lint/Genshi.py (renamed from src/lib/Server/Lint/Genshi.py)12
-rw-r--r--src/lib/Bcfg2/Server/Lint/GroupPatterns.py (renamed from src/lib/Server/Lint/GroupPatterns.py)8
-rw-r--r--src/lib/Bcfg2/Server/Lint/InfoXML.py (renamed from src/lib/Server/Lint/InfoXML.py)8
-rw-r--r--src/lib/Bcfg2/Server/Lint/MergeFiles.py (renamed from src/lib/Server/Lint/MergeFiles.py)11
-rw-r--r--src/lib/Bcfg2/Server/Lint/Pkgmgr.py (renamed from src/lib/Server/Lint/Pkgmgr.py)5
-rw-r--r--src/lib/Bcfg2/Server/Lint/RequiredAttrs.py (renamed from src/lib/Server/Lint/RequiredAttrs.py)9
-rw-r--r--src/lib/Bcfg2/Server/Lint/TemplateHelper.py64
-rw-r--r--src/lib/Bcfg2/Server/Lint/Validate.py (renamed from src/lib/Server/Lint/Validate.py)16
-rw-r--r--src/lib/Bcfg2/Server/Lint/__init__.py (renamed from src/lib/Server/Lint/__init__.py)52
-rw-r--r--src/lib/Bcfg2/Server/Plugin.py (renamed from src/lib/Server/Plugin.py)64
-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)4
-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)3
-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)634
-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)7
-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)6
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py (renamed from src/lib/Server/Plugins/Packages/Yum.py)34
-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)8
-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)11
-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)2
-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)15
-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/split.py8
-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)97
-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-info190
-rwxr-xr-xsrc/sbin/bcfg2-lint25
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep2
-rwxr-xr-xsrc/sbin/bcfg2-reports1
-rwxr-xr-xsrc/sbin/bcfg2-server1
-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
278 files changed, 2716 insertions, 1507 deletions
diff --git a/AUTHORS b/AUTHORS
index f13deb691..d2bebd02f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,7 +9,7 @@ In no particular order:
- Tim Laszlo <tim.laszlo@gmail.com> worked on the reporting system and
made plugins.
-- Fabian Affolter <fabian@bernewireless.net> made some patches, added
+- Fabian Affolter <mail@fabian-affolter.ch> made some patches, added
some new features and plugins, and restructured the manual for Bcfg2.
- Andrew Brestick <brestick@mcs.anl.gov> fixed bugs and completed
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/configuration/mrepo.txt b/doc/appendix/configuration/mrepo.txt
index 309cd6779..c1bdb3fed 100644
--- a/doc/appendix/configuration/mrepo.txt
+++ b/doc/appendix/configuration/mrepo.txt
@@ -7,7 +7,7 @@
mrepo
=====
-This section describes how to setup an `mrepo`_ mirror.
+.. This section describes how to setup an `mrepo`_ mirror.
`mrepo`_ builds a local APT/Yum RPM repository from local ISO files,
downloaded updates, and extra packages from 3rd party repositories. It
@@ -68,3 +68,27 @@ Update the repositories
::
mrepo -ug
+
+Example sources.xml file
+------------------------
+
+.. code-block:: xml
+
+ <Sources>
+ <!-- CentOS (5.4) sources -->
+ <YUMSource>
+ <Group>centos-5.4</Group>
+ <RawURL>http://mrepo/centos5-x86_64/RPMS.os</RawURL>
+ <Arch>x86_64</Arch>
+ </YUMSource>
+ <YUMSource>
+ <Group>centos-5.4</Group>
+ <RawURL>http://mrepo/centos5-x86_64/RPMS.updates</RawURL>
+ <Arch>x86_64</Arch>
+ </YUMSource>
+ <YUMSource>
+ <Group>centos-5.4</Group>
+ <RawURL>http://mrepo/centos5-x86_64/RPMS.extras</RawURL>
+ <Arch>x86_64</Arch>
+ </YUMSource>
+ </Sources>
diff --git a/doc/appendix/contributors.txt b/doc/appendix/contributors.txt
index ebc49d364..1afc62706 100644
--- a/doc/appendix/contributors.txt
+++ b/doc/appendix/contributors.txt
@@ -11,7 +11,7 @@ Contributors
..
This is list is no longer in chronological order like the
- AUTHORS file because it's easier to maintain (for me).
+ AUTHORS file because it's easier to maintain.
Automatically sorted.
In alphabetical order of the given name:
@@ -22,6 +22,8 @@ In alphabetical order of the given name:
which was used in the Bcfg2 client.
- Chris Vuletich <vuletich@mcs.anl.gov> wrote some SSL code and the
verification debugging code.
+- Chris St. Pierre <stpierreca@ornl.gov> (re)wrote bcfg2-lint and has
+ made other miscellaneous contributions.
- Cory Lueninghoener <cory@mcs.anl.gov> wrote the showentries function
in ``bcfg2-info``.
- Daniel Clark <dclark@pobox.com> created encap packages for bcfg2 and
@@ -31,7 +33,7 @@ In alphabetical order of the given name:
- David Strauss worked on CentOS, RHEL, Yum, and Bazaar VCS support.
- Ed Smith <esmith4@inf.ed.ac.uk> has done substantial hardening of the
Bcfg client and server and implemented a common logging infrastructure.
-- Fabian Affolter <fabian@bernewireless.net> made some patches, added
+- Fabian Affolter <mail@fabian-affolter.ch> made some patches, added
some new features and plugins, and restructured the manual for Bcfg2.
- Jack Neely <jjneely@ncsu.edu> worked on YUMng.
- James Yang <jjyang@mcs.anl.gov> worked on ``bcfg2-admin`` and
@@ -45,7 +47,7 @@ In alphabetical order of the given name:
- Ken Raffenetti <raffenet@mcs.anl.gov> and Rick Bradshaw have written the
Hostbase plugin.
- Michael Jinks <mjinks@uchicago.edu> wrote the Gentoo tool drivers.
-- Narayan Desai <desai@mcs.anl.gov> has written most of bcfg2, including all
+- Narayan Desai <desai@mcs.anl.gov> has written most of Bcfg2, including all
parts not explicitly mentioned in this file.
- Patrick Ruckstuhl fixed bugs in the templating.
- Pedro Flores made the Reporting system design help.
diff --git a/doc/appendix/guides/centos.txt b/doc/appendix/guides/centos.txt
index 2bb3edc2b..50334ccbc 100644
--- a/doc/appendix/guides/centos.txt
+++ b/doc/appendix/guides/centos.txt
@@ -17,7 +17,7 @@ Install Bcfg2
The fastest way to get Bcfg2 onto your system is to use Yum or
your preferred package management tool. We'll be using the ones
that are distributed through EPEL_, but depending on your aversion
-to risk you could download an RPM from other places as well. See
+to risk you could download an RPM from other places as well. See
:ref:`getting_started-using_bcfg2-with-centos` for information about
building Bcfg2 from source and making your own packages.
@@ -102,20 +102,20 @@ Run bcfg2 to be sure you are able to communicate with the server::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Phase: initial
- Correct entries: 0
- Incorrect entries: 0
- Total managed entries: 0
- Unmanaged entries: 208
+ Correct entries: 0
+ Incorrect entries: 0
+ Total managed entries: 0
+ Unmanaged entries: 208
Phase: final
- Correct entries: 0
- Incorrect entries: 0
- Total managed entries: 0
- Unmanaged entries: 208
+ Correct entries: 0
+ Incorrect entries: 0
+ Total managed entries: 0
+ Unmanaged entries: 208
No ca is specified. Cannot authenticate the server with SSL.
@@ -147,20 +147,20 @@ Now if you run the client, no more warning::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Phase: initial
- Correct entries: 0
- Incorrect entries: 0
- Total managed entries: 0
- Unmanaged entries: 208
+ Correct entries: 0
+ Incorrect entries: 0
+ Total managed entries: 0
+ Unmanaged entries: 208
Phase: final
- Correct entries: 0
- Incorrect entries: 0
- Total managed entries: 0
- Unmanaged entries: 208
+ Correct entries: 0
+ Incorrect entries: 0
+ Total managed entries: 0
+ Unmanaged entries: 208
Bring your first machine under Bcfg2 control
============================================
@@ -173,7 +173,7 @@ Setup the :ref:`server-plugins-generators-packages` plugin
----------------------------------------------------------
First, replace **Pkgmgr** with **Packages** in the plugins
-line of ``bcfg2.conf``. Then create Packages layout (as per
+line of ``bcfg2.conf``. Then create Packages layout (as per
:ref:`packages-exampleusage`) in ``/var/lib/bcfg2``
.. note:: I am using the RawURL syntax here since we are using `mrepo`_
@@ -291,30 +291,30 @@ Now if we run the client, we can see what this has done for us.::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Package pam failed verification.
Phase: initial
- Correct entries: 94
- Incorrect entries: 1
- Total managed entries: 95
- Unmanaged entries: 113
+ Correct entries: 94
+ Incorrect entries: 1
+ Total managed entries: 95
+ Unmanaged entries: 113
In dryrun mode: suppressing entry installation for:
Package:pam
Phase: final
- Correct entries: 94
- Incorrect entries: 1
+ Correct entries: 94
+ Incorrect entries: 1
Package:pam
- Total managed entries: 95
- Unmanaged entries: 113
+ Total managed entries: 95
+ Unmanaged entries: 113
Interesting, our **pam** package failed verification. What does this
mean? Let's have a look::
[root@centos ~]# rpm --verify pam
- ....L... c /etc/pam.d/system-auth
+ ....L... c /etc/pam.d/system-auth
Sigh, it looks like the default RPM install for pam fails to verify
using its own verification process (trust me, it's not the only one). At
@@ -336,23 +336,23 @@ entries?::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Extra Package openssh-clients 4.3p2-36.el5_4.4.x86_64.
Extra Package libuser 0.54.7-2.1el5_4.1.x86_64.
...
Phase: initial
- Correct entries: 95
- Incorrect entries: 0
- Total managed entries: 95
- Unmanaged entries: 113
+ Correct entries: 95
+ Incorrect entries: 0
+ Total managed entries: 95
+ Unmanaged entries: 113
Phase: final
- Correct entries: 95
- Incorrect entries: 0
- Total managed entries: 95
- Unmanaged entries: 113
+ Correct entries: 95
+ Incorrect entries: 0
+ Total managed entries: 95
+ Unmanaged entries: 113
Package:at
Package:avahi
Package:avahi-compat-libdns_sd
@@ -394,22 +394,22 @@ package::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Extra Package gpg-pubkey e8562897-459f07a4.None.
Extra Package gpg-pubkey 217521f6-45e8a532.None.
Phase: initial
- Correct entries: 187
- Incorrect entries: 0
- Total managed entries: 187
- Unmanaged entries: 16
+ Correct entries: 187
+ Incorrect entries: 0
+ Total managed entries: 187
+ Unmanaged entries: 16
Phase: final
- Correct entries: 187
- Incorrect entries: 0
- Total managed entries: 187
- Unmanaged entries: 16
+ Correct entries: 187
+ Incorrect entries: 0
+ Total managed entries: 187
+ Unmanaged entries: 16
Package:gpg-pubkey
Service:atd
Service:avahi-daemon
@@ -562,20 +562,20 @@ Now we run the client and see there are no more unmanaged entries!::
Excluding Packages in global exclude list
Finished
Loaded tool drivers:
- Action Chkconfig POSIX YUMng
+ Action Chkconfig POSIX YUMng
Phase: initial
- Correct entries: 205
- Incorrect entries: 0
- Total managed entries: 205
- Unmanaged entries: 0
+ Correct entries: 205
+ Incorrect entries: 0
+ Total managed entries: 205
+ Unmanaged entries: 0
Phase: final
- Correct entries: 205
- Incorrect entries: 0
- Total managed entries: 205
- Unmanaged entries: 0
+ Correct entries: 205
+ Incorrect entries: 0
+ Total managed entries: 205
+ Unmanaged entries: 0
.. warning::
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/appendix/guides/using-bcfg2-with-centos.txt b/doc/appendix/guides/using-bcfg2-with-centos.txt
index cd00e5e79..6a48fc5da 100644
--- a/doc/appendix/guides/using-bcfg2-with-centos.txt
+++ b/doc/appendix/guides/using-bcfg2-with-centos.txt
@@ -23,11 +23,13 @@ While you can go about building all these things from source, this
how to will try and meet the dependencies using packages from EPEL_
or RPMforge_. The *el5* package should be compatible with CentOS 5.x.
-EPEL_::
+EPEL_ for 5.x ::
+ [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
- [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm
+EPEL_ for 6.x ::
+ [root@centos ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm
-RPMforge_::
+RPMforge_ for 5.x ::
[root@centos ~]# rpm -Uvh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
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/getting_started/index.txt b/doc/getting_started/index.txt
index e7075c33f..034584014 100644
--- a/doc/getting_started/index.txt
+++ b/doc/getting_started/index.txt
@@ -236,7 +236,7 @@ Next Steps
Several other utilities can help from this point on:
:ref:`bcfg2-info <server-bcfg2-info>` is a utility that
-instantiates a copy of the bcfg2 server core (minus the networking code)
+instantiates a copy of the Bcfg2 server core (minus the networking code)
for examination. From this, you can directly query:
* Client Metadata
diff --git a/doc/installation/distributions.txt b/doc/installation/distributions.txt
index 383943e9f..3dcfd7721 100644
--- a/doc/installation/distributions.txt
+++ b/doc/installation/distributions.txt
@@ -10,6 +10,17 @@ The installation of Bcfg2 on a specific distribution depends on the
package management tool and the availability of the package in the
distribution's repository.
+Alpine Linux
+============
+
+Packages for `Alpine Linux`_ are available in the `testing`_ repository.
+Just use `apk` to perform the installation ::
+
+ apk add bcfg2 bcfg2-server -U -X http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted
+
+.. _Alpine Linux: http://www.alpinelinux.org/
+.. _testing: http://git.alpinelinux.org/cgit/aports/tree/testing/bcfg2
+
ArchLinux
=========
@@ -101,13 +112,15 @@ RHEL / Centos / Scientific Linux
While you can go about building all these things from source, this
section will try and meet the dependencies using packages from EPEL_
-[#f1]_. The *el5* and the soon available *el6* package should be compatible
+[#f1]_. The *el5* and the *el6* package should be compatible
with `CentOS`_ 5.x/6.x and `Scientific Linux`_.
-EPEL_::
-
+EPEL_ for 5.x ::
[root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
+EPEL_ for 6.x ::
+ [root@centos ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm
+
Install the bcfg2-server and bcfg2 RPMs::
[root@centos ~]# yum install bcfg2-server bcfg2
diff --git a/doc/server/admin/bundle.txt b/doc/server/admin/bundle.txt
index 101666bb9..e9cb79781 100644
--- a/doc/server/admin/bundle.txt
+++ b/doc/server/admin/bundle.txt
@@ -8,13 +8,17 @@ bundle
For a list of all available xml bundles use ``list-xml``. ``list-genshi``
will list all available genshi bundles.::
- # bcfg2-admin bundle list-xml
- # bcfg2-admin bundle list-genshi
+.. code-block:: sh
+
+ # bcfg2-admin bundles list-xml
+ # bcfg2-admin bundles list-genshi
``show`` provides an interactive dialog to get details about the available
bundles.::
- # bcfg2-admin bundle show
+.. code-block:: sh
+
+ # bcfg2-admin bundles show
Available bundles (Number of bundles: 4)
----------------------------------------
[0] motd.xml
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 900162aaa..62574be76 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -56,19 +56,19 @@ member clients.
.. versionadded:: 1.2.0
Magic OS groups can be disabled in Bcfg2 1.2 and greater by setting
- ``magic_groups`` to ``0`` in ``Packages/packages.conf``. This may
+ ``magic_groups`` to ``0`` in ``Packages/packages.conf``. This may
give you greater flexibility in determining which source types to
- use for which OSes. Magic architecture groups cannot be disabled.
+ use for which OSes. Magic architecture groups cannot be disabled.
Limiting sources to groups
==========================
``Packages/sources.xml`` processes ``<Group>`` and ``<Client>`` tags
-just like Bundles. In addition to any groups or clients specified
+just like Bundles. In addition to any groups or clients specified
that way, clients must
be a member of the appropriate architecture group as specified in a
-Source stanza. In total, in order for a source to be associated with
+Source stanza. In total, in order for a source to be associated with
a client, the client must be in one of the magic groups (debian,
ubuntu, or nexenta), any explicit groups or clients specified in
``sources.xml``, and any specified architecture groups.
@@ -161,7 +161,7 @@ Handling GPG Keys
If you have yum libraries installed, Packages can automatically handle
GPG signing keys for Yum and Pulp repositories. (You do not need to
use the native yum resolver; if yum libraries are available, GPG
-signing keys can be handled automatically.) Simply specify the URL to
+signing keys can be handled automatically.) Simply specify the URL to
the GPG key(s) for a repository in ``sources.xml``::
<Source type="yum"
@@ -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>
@@ -335,7 +342,7 @@ updated.
Availability
============
-Support for clients using yum and apt is currently available. Support for
+Support for clients using yum and apt is currently available. Support for
other package managers (Portage, Zypper, IPS, etc) remain to be added.
Validation
@@ -351,7 +358,7 @@ Limitations
Packages does not do traditional caching as other plugins
do. Modifying sources in the Packages ``sources.xml`` file requires a
-server restart for the time being. You do not have to restart the
+server restart for the time being. You do not have to restart the
server after changing ``packages.conf`` or after adding new sources to
``sources.xml``.
@@ -384,9 +391,9 @@ Then add the corresponding Path entry to your Yum bundle.
.. versionadded:: 1.1.0
APT repository information can be generated automatically from
-software sources using :doc:`./tgenshi/index` or :doc:`./tcheetah`. A
+software sources using :doc:`./tgenshi/index` or :doc:`./tcheetah`. A
list of source urls are exposed in the client's metadata as
-``metadata.Packages.sources``. E.g.::
+``metadata.Packages.sources``. E.g.::
# bcfg2 maintained apt
@@ -404,7 +411,7 @@ Using Native Yum Libraries
By default, Bcfg2 uses an internal implementation of Yum's dependency
resolution and other routines so that the Bcfg2 server can be run on a
-host that does not support Yum itself. If you run the Bcfg2 server on
+host that does not support Yum itself. If you run the Bcfg2 server on
a machine that does have Yum libraries, however, you can enable use of
those native libraries in Bcfg2 by setting ``use_yum_libraries`` to
``1`` in the ``[yum]`` section of ``Packages/packages.conf``.
@@ -418,7 +425,7 @@ Benefits to this include:
Drawbacks include:
-* More disk I/O. In some cases, you may have to raise the open file
+* More disk I/O. In some cases, you may have to raise the open file
limit for the user who runs your Bcfg2 server process, particularly
if you have a lot of repositories.
* Resolution of package dependencies is slower in some cases,
@@ -433,8 +440,8 @@ Configuring the Yum Helper
Due to poor memory management by the Yum API, the long-lived
bcfg2-server process uses an external short-lived helper,
``bcfg2-yum-helper``, to do the actual Yum API calls for native yum
-library support. By default, Bcfg2 looks for this helper at
-``/usr/sbin/bcfg2-yum-helper``. If you have installed the helper
+library support. By default, Bcfg2 looks for this helper at
+``/usr/sbin/bcfg2-yum-helper``. If you have installed the helper
elsewhere, you will need to configure that location with the
``helper`` option in the ``[yum]`` section, e.g.::
@@ -448,11 +455,11 @@ Setting Yum Options
In ``Packages/packages.conf``, any options you set in the ``[yum]``
section other than ``use_yum_libraries`` and ``helper`` will be passed
along verbatim to the configuration of the Yum objects used in the
-Bcfg2 server. The following options are set by default, and should
+Bcfg2 server. The following options are set by default, and should
not generally be overridden:
* ``cachedir`` is set to a hashed value unique to each distinct Yum
- configuration. Don't set this unless you know what you're doing.
+ configuration. Don't set this unless you know what you're doing.
* ``keepcache`` is set to ``0``; there is no benefit to changing this.
* ``sslverify`` is set to ``0``; change this if you know what you're
doing.
@@ -462,16 +469,16 @@ not generally be overridden:
Package Groups
--------------
-Yum package groups are supported by the native Yum libraries. To
+Yum package groups are supported by the native Yum libraries. To
include a package group, use the ``group`` attribute of the
-``Package`` tag. You can use either the short group ID or the long
+``Package`` tag. You can use either the short group ID or the long
group name::
<Package group="SNMP Support"/>
<Package group="system-management-snmp"/>
By default, only those packages considered the "default" packages in a
-group will be installed. You can change this behavior using the
+group will be installed. You can change this behavior using the
"type" attribute::
<Package group="development" type="optional"/>
@@ -485,7 +492,7 @@ Valid values of "type" are:
including mandatory, default, and optional packages.
You can view the packages in a group by category with the ``yum
-groupinfo`` command. More information about the different levels can
+groupinfo`` command. More information about the different levels can
be found at
http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation
@@ -497,7 +504,7 @@ Pulp Support
.. versionadded:: 1.2.0
Bcfg2 contains explicit support for repositories managed by Pulp
-(http://pulpproject.org/). Due to the amount of data about a
+(http://pulpproject.org/). Due to the amount of data about a
repository that can be retrieved directly from Pulp, the only thing
necessary to configure a Pulp repo is the repo ID::
@@ -509,7 +516,7 @@ necessary to configure a Pulp repo is the repo ID::
</Group>
</Sources>
-Pulp sources require some additional configuration. First, the Bcfg2
+Pulp sources require some additional configuration. First, the Bcfg2
server must have a valid ``/etc/pulp/consumer/consumer.conf`` that is
readable by the user your Bcfg2 server runs as; the Pulp server,
URLs, and so on, are determined from this.
@@ -519,7 +526,7 @@ options in the ``[pulp]`` section:
* ``username`` and ``password``: The username and password of a Pulp
user that will be used to register new clients and bind them to
- repositories. Membership in the default ``consumer-users`` role is
+ repositories. Membership in the default ``consumer-users`` role is
sufficient.
Bcfg2 clients using Pulp sources will be registered to the Pulp server
@@ -640,7 +647,7 @@ packages.conf
=============
``packages.conf`` contains miscellaneous configuration options for the
-Packages plugin. Any booleans in the config file accept the values
+Packages plugin. Any booleans in the config file accept the values
"1", "yes", "true", and "on" for True, and "0", "no", "false", and
"off" for False
@@ -649,18 +656,18 @@ It understands the following directives:
[global] section
----------------
-* ``resolver``: Enable dependency resolution. Default is ``1``
+* ``resolver``: Enable dependency resolution. Default is ``1``
(true). For historical reasons, this also accepts "enabled" and
"disabled".
-* ``metadata``: Enable metadata processing. Default is ``1``
+* ``metadata``: Enable metadata processing. Default is ``1``
(true). For historical reasons, this also accepts "enabled" and
"disabled".
-* ``yum_config``: The path at which to generate Yum configs. No
+* ``yum_config``: The path at which to generate Yum configs. No
default.
-* ``apt_config``: The path at which to generate APT configs. No
+* ``apt_config``: The path at which to generate APT configs. No
default.
* ``gpg_keypath``: The path on the client RPM GPG keys will be copied
- to before they are imported on the client. Default is
+ to before they are imported on the client. Default is
"/etc/pki/rpm-gpg".
* ``version``: Set the version attribute used when binding
Packages. Default is ``auto``.
@@ -669,7 +676,7 @@ It understands the following directives:
-------------
* ``use_yum_libraries``: Whether or not to use the :ref:`native yum
- library support <native-yum-libraries>`. Default is ``0`` (false).
+ library support <native-yum-libraries>`. Default is ``0`` (false).
All other options in the ``[yum]`` section will be passed along
verbatim to the Yum configuration if you are using the native Yum
@@ -680,5 +687,5 @@ library support.
* ``username`` and ``password``: The username and password of a Pulp
user that will be used to register new clients and bind them to
- repositories. Membership in the default ``consumer-users`` role is
+ repositories. Membership in the default ``consumer-users`` role is
sufficient.
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/doc/server/plugins/probes/index.txt b/doc/server/plugins/probes/index.txt
index f22f405c1..95aa2d0ce 100644
--- a/doc/server/plugins/probes/index.txt
+++ b/doc/server/plugins/probes/index.txt
@@ -208,7 +208,7 @@ look something like:
<FileProbes>
<FileProbe name="/etc/foo.conf"/>
<Group name="blah-servers">
- <FileProbe name="/etc/blah.conf" update="true"
+ <FileProbe name="/etc/blah.conf" update="true"/>
</Group>
<Client name="bar.example.com">
<FileProbe name="/var/lib/bar.gz" base64="true"/>
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 9fd4c5a13..75c6090a0 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}
@@ -91,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}
@@ -107,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
@@ -136,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
@@ -144,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
@@ -172,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}
@@ -190,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
@@ -219,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
@@ -229,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}
@@ -242,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
@@ -272,7 +275,7 @@ touch %{buildroot}%{_sysconfdir}/bcfg2.conf %{buildroot}%{_sysconfdir}/bcfg2-web
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} || exit 2
-%files -n bcfg2
+%files
%defattr(-,root,root,-)
%{_sbindir}/bcfg2
%dir %{python_sitelib}/Bcfg2
@@ -293,47 +296,7 @@ touch %{buildroot}%{_sysconfdir}/bcfg2.conf %{buildroot}%{_sysconfdir}/bcfg2-web
%endif
%ghost %config(noreplace,missingok) %attr(0600,root,root) %{_sysconfdir}/bcfg2.conf
-%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 +331,11 @@ fi
%dir %{_prefix}/lib/bcfg2
%ghost %config(noreplace,missingok) %attr(0600,root,root) %{_sysconfdir}/bcfg2.conf
-%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
@@ -381,6 +344,46 @@ fi
%config(noreplace) %{apache_conf}/conf.d/wsgi_bcfg2.conf
%ghost %config(noreplace,missingok) %attr(0640,root,apache) %{_sysconfdir}/bcfg2-web.conf
+%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).
@@ -394,7 +397,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 1b8ab5285..64adbe5c2 100644
--- a/redhat/bcfg2.spec.in
+++ b/redhat/bcfg2.spec.in
@@ -118,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/redhat/systemd/bcfg2-server.service b/redhat/systemd/bcfg2-server.service
index 641b02ec6..1fd035fcb 100644
--- a/redhat/systemd/bcfg2-server.service
+++ b/redhat/systemd/bcfg2-server.service
@@ -7,7 +7,7 @@ Type=forking
StandardOutput=syslog
StandardError=syslog
EnvironmentFile=-/etc/sysconfig/bcfg2
-ExecStart=/usr/bin/bcfg2-server $OPTIONS
+ExecStart=/usr/sbin/bcfg2-server $OPTIONS
[Install]
WantedBy=multi-user.target
diff --git a/redhat/systemd/bcfg2.service b/redhat/systemd/bcfg2.service
index a0c3afd81..572391fd0 100644
--- a/redhat/systemd/bcfg2.service
+++ b/redhat/systemd/bcfg2.service
@@ -7,7 +7,7 @@ Type=forking
StandardOutput=syslog
StandardError=syslog
EnvironmentFile=-/etc/sysconfig/bcfg2
-ExecStart=/usr/bin/bcfg2 $OPTIONS
+ExecStart=/usr/sbin/bcfg2 $OPTIONS
[Install]
WantedBy=multi-user.target
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/clients.xsd b/schemas/clients.xsd
index b79be385c..56f458a45 100644
--- a/schemas/clients.xsd
+++ b/schemas/clients.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
client schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
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/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/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 2de44b1d7..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>
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 179981ca3..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,21 +111,11 @@ 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", "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..0d67dbbab 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
@@ -21,7 +20,7 @@ import Bcfg2.Client.Tools
import Bcfg2.Options
from Bcfg2.Client import XML
-log = logging.getLogger('posix')
+log = logging.getLogger('POSIX')
# map between dev_type attribute and stat constants
device_map = {'block': stat.S_IFBLK,
@@ -259,8 +258,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
if entry.get('perms') == None or \
entry.get('owner') == None or \
entry.get('group') == None:
- self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-lint.' % (entry.get('name')))
+ self.logger.error("POSIX: Entry %s not completely specified. "
+ "Try running bcfg2-lint." % (entry.get('name')))
return False
while len(entry.get('perms', '')) < 4:
entry.set('perms', '0' + entry.get('perms', ''))
@@ -268,15 +267,15 @@ class POSIX(Bcfg2.Client.Tools.Tool):
ondisk = os.stat(entry.get('name'))
except OSError:
entry.set('current_exists', 'false')
- self.logger.debug("%s %s does not exist" %
+ self.logger.info("POSIX: %s %s does not exist" %
(entry.tag, entry.get('name')))
return False
try:
owner = str(ondisk[stat.ST_UID])
group = str(ondisk[stat.ST_GID])
except (OSError, KeyError):
- self.logger.error('User/Group resolution failed for path %s' % \
- entry.get('name'))
+ self.logger.info("POSIX: User/Group resolution failed "
+ "for path %s" % entry.get('name'))
owner = 'root'
group = '0'
finfo = os.stat(entry.get('name'))
@@ -301,11 +300,11 @@ class POSIX(Bcfg2.Client.Tools.Tool):
ex_ents = [e for e in entries if e not in modlist]
if ex_ents:
pruneTrue = False
- self.logger.debug("Directory %s contains extra entries:" % \
- entry.get('name'))
- self.logger.debug(ex_ents)
+ self.logger.info("POSIX: Directory %s contains "
+ "extra entries:" % entry.get('name'))
+ self.logger.info(ex_ents)
nqtext = entry.get('qtext', '') + '\n'
- nqtext += "Directory %s contains extra entries:" % \
+ nqtext += "Directory %s contains extra entries: " % \
entry.get('name')
nqtext += ":".join(ex_ents)
entry.set('qtest', nqtext)
@@ -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 b55a8a55a..dc14eaf00 100644
--- a/src/lib/Options.py
+++ b/src/lib/Bcfg2/Options.py
@@ -1,7 +1,7 @@
"""Option parsing library for utilities."""
-__revision__ = '$Revision$'
import getopt
+import re
import os
import sys
import shlex
@@ -135,7 +135,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:
@@ -167,8 +172,10 @@ class OptionSet(dict):
val = option.value
self[key] = val
-list_split = lambda x:x.replace(' ','').split(',')
-flist_split = lambda x:list_split(x.replace(':', '').lower())
+def list_split(c_string):
+ if c_string:
+ return re.split("\s*,\s*", c_string)
+ return []
def colon_split(c_string):
if c_string:
diff --git a/src/lib/Proxy.py b/src/lib/Bcfg2/Proxy.py
index fe2b18de8..422d642db 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..c1f9ed484 100644
--- a/src/lib/Server/Admin/Init.py
+++ b/src/lib/Bcfg2/Server/Admin/Init.py
@@ -55,6 +55,7 @@ groups = '''<Groups version='3.0'>
<Group name='suse'/>
<Group name='mandrake'/>
<Group name='solaris'/>
+ <Group name='arch'/>
</Groups>
'''
@@ -71,7 +72,8 @@ os_list = [('Red Hat/Fedora/RHEL/RHAS/Centos', 'redhat'),
('Debian', 'debian'),
('Ubuntu', 'ubuntu'),
('Gentoo', 'gentoo'),
- ('FreeBSD', 'freebsd')]
+ ('FreeBSD', 'freebsd'),
+ ('Arch', 'arch')]
# Complete list of plugins
plugin_list = ['Account',
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 8f02e0a40..4321c060b 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
@@ -326,7 +325,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..e6b6307f2 100644
--- a/src/lib/Server/Lint/Bundles.py
+++ b/src/lib/Bcfg2/Server/Lint/Bundles.py
@@ -1,9 +1,8 @@
import lxml.etree
import Bcfg2.Server.Lint
-
+
class Bundles(Bcfg2.Server.Lint.ServerPlugin):
""" Perform various bundle checks """
-
def Run(self):
""" run plugin """
if 'Bundler' in self.core.plugins:
@@ -15,6 +14,11 @@ class Bundles(Bcfg2.Server.Lint.ServerPlugin):
Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile):
self.bundle_names(bundle)
+ @classmethod
+ def Errors(cls):
+ return {"bundle-not-found":"error",
+ "inconsistent-bundle-name":"warning"}
+
def missing_bundles(self):
""" find bundles listed in Metadata but not implemented in Bundler """
if self.files is None:
@@ -41,21 +45,10 @@ 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:
self.LintError("inconsistent-bundle-name",
"Inconsistent bundle name: filename is %s, bundle name is %s" %
(fname, bname))
-
- def sgenshi_groups(self, bundle):
- """ ensure that Genshi Bundles do not include <Group> tags,
- which are not supported """
- xdata = lxml.etree.parse(bundle.name)
- groups = [self.RenderXML(g)
- for g in xdata.getroottree().findall("//Group")]
- if groups:
- self.LintError("group-tag-not-allowed",
- "<Group> tag is not allowed in SGenshi Bundle:\n%s" %
- "\n".join(groups))
diff --git a/src/lib/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py
index 19fae1b08..3b9370c3b 100644
--- a/src/lib/Server/Lint/Comments.py
+++ b/src/lib/Bcfg2/Server/Lint/Comments.py
@@ -16,6 +16,13 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
self.check_infoxml()
self.check_probes()
+ @classmethod
+ def Errors(cls):
+ return {"unexpanded-keywords":"warning",
+ "keywords-not-found":"warning",
+ "comments-not-found":"warning",
+ "broken-xinclude-chain":"warning"}
+
def required_keywords(self, rtype):
""" given a file type, fetch the list of required VCS keywords
from the bcfg2-lint config """
@@ -31,13 +38,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 +135,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 +162,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/Bcfg2/Server/Lint/Deltas.py b/src/lib/Bcfg2/Server/Lint/Deltas.py
index cf91d1d13..de2e0668f 100644
--- a/src/lib/Bcfg2/Server/Lint/Deltas.py
+++ b/src/lib/Bcfg2/Server/Lint/Deltas.py
@@ -10,6 +10,11 @@ class Deltas(Bcfg2.Server.Lint.ServerPlugin):
for basename, entry in list(cfg.entries.items()):
self.check_entry(basename, entry)
+ @classmethod
+ def Errors(cls):
+ return {"cat-file-used":"warning",
+ "diff-file-used":"warning"}
+
def check_entry(self, basename, entry):
for fname in list(entry.entries.keys()):
if self.HandlesFile(fname):
diff --git a/src/lib/Server/Lint/Duplicates.py b/src/lib/Bcfg2/Server/Lint/Duplicates.py
index 75f620603..ee6b7a2e6 100644
--- a/src/lib/Server/Lint/Duplicates.py
+++ b/src/lib/Bcfg2/Server/Lint/Duplicates.py
@@ -22,6 +22,14 @@ class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
if self.clients_xdata is not None:
self.duplicate_clients()
+ @classmethod
+ def Errors(cls):
+ return {"broken-xinclude-chain":"warning",
+ "duplicate-client":"error",
+ "duplicate-group":"error",
+ "duplicate-package":"error",
+ "multiple-default-groups":"error"}
+
def load_xdata(self):
""" attempt to load XML data for groups and clients. only
actually load data if all documents reference in XIncludes can
@@ -30,7 +38,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 56803246c..b6007161e 100755
--- a/src/lib/Server/Lint/Genshi.py
+++ b/src/lib/Bcfg2/Server/Lint/Genshi.py
@@ -1,9 +1,8 @@
import genshi.template
import Bcfg2.Server.Lint
-
+
class Genshi(Bcfg2.Server.Lint.ServerPlugin):
""" Check Genshi templates for syntax errors """
-
def Run(self):
""" run plugin """
loader = genshi.template.TemplateLoader()
@@ -12,10 +11,14 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
self.check_files(self.core.plugins[plugin].entries,
loader=loader)
+ @classmethod
+ def Errors(cls):
+ return {"genshi-syntax-error":"error"}
+
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 (self.HandlesFile(fname) and
@@ -23,6 +26,7 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
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..431ba4056 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 """
@@ -14,12 +14,16 @@ class GroupPatterns(Bcfg2.Server.Lint.ServerPlugin):
self.check(entry, groups, ptype='NamePattern')
self.check(entry, groups, ptype='NameRange')
+ @classmethod
+ def Errors(cls):
+ return {"pattern-fails-to-initialize":"error"}
+
def check(self, entry, groups, ptype="NamePattern"):
if ptype == "NamePattern":
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..7b89e86b2 100644
--- a/src/lib/Server/Lint/InfoXML.py
+++ b/src/lib/Bcfg2/Server/Lint/InfoXML.py
@@ -4,7 +4,6 @@ import Bcfg2.Server.Lint
class InfoXML(Bcfg2.Server.Lint.ServerPlugin):
""" ensure that all config files have an info.xml file"""
-
def Run(self):
if 'Cfg' in self.core.plugins:
for filename, entryset in self.core.plugins['Cfg'].entries.items():
@@ -18,6 +17,13 @@ class InfoXML(Bcfg2.Server.Lint.ServerPlugin):
self.LintError("no-infoxml",
"No info.xml found for %s" % filename)
+ @classmethod
+ def Errors(cls):
+ return {"no-infoxml":"warning",
+ "paranoid-false":"warning",
+ "broken-xinclude-chain":"warning",
+ "required-infoxml-attrs-missing":"error"}
+
def check_infoxml(self, fname, xdata):
for info in xdata.getroottree().findall("//Info"):
required = []
diff --git a/src/lib/Server/Lint/MergeFiles.py b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
index ff6e3449a..797de6ed9 100644
--- a/src/lib/Server/Lint/MergeFiles.py
+++ b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
@@ -6,13 +6,18 @@ import Bcfg2.Server.Lint
class MergeFiles(Bcfg2.Server.Lint.ServerPlugin):
""" find Probes or Cfg files with multiple similar files that
might be merged into one """
-
def Run(self):
if 'Cfg' in self.core.plugins:
self.check_cfg()
if 'Probes' in self.core.plugins:
self.check_probes()
+ @classmethod
+ def Errors(cls):
+ return {"merge-cfg":"warning",
+ "merge-probes":"warning"}
+
+
def check_cfg(self):
for filename, entryset in self.core.plugins['Cfg'].entries.items():
for mset in self.get_similar(entryset.entries):
@@ -26,7 +31,7 @@ class MergeFiles(Bcfg2.Server.Lint.ServerPlugin):
def check_probes(self):
probes = self.core.plugins['Probes'].probes.entries
for mset in self.get_similar(probes):
- self.LintError("merge-cfg",
+ self.LintError("merge-probes",
"The following probes are similar: %s. "
"Consider merging them into a single probe." %
", ".join([p for p in mset]))
@@ -66,4 +71,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..ceb46238a 100644
--- a/src/lib/Server/Lint/Pkgmgr.py
+++ b/src/lib/Bcfg2/Server/Lint/Pkgmgr.py
@@ -4,7 +4,6 @@ import Bcfg2.Server.Lint
class Pkgmgr(Bcfg2.Server.Lint.ServerlessPlugin):
""" find duplicate Pkgmgr entries with the same priority """
-
def Run(self):
pset = set()
for pfile in glob.glob("%s/Pkgmgr/*.xml" % self.config['repo']):
@@ -33,3 +32,7 @@ class Pkgmgr(Bcfg2.Server.Lint.ServerlessPlugin):
(pkg.get('name'), priority, ptype))
else:
pset.add(ptuple)
+
+ @classmethod
+ def Errors(cls):
+ return {"duplicate-packages":"error"}
diff --git a/src/lib/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
index 55206d2ba..6f76cf2db 100644
--- a/src/lib/Server/Lint/RequiredAttrs.py
+++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
@@ -6,7 +6,6 @@ from Bcfg2.Server.Plugins.Packages import Apt, Yum
class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
""" verify attributes for configuration entries (as defined in
doc/server/configurationentries) """
-
def __init__(self, *args, **kwargs):
Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs)
self.required_attrs = {
@@ -38,6 +37,14 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
self.check_rules()
self.check_bundles()
+ @classmethod
+ def Errors(cls):
+ return {"unknown-entry-type":"error",
+ "unknown-entry-tag":"error",
+ "required-attrs-missing":"error",
+ "extra-attrs":"warning"}
+
+
def check_packages(self):
""" check package sources for Source entries with missing attrs """
if 'Packages' in self.core.plugins:
diff --git a/src/lib/Bcfg2/Server/Lint/TemplateHelper.py b/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
new file mode 100644
index 000000000..be270a59c
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
@@ -0,0 +1,64 @@
+import sys
+import imp
+import glob
+import Bcfg2.Server.Lint
+from Bcfg2.Server.Plugins.TemplateHelper import HelperModule
+
+class TemplateHelper(Bcfg2.Server.Lint.ServerlessPlugin):
+ """ find duplicate Pkgmgr entries with the same priority """
+ def __init__(self, *args, **kwargs):
+ Bcfg2.Server.Lint.ServerlessPlugin.__init__(self, *args, **kwargs)
+ hm = HelperModule("foo.py", None, None)
+ self.reserved_keywords = dir(hm)
+
+ def Run(self):
+ for helper in glob.glob("%s/TemplateHelper/*.py" % self.config['repo']):
+ if not self.HandlesFile(helper):
+ continue
+
+ match = HelperModule._module_name_re.search(helper)
+ if match:
+ module_name = match.group(1)
+ else:
+ module_name = helper
+
+ try:
+ module = imp.load_source(module_name, helper)
+ except:
+ err = sys.exc_info()[1]
+ self.LintError("templatehelper-import-error",
+ "Failed to import %s: %s" %
+ (helper, err))
+ continue
+
+ if not hasattr(module, "__export__"):
+ self.LintError("templatehelper-no-export",
+ "%s has no __export__ list" % helper)
+ continue
+ elif not isinstance(module.__export__, list):
+ self.LintError("templatehelper-nonlist-export",
+ "__export__ is not a list in %s" % helper)
+ continue
+
+ for sym in module.__export__:
+ if not hasattr(module, sym):
+ self.LintError("templatehelper-nonexistent-export",
+ "%s: exported symbol %s does not exist" %
+ (helper, sym))
+ elif sym in self.reserved_keywords:
+ self.LintError("templatehelper-reserved-export",
+ "%s: exported symbol %s is reserved" %
+ (helper, sym))
+ elif sym.startswith("_"):
+ self.LintError("templatehelper-underscore-export",
+ "%s: exported symbol %s starts with underscore" %
+ (helper, sym))
+
+ @classmethod
+ def Errors(cls):
+ return {"templatehelper-import-error":"error",
+ "templatehelper-no-export":"error",
+ "templatehelper-nonlist-export":"error",
+ "templatehelper-nonexistent-export":"error",
+ "templatehelper-reserved-export":"error",
+ "templatehelper-underscore-export":"warning"}
diff --git a/src/lib/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py
index 952a65365..05fedc313 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]
@@ -63,6 +63,16 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
self.check_properties()
+ @classmethod
+ def Errors(cls):
+ return {"broken-xinclude-chain":"warning",
+ "schema-failed-to-parse":"warning",
+ "properties-schema-not-found":"warning",
+ "xml-failed-to-parse":"error",
+ "xml-failed-to-read":"error",
+ "xml-failed-to-verify":"error",
+ "input-output-error":"error"}
+
def check_properties(self):
""" check Properties files against their schemas """
for filename in self.filelists['props']:
@@ -98,7 +108,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 +190,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 4d6df8c8f..5d7dd707b 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',
@@ -56,14 +54,20 @@ class Plugin (object):
self.config = config
self.logger = logging.getLogger('bcfg2-lint')
if errorhandler is None:
- self.errorHandler = ErrorHandler()
+ self.errorhandler = ErrorHandler()
else:
- self.errorHandler = errorhandler
+ self.errorhandler = errorhandler
+ self.errorhandler.RegisterErrors(self.Errors())
def Run(self):
""" run the plugin. must be overloaded by child classes """
pass
+ @classmethod
+ def Errors(cls):
+ """ returns a dict of errors the plugin supplies. must be
+ overloaded by child classes """
+
def HandlesFile(self, fname):
""" returns true if the given file should be handled by the
plugin according to the files list, false otherwise """
@@ -75,8 +79,8 @@ class Plugin (object):
fname)) in self.files)
def LintError(self, err, msg):
- self.errorHandler.dispatch(err, msg)
-
+ self.errorhandler.dispatch(err, msg)
+
def RenderXML(self, element):
"""render an XML element for error output -- line number
prefixed, no children"""
@@ -93,37 +97,6 @@ class Plugin (object):
class ErrorHandler (object):
- # how to handle different errors by default
- _errors = {"no-infoxml":"warning",
- "paranoid-false":"warning",
- "bundle-not-found":"error",
- "inconsistent-bundle-name":"warning",
- "group-tag-not-allowed":"error",
- "unexpanded-keywords":"warning",
- "keywords-not-found":"warning",
- "comments-not-found":"warning",
- "broken-xinclude-chain":"warning",
- "duplicate-client":"error",
- "duplicate-group":"error",
- "duplicate-package":"error",
- "multiple-default-groups":"error",
- "required-infoxml-attrs-missing":"error",
- "unknown-entry-type":"error",
- "required-attrs-missing":"error",
- "extra-attrs":"warning",
- "schema-failed-to-parse":"warning",
- "properties-schema-not-found":"warning",
- "xml-failed-to-parse":"error",
- "xml-failed-to-read":"error",
- "xml-failed-to-verify":"error",
- "merge-cfg":"warning",
- "merge-probes":"warning",
- "input-output-error":"error",
- "genshi-syntax-error":"error",
- "pattern-fails-to-initialize":"error",
- "cat-file-used":"warning",
- "diff-file-used":"warning"}
-
def __init__(self, config=None):
self.errors = 0
self.warnings = 0
@@ -149,7 +122,8 @@ class ErrorHandler (object):
else:
self._handlers[err] = self.debug
- for err, action in self._errors.items():
+ def RegisterErrors(self, errors):
+ for err, action in errors.items():
if err not in self._handlers:
if "warn" in action:
self._handlers[err] = self.warn
@@ -157,7 +131,7 @@ class ErrorHandler (object):
self._handlers[err] = self.error
else:
self._handlers[err] = self.debug
-
+
def dispatch(self, err, msg):
if err in self._handlers:
self._handlers[err](msg)
diff --git a/src/lib/Server/Plugin.py b/src/lib/Bcfg2/Server/Plugin.py
index 9b3c5814f..41314bbea 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
@@ -71,7 +70,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
@@ -84,7 +83,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:
@@ -93,7 +91,6 @@ class Plugin(Debuggable):
- Data collection (overloading GetProbes/ReceiveData)
"""
name = 'Plugin'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = False
deprecated = False
@@ -106,14 +103,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)
@@ -365,7 +362,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']:
@@ -400,7 +397,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)
@@ -458,7 +455,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.
@@ -594,6 +591,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:
@@ -603,22 +623,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__]
@@ -627,7 +639,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)
@@ -660,7 +672,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 = []
@@ -834,7 +846,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():
@@ -937,7 +949,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)$")
@@ -1013,7 +1025,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)
@@ -1093,7 +1106,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
@@ -1184,7 +1196,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 9ec39e108..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]
@@ -281,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..999e078b9 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)
@@ -100,6 +99,8 @@ class DBStats(Bcfg2.Server.Plugin.Plugin,
ret.append(getattr(entry.reason, "current_%s" % t))
if entry.reason.is_sensitive:
raise Bcfg2.Server.Plugin.PluginExecutionError
+ elif len(entry.reason.unpruned) != 0:
+ ret.append('\n'.join(entry.reason.unpruned))
elif entry.reason.current_diff != '':
if entry.reason.is_binary:
ret.append(binascii.a2b_base64(entry.reason.current_diff))
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 cc1f78456..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,8 +791,8 @@ 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:
@@ -823,7 +800,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
(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:
@@ -843,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')) \
@@ -855,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':
@@ -865,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..49e9d417b 100644
--- a/src/lib/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -50,10 +50,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 +76,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 e03303d30..a966268c0 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:
@@ -43,8 +43,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 self.config.loaded and self.loaded:
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 5b0aa4213..ada04c067 100644
--- a/src/lib/Server/Plugins/Packages/Source.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
@@ -41,6 +41,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable):
self.basepath = basepath
self.xsource = xsource
self.config = config
+ self.essentialpkgs = set()
try:
self.version = xsource.find('Version').text
@@ -54,8 +55,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 +114,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 be5a68aa1..941203db3 100644
--- a/src/lib/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -72,7 +72,7 @@ def _setup_pulp(config):
PULPCONFIG = ConsumerConfig()
serveropts = PULPCONFIG.server
-
+
PULPSERVER = server.PulpServer(serveropts['host'],
int(serveropts['port']),
serveropts['scheme'],
@@ -86,7 +86,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")
@@ -101,7 +101,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)
@@ -133,7 +133,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",
@@ -173,7 +173,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")
@@ -233,7 +233,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',
@@ -299,12 +299,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,
@@ -384,7 +384,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):
@@ -410,7 +410,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)
@@ -442,7 +442,7 @@ class YumCollection(Collection):
os.unlink(self.cfgfile)
self.write_config()
-
+
if force_update:
self.call_helper("clean")
@@ -456,7 +456,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:
@@ -484,7 +484,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:
@@ -508,7 +508,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:
@@ -524,7 +524,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}]
@@ -542,7 +542,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)
@@ -659,7 +659,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 072f5cd7b..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,11 +114,13 @@ class TemplateFile:
if entry.text == '':
entry.set('empty', 'true')
except TemplateError:
+ err = sys.exc_info()[1]
logger.exception('Genshi template error')
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ raise Bcfg2.Server.Plugin.PluginExecutionError('Genshi template error: %s' % err)
except AttributeError:
+ err = sys.exc_info()[1]
logger.exception('Genshi template loading error')
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ raise Bcfg2.Server.Plugin.PluginExecutionError('Genshi template loading error: %s' % err)
class TGenshi(Bcfg2.Server.Plugin.GroupSpool):
@@ -129,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..16df86a9b 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
@@ -41,6 +40,7 @@ from Bcfg2.Bcfg2Py3k import ConfigParser
def build_reason_kwargs(r_ent, encoding, logger):
binary_file = False
sensitive_file = False
+ unpruned_entries = ''
if r_ent.get('sensitive') in ['true', 'True']:
sensitive_file = True
rc_diff = ''
@@ -58,6 +58,10 @@ def build_reason_kwargs(r_ent, encoding, logger):
rc_diff = r_ent.get('current_diff')
else:
rc_diff = ''
+ # detect unmanaged entries in pruned directories
+ if r_ent.get('prune', 'false') == 'true' and r_ent.get('qtest'):
+ unpruned_elist = [e.get('path') for e in r_ent.findall('Prune')]
+ unpruned_entries = "\n".join(unpruned_elist)
if not binary_file:
try:
rc_diff = rc_diff.decode(encoding)
@@ -79,7 +83,8 @@ def build_reason_kwargs(r_ent, encoding, logger):
current_exists=r_ent.get('current_exists', default="True").capitalize() == "True",
current_diff=rc_diff,
is_binary=binary_file,
- is_sensitive=sensitive_file)
+ is_sensitive=sensitive_file,
+ unpruned=unpruned_entries)
def load_stats(cdata, sdata, encoding, vlevel, logger, quick=False, location=''):
@@ -118,8 +123,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..0438ea133 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
@@ -278,6 +277,7 @@ class Reason(models.Model):
current_diff = models.TextField(max_length=1280, blank=True)
is_binary = models.BooleanField(default=False)
is_sensitive = models.BooleanField(default=False)
+ unpruned = models.TextField(max_length=1280, blank=True)
def _str_(self):
return "Reason"
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..cadc178a7 100644
--- a/src/lib/Server/Reports/reports/templates/config_items/item.html
+++ b/src/lib/Bcfg2/Server/Reports/reports/templates/config_items/item.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load split %}
{% load syntax_coloring %}
@@ -91,6 +92,20 @@ div.entry_list h3 {
</div>
{% endif %}
+ <!-- display extra directory entries -->
+ {% if item.reason.unpruned %}
+ <div class='entry_list'>
+ <div class='entry_list_head'>
+ <h3>Extra entries found</h3>
+ </div>
+ <table class='entry_list' cellpadding='3'>
+ {% for unpruned_item in item.reason.unpruned|split %}
+ <tr><td>{{ unpruned_item }}</td></tr>
+ {% endfor %}
+ </table>
+ </div>
+ {% endif %}
+
<div class='entry_list'>
<div class='entry_list_head'>
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/Bcfg2/Server/Reports/reports/templatetags/split.py b/src/lib/Bcfg2/Server/Reports/reports/templatetags/split.py
new file mode 100644
index 000000000..a9b4f0371
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Reports/reports/templatetags/split.py
@@ -0,0 +1,8 @@
+from django import template
+register = template.Library()
+
+
+@register.filter
+def split(s):
+ """split by newlines"""
+ return s.split('\n')
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..39fc10b56 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,8 @@ _fixes = [_merge_database_table_entries,
_interactions_constraint_or_idx,
'alter table reports_reason add is_binary bool NOT NULL default False;',
'alter table reports_reason add is_sensitive bool NOT NULL default False;',
+ _remove_table_column('reports_interaction', 'client_version'),
+ "alter table reports_reason add unpruned varchar(1280) not null default 'N/A';",
]
# this will calculate the last possible version of the database
@@ -110,7 +179,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 +191,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 +206,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 +243,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 e09b7ed87..5e260d94d 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,17 +1,16 @@
#!/usr/bin/env python
-
"""This tool loads the Bcfg2 core into an interactive debugger."""
-__revision__ = '$Revision$'
-from code import InteractiveConsole
+import os
+import sys
import cmd
import errno
import getopt
+import fnmatch
import logging
-import lxml.etree
-import os
-import sys
import tempfile
+import lxml.etree
+from code import InteractiveConsole
try:
try:
@@ -34,7 +33,8 @@ logger = logging.getLogger('bcfg2-info')
USAGE = """Commands:
build <hostname> <filename> - Build config for hostname, writing to filename
builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname
-buildall <directory> - Build configs for all clients in directory
+buildall <directory> [<hostnames*>] - Build configs for all clients in directory
+buildallfile <directory> <filename> [<hostnames*>] - Build config file for all clients in directory
buildfile <filename> <hostname> - Build config file for hostname (not written to disk)
buildbundle <bundle> <hostname> - Render a templated bundle for hostname (not written to disk)
bundles - Print out group/bundle information
@@ -51,8 +51,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>
@@ -78,10 +77,12 @@ class mockLog(object):
def debug(self, *args, **kwargs):
pass
+
class dummyError(Exception):
"""This is just a dummy."""
pass
+
class FileNotBuilt(Exception):
"""Thrown when File entry contains no content."""
def __init__(self, value):
@@ -90,6 +91,30 @@ class FileNotBuilt(Exception):
def __str__(self):
return repr(self.value)
+
+def getClientList(hostglobs):
+ """ given a host glob, get a list of clients that match it """
+ # special cases to speed things up:
+ if '*' in hostglobs:
+ return list(self.metadata.clients.keys())
+ has_wildcards = False
+ for glob in hostglobs:
+ # check if any wildcard characters are in the string
+ if set('*?[]') & set(glob):
+ has_wildcards = True
+ break
+ if not has_wildcards:
+ return hostglobs
+
+ rv = set()
+ clist = set(self.metadata.clients.keys())
+ for glob in hostglobs:
+ for client in clist:
+ if fnmatch.fnmatch(client, glob):
+ rv.update(client)
+ clist.difference_update(rv)
+ return list(rv)
+
def printTabular(rows):
"""Print data in tabular format."""
cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 \
@@ -197,10 +222,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()
@@ -257,42 +278,101 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
self.help_builddir()
def do_buildall(self, args):
- if len(args.split()) != 1:
- print("Usage: buildall <directory>")
+ alist = args.split()
+ if len(alist) < 1:
+ print("Usage: buildall <directory> [<hostnames*>]")
return
+
+ destdir = alist[0]
+ try:
+ os.mkdir(destdir)
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno != 17:
+ print("Could not create %s: %s" % (destdir, err))
+ if len(alist) > 1:
+ clients = getClientList(alist[1:])
+ else:
+ clients = list(self.metadata.clients.keys())
+ for client in clients:
+ self.do_build("%s %s" % (client, os.path.join(destdir,
+ client + ".xml")))
+
+ def do_buildallfile(self, args):
+ """Build a config file for all clients."""
+ usage = 'Usage: buildallfile [--altsrc=<altsrc>] <directory> <filename> [<hostnames*>]'
try:
- os.mkdir(args)
+ opts, args = getopt.gnu_getopt(args.split(), '', ['altsrc='])
except:
- pass
- for client in self.metadata.clients:
- self.do_build("%s %s/%s.xml" % (client, args, client))
+ print(usage)
+ return
+ altsrc = None
+ for opt in opts:
+ if opt[0] == '--altsrc':
+ altsrc = opt[1]
+ if len(args) < 2:
+ print(usage)
+ return
+
+ destdir = args[0]
+ filename = args[1]
+ try:
+ os.mkdir(destdir)
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno != 17:
+ print("Could not create %s: %s" % (destdir, err))
+ if len(args) > 2:
+ clients = getClientList(args[1:])
+ else:
+ clients = list(self.metadata.clients.keys())
+ if altsrc:
+ args = "--altsrc %s -f %%s %%s %%s" % altsrc
+ else:
+ args = "-f %s %s %s"
+ for client in clients:
+ self.do_buildfile(args % (os.path.join(destdir, client),
+ filename, client))
def do_buildfile(self, args):
"""Build a config file for client."""
- usage = 'Usage: buildfile [--altsrc=<altsrc>] filename hostname'
+ usage = 'Usage: buildfile [-f <outfile>] [--altsrc=<altsrc>] filename hostname'
try:
- opts, alist = getopt.gnu_getopt(args.split(), '', ['altsrc='])
+ opts, alist = getopt.gnu_getopt(args.split(), 'f:', ['altsrc='])
except:
print(usage)
return
altsrc = None
+ outfile = None
for opt in opts:
if opt[0] == '--altsrc':
altsrc = opt[1]
- if len(alist) == 2:
- fname, client = alist
- entry = lxml.etree.Element('Path', type='file', name=fname)
- if altsrc:
- entry.set("altsrc", altsrc)
- try:
- metadata = self.build_metadata(client)
- self.Bind(entry, metadata)
- print(lxml.etree.tostring(entry, encoding="UTF-8",
- xml_declaration=True))
- except:
- print("Failed to build entry %s for host %s" % (fname, client))
- else:
+ elif opt[0] == '-f':
+ outfile = opt[1]
+ if len(alist) != 2:
print(usage)
+ return
+
+ fname, client = alist
+ entry = lxml.etree.Element('Path', type='file', name=fname)
+ if altsrc:
+ entry.set("altsrc", altsrc)
+ try:
+ metadata = self.build_metadata(client)
+ self.Bind(entry, metadata)
+ data = lxml.etree.tostring(entry, encoding="UTF-8",
+ xml_declaration=True)
+ if outfile:
+ open(outfile, 'w').write(data)
+ else:
+ print(data)
+ except IOError:
+ err = sys.exc_info()[1]
+ print("Could not write to %s: %s" % (outfile, err))
+ print(data)
+ except Exception:
+ print("Failed to build entry %s for host %s" % (fname, client))
+ raise
def do_buildbundle(self, args):
"""Render a bundle for client."""
@@ -533,7 +613,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:
@@ -557,27 +637,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..5ee88535d 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
@@ -115,21 +114,6 @@ if __name__ == '__main__':
config.read(setup['configfile'])
config.read(setup['config'])
- if setup['showerrors']:
- if config.has_section("errors"):
- econf = dict(config.items("errors"))
- else:
- econf = dict()
-
- print("%-35s %-35s" % ("Error name", "Handler (Default)"))
- for err, default in Bcfg2.Server.Lint.ErrorHandler._errors.items():
- if err in econf and econf[err] != default:
- handler = "%s (%s)" % (econf[err], default)
- else:
- handler = default
- print("%-35s %-35s" % (err, handler))
- raise SystemExit(0)
-
# get list of plugins to run
if setup['args']:
allplugins = setup['args']
@@ -170,6 +154,15 @@ if __name__ == '__main__':
errorhandler = get_errorhandler(config)
+ if setup['showerrors']:
+ for plugin in serverplugins.values() + serverlessplugins.values():
+ errorhandler.RegisterErrors(getattr(plugin, 'Errors')())
+
+ print("%-35s %-35s" % ("Error name", "Handler"))
+ for err, handler in errorhandler._handlers.items():
+ print("%-35s %-35s" % (err, handler.__name__))
+ raise SystemExit(0)
+
run_serverless_plugins(serverlessplugins,
errorhandler=errorhandler,
config=config, setup=setup)
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-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