summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README14
-rw-r--r--debian/bcfg2-server.install4
-rw-r--r--debian/bcfg2-web.install3
-rw-r--r--debian/control10
-rwxr-xr-xdebian/rules4
-rw-r--r--misc/apache/bcfg2.conf26
-rw-r--r--misc/bcfg2.spec58
-rw-r--r--reports/reports.wsgi4
-rw-r--r--schemas/rules.xsd26
-rw-r--r--setup.py1
-rw-r--r--src/lib/Client/Frame.py14
-rw-r--r--src/lib/Client/Tools/APT.py2
-rw-r--r--src/lib/Client/Tools/Action.py23
-rw-r--r--src/lib/Client/Tools/Blast.py5
-rw-r--r--src/lib/Client/Tools/Chkconfig.py8
-rw-r--r--src/lib/Client/Tools/DebInit.py10
-rw-r--r--src/lib/Client/Tools/FreeBSDInit.py1
-rw-r--r--src/lib/Client/Tools/FreeBSDPackage.py3
-rw-r--r--src/lib/Client/Tools/IPS.py5
-rw-r--r--src/lib/Client/Tools/MacPorts.py1
-rw-r--r--src/lib/Client/Tools/POSIX.py633
-rw-r--r--src/lib/Client/Tools/Portage.py8
-rw-r--r--src/lib/Client/Tools/RcUpdate.py4
-rw-r--r--src/lib/Client/Tools/SMF.py17
-rw-r--r--src/lib/Client/Tools/SYSV.py6
-rw-r--r--src/lib/Client/Tools/YUMng.py91
-rw-r--r--src/lib/Client/Tools/__init__.py3
-rw-r--r--src/lib/Client/Tools/launchd.py18
-rw-r--r--src/lib/Server/Admin/Bundle.py2
-rw-r--r--src/lib/Server/Plugin.py4
-rw-r--r--src/lib/Server/Plugins/Guppy.py63
-rw-r--r--src/lib/Server/Plugins/Metadata.py18
-rw-r--r--src/lib/Server/Plugins/POSIXCompat.py38
-rw-r--r--src/lib/Server/Plugins/__init__.py1
-rw-r--r--src/lib/Server/Reports/reports/templates/base.html16
-rw-r--r--src/lib/Server/Reports/reports/templates/config_items/item.html2
-rw-r--r--src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py41
-rw-r--r--src/lib/Server/Reports/reports/views.py4
-rw-r--r--src/lib/Server/Reports/urls.py24
-rwxr-xr-xsrc/sbin/bcfg2-repo-validate41
40 files changed, 731 insertions, 525 deletions
diff --git a/README b/README
index bac8e30ae..beb0d3303 100644
--- a/README
+++ b/README
@@ -23,17 +23,17 @@ Installation
For details about the installation of Bcfg2 please refer to the
following pages in the Bcfg2 wiki.
-* Prerequisites: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Prereqs
-* Download: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Download
-* Installation: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Install
+* Prerequisites: http://bcfg2.org/wiki/Prereqs
+* Download: http://bcfg2.org/wiki/Download
+* Installation: http://bcfg2.org/wiki/Install
Need help
---------
-* FAQ: http://trac.mcs.anl.gov/projects/bcfg2/wiki/FAQ
+* FAQ: http://bcfg2.org/wiki/FAQ
* IRC: #bcfg2 on chat.freenode.net
* Mailing list: https://lists.mcs.anl.gov/mailman/listinfo/bcfg-dev
-* Bug tracker: http://trac.mcs.anl.gov/projects/bcfg2/report
+* Bug tracker: http://bcfg2.org/report
Documentation
-------------
@@ -41,8 +41,8 @@ Documentation
A lot of documentation is available in the Bcfg2 wiki and the Bcfg2
manual.
-Wiki: http://trac.mcs.anl.gov/projects/bcfg2/wiki/
-Manual: http://doc.bcfg2.fourkitchens.com/
+Wiki: http://bcfg2.org/wiki/
+Manual: http://docs.bcfg2.org/
Bcfg2 is licensed under BSD, for more details check COPYING.
diff --git a/debian/bcfg2-server.install b/debian/bcfg2-server.install
index 859806fa0..91b1b2aef 100644
--- a/debian/bcfg2-server.install
+++ b/debian/bcfg2-server.install
@@ -1,5 +1,7 @@
debian/bcfg2-server.default usr/share/bcfg2
debian/tmp/usr/bin/bcfg2-* usr/sbin
debian/tmp/usr/lib/python*/*-packages/Bcfg2/Server/*
-debian/tmp/usr/share/bcfg2/*
+debian/tmp/usr/share/bcfg2/Hostbase/*
+debian/tmp/usr/share/bcfg2/schemas/*
+debian/tmp/usr/share/bcfg2/xsl-transforms/*
debian/tmp/usr/share/man/man8/*
diff --git a/debian/bcfg2-web.install b/debian/bcfg2-web.install
new file mode 100644
index 000000000..68caa98fa
--- /dev/null
+++ b/debian/bcfg2-web.install
@@ -0,0 +1,3 @@
+misc/apache/bcfg2.conf etc/apache2/conf.d/
+debian/tmp/usr/share/bcfg2/reports.wsgi
+debian/tmp/usr/share/bcfg2/site_media/*
diff --git a/debian/control b/debian/control
index acfc6cc9c..4db926f77 100644
--- a/debian/control
+++ b/debian/control
@@ -29,3 +29,13 @@ Description: Configuration management server
for clients bound by client profiles.
bcfg2-server is the server for bcfg2 clients, which generates configuration
sets and stores statistics of client system states.
+
+Package: bcfg2-web
+Architecture: all
+Depends: ${python:Depends}, ${misc:Depends}, bcfg2 (= ${binary:Version}), python-django, libapache2-mod-wsgi
+Suggests: python-mysqldb, python-psycopg2, python-sqlite
+XB-Python-Version: >= 2.4
+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.
diff --git a/debian/rules b/debian/rules
index 928b3d2d3..1638b8415 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,4 +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 $@
@@ -14,3 +17,4 @@ override_dh_installinit:
dh_installinit --package=bcfg2 --no-start
# Install bcfg2-server initscript without starting it on postinst
dh_installinit --package=bcfg2-server --no-start
+
diff --git a/misc/apache/bcfg2.conf b/misc/apache/bcfg2.conf
new file mode 100644
index 000000000..b9b4b0452
--- /dev/null
+++ b/misc/apache/bcfg2.conf
@@ -0,0 +1,26 @@
+<IfModule mod_wsgi.c>
+ #
+ # If the root is changed update the static content alias as well
+ #
+ WSGIScriptAlias /bcfg2 "/usr/share/bcfg2/reports.wsgi"
+
+ WSGISocketPrefix run
+ WSGIDaemonProcess Bcfg2.Server.Reports processes=1 threads=10
+ WSGIProcessGroup Bcfg2.Server.Reports
+
+ #
+ # Manually set this to override the static content
+ #
+ #SetEnv bcfg2.media_url /bcfg2/site_media/
+
+ #
+ # This should have the same prefix as WSGIScriptAlias
+ #
+ Alias "/bcfg2/site_media/" "/usr/share/bcfg2/site_media/"
+ <Directory "/usr/share/bcfg2/site_media/">
+ Options None
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+ </Directory>
+</IfModule>
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index 72008435a..8e77ad908 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -13,7 +13,7 @@
%define lxmldep %(rpm -q %{alt_lxml} 2>&1 > /dev/null && echo %{alt_lxml} || echo %{dfl_lxml})
Name: bcfg2
-Version: 1.1.0
+Version: 1.2.0
Release: %{release}
Summary: Configuration management system
@@ -93,6 +93,46 @@ systems are constantly changing; if required in your environment,
Bcfg2 can enable the construction of complex change management and
deployment strategies.
+%package -n bcfg2-web
+Version: %{version}
+Summary: Bcfg2 Web Reporting Interface
+Group: System Tools
+Requires: bcfg2-server
+Requires: httpd,Django
+%if "%{_vendor}" == "redhat"
+Requires: mod_wsgi
+%define apache_conf %{_sysconfdir}/httpd
+%else
+Requires: apache2-mod_wsgi
+%define apache_conf %{_sysconfdir}/apache2
+%endif
+
+%description -n bcfg2-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
+tasks. It is the fifth generation of configuration management tools
+developed in the Mathematics and Computer Science Division of Argonne
+National Laboratory.
+
+It is based on an operational model in which the specification can be
+used to validate and optionally change the state of clients, but in a
+feature unique to bcfg2 the client's response to the specification can
+also be used to assess the completeness of the specification. Using
+this feature, bcfg2 provides an objective measure of how good a job an
+administrator has done in specifying the configuration of client
+systems. Bcfg2 is therefore built to help administrators construct an
+accurate, comprehensive specification.
+
+Bcfg2 has been designed from the ground up to support gentle
+reconciliation between the specification and current client states. It
+is designed to gracefully cope with manual system modifications.
+
+Finally, due to the rapid pace of updates on modern networks, client
+systems are constantly changing; if required in your environment,
+Bcfg2 can enable the construction of complex change management and
+deployment strategies.
+
%prep
%setup -q -n bcfg2-%{version}
@@ -117,6 +157,9 @@ deployment strategies.
%{__install} -m 755 debian/bcfg2.cron.hourly %{buildroot}%{_sysconfdir}/cron.hourly/bcfg2
%{__install} -m 755 tools/bcfg2-cron %{buildroot}%{_prefix}/lib/bcfg2/bcfg2-cron
+%{__install} -d %{buildroot}%{apache_conf}/conf.d
+%{__install} -m 644 misc/apache/bcfg2.conf %{buildroot}%{apache_conf}/conf.d/wsgi_bcfg2.conf
+
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} || exit 2
@@ -147,7 +190,10 @@ deployment strategies.
%{python_sitelib}/*egg-info
%endif
-%{_datadir}/bcfg2
+%dir %{_datadir}/bcfg2
+%{_datadir}/bcfg2/Hostbase
+%{_datadir}/bcfg2/schemas
+%{_datadir}/bcfg2/xsl-transforms
%config(noreplace) %{_sysconfdir}/default/bcfg2-server
%{_sbindir}/bcfg2-admin
%{_sbindir}/bcfg2-build-reports
@@ -160,6 +206,14 @@ deployment strategies.
%{_mandir}/man8/*.8*
%dir %{_prefix}/lib/bcfg2
+%files -n bcfg2-web
+%defattr(-,root,root,-)
+
+%{_datadir}/bcfg2/reports.wsgi
+%{_datadir}/bcfg2/site_media
+
+%config(noreplace) %{apache_conf}/conf.d/wsgi_bcfg2.conf
+
%changelog
* Mon Jun 21 2010 Fabian Affolter <fabian@bernewireless.net> - 1.1.0rc3-0.1
- Changed source0 in order that it works with spectool
diff --git a/reports/reports.wsgi b/reports/reports.wsgi
new file mode 100644
index 000000000..232650485
--- /dev/null
+++ b/reports/reports.wsgi
@@ -0,0 +1,4 @@
+import os
+os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.Server.Reports.settings'
+import django.core.handlers.wsgi
+application = django.core.handlers.wsgi.WSGIHandler()
diff --git a/schemas/rules.xsd b/schemas/rules.xsd
index 207eb65e5..80036834a 100644
--- a/schemas/rules.xsd
+++ b/schemas/rules.xsd
@@ -54,14 +54,6 @@
<xsd:attribute name='verify_flags' type='xsd:string'/>
</xsd:complexType>
- <xsd:complexType name='DirectoryType'>
- <xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='perms'/>
- <xsd:attribute type='xsd:string' name='owner'/>
- <xsd:attribute type='xsd:string' name='group'/>
- <xsd:attribute type='xsd:string' name='prune'/>
- </xsd:complexType>
-
<xsd:complexType name='ActionType'>
<xsd:attribute type='ActionTimingEnum' name='timing' use='required'/>
<xsd:attribute type='ActionWhenEnum' name='when' use='required'/>
@@ -70,11 +62,6 @@
<xsd:attribute type='xsd:string' name='command' use='required'/>
</xsd:complexType>
- <xsd:complexType name='SymLinkType'>
- <xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='to' use='required'/>
- </xsd:complexType>
-
<xsd:complexType name='PathType'>
<xsd:attribute type='PathTypeEnum' name='type' use='required'/>
<xsd:attribute type='xsd:string' name='name' use='required'/>
@@ -89,21 +76,11 @@
<xsd:attribute type='xsd:string' name='to'/>
</xsd:complexType>
- <xsd:complexType name='PermissionsType'>
- <xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='perms' use='required'/>
- <xsd:attribute type='xsd:string' name='owner' use='required'/>
- <xsd:attribute type='xsd:string' name='group' use='required'/>
- </xsd:complexType>
-
<xsd:complexType name='RContainerType'>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Service' type='ServiceType'/>
- <xsd:element name='Directory' type='DirectoryType'/>
- <xsd:element name='SymLink' type='SymLinkType'/>
<xsd:element name='Package' type='PackageType'/>
<xsd:element name='Path' type='PathType'/>
- <xsd:element name='Permissions' type='PermissionsType'/>
<xsd:element name='Action' type='ActionType'/>
<xsd:element name='Group' type='RContainerType'/>
<xsd:element name='Client' type='RContainerType'/>
@@ -117,11 +94,8 @@
<xsd:complexType>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Service' type='ServiceType'/>
- <xsd:element name='Directory' type='DirectoryType'/>
- <xsd:element name='SymLink' type='SymLinkType'/>
<xsd:element name='Package' type='PackageType'/>
<xsd:element name='Path' type='PathType'/>
- <xsd:element name='Permissions' type='PermissionsType'/>
<xsd:element name='Action' type='ActionType'/>
<xsd:element name='Group' type='RContainerType'/>
<xsd:element name='Client' type='RContainerType'/>
diff --git a/setup.py b/setup.py
index a1499e474..37a6b9e36 100644
--- a/setup.py
+++ b/setup.py
@@ -38,6 +38,7 @@ setup(cmdclass=cmdclass,
glob('reports/xsl-transforms/*.xsl')),
('share/bcfg2/xsl-transforms/xsl-transform-includes',
glob('reports/xsl-transforms/xsl-transform-includes/*.xsl')),
+ ('share/bcfg2', ['reports/reports.wsgi']),
('share/man/man1', glob("man/bcfg2.1")),
('share/man/man5', glob("man/*.5")),
('share/man/man8', glob("man/*.8")),
diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py
index 6cfb19732..545d4b584 100644
--- a/src/lib/Client/Frame.py
+++ b/src/lib/Client/Frame.py
@@ -110,20 +110,6 @@ class Frame:
self.logger.info("Loaded tool drivers:")
self.logger.info([tool.name for tool in self.tools])
if not self.dryrun and not self.setup['bundle']:
- for cfile in [cfl for cfl in config.findall(".//ConfigFile") \
- if cfl.get('name') in self.__important__]:
- tl = [t for t in self.tools if t.handlesEntry(cfile) \
- and t.canVerify(cfile)]
- if tl:
- if not tl[0].VerifyConfigFile(cfile, []):
- if self.setup['interactive'] and not \
- promptFilter("Install %s: %s? (y/N):", [cfile]):
- continue
- try:
- self.states[cfile] = tl[0].InstallConfigFile(cfile)
- except:
- self.logger.error("Unexpected tool failure",
- exc_info=1)
for cfile in [cfl for cfl in config.findall(".//Path") \
if cfl.get('name') in self.__important__ and \
cfl.get('type') == 'file']:
diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Client/Tools/APT.py
index 9dc2c5bba..2afe2eab7 100644
--- a/src/lib/Client/Tools/APT.py
+++ b/src/lib/Client/Tools/APT.py
@@ -55,7 +55,7 @@ class APT(Bcfg2.Client.Tools.Tool):
'%s/apt/apt.conf' % etc_path,
'%s/dpkg/dpkg.cfg' % etc_path] + \
[entry.get('name') for struct in config for entry in struct \
- if entry.tag in ['Path', 'ConfigFile'] and \
+ if entry.tag == 'Path' and \
entry.get('name').startswith('%s/apt/sources.list' % etc_path)]
self.nonexistent = [entry.get('name') for struct in config for entry in struct \
if entry.tag == 'Path' and entry.get('type') == 'nonexistent']
diff --git a/src/lib/Client/Tools/Action.py b/src/lib/Client/Tools/Action.py
index 3610d9015..452788f94 100644
--- a/src/lib/Client/Tools/Action.py
+++ b/src/lib/Client/Tools/Action.py
@@ -3,23 +3,34 @@ __revision__ = '$Revision$'
import Bcfg2.Client.Tools
-# <Action timing='pre|post|both' name='name' command='cmd text' when='always|modified'
-# status='ignore|check'/>
-# <PostInstall name='foo'/>
-# => <Action timing='post' when='modified' name='n' command='foo' status='ignore'/>
+"""
+<Action timing='pre|post|both'
+ name='name'
+ command='cmd text'
+ when='always|modified'
+ status='ignore|check'/>
+<PostInstall name='foo'/>
+ => <Action timing='post'
+ when='modified'
+ name='n'
+ command='foo'
+ status='ignore'/>
+"""
+
class Action(Bcfg2.Client.Tools.Tool):
"""Implement Actions"""
name = 'Action'
__handles__ = [('PostInstall', None), ('Action', None)]
__req__ = {'PostInstall': ['name'],
- 'Action':['name', 'timing', 'when', 'command', 'status']}
+ 'Action': ['name', 'timing', 'when', 'command', 'status']}
def RunAction(self, entry):
"""This method handles command execution and status return."""
if not self.setup['dryrun']:
if self.setup['interactive']:
- prompt = 'Run Action %s, %s: (y/N): ' % (entry.get('name'), entry.get('command'))
+ prompt = ('Run Action %s, %s: (y/N): ' %
+ (entry.get('name'), entry.get('command')))
if raw_input(prompt) not in ['y', 'Y']:
return False
if self.setup['servicemode'] == 'build':
diff --git a/src/lib/Client/Tools/Blast.py b/src/lib/Client/Tools/Blast.py
index 737c6924e..4f2891fd6 100644
--- a/src/lib/Client/Tools/Blast.py
+++ b/src/lib/Client/Tools/Blast.py
@@ -2,7 +2,9 @@
"""This provides bcfg2 support for blastwave"""
__revision__ = '$Revision$'
-import Bcfg2.Client.Tools.SYSV, tempfile
+import tempfile
+import Bcfg2.Client.Tools.SYSV
+
class Blast(Bcfg2.Client.Tools.SYSV.SYSV):
"""Support for Blastwave packages"""
@@ -27,7 +29,6 @@ class Blast(Bcfg2.Client.Tools.SYSV.SYSV):
# Install comes from Bcfg2.Client.Tools.PkgTool
# Extra comes from Bcfg2.Client.Tools.Tool
# Remove comes from Bcfg2.Client.Tools.SYSV
-
def FindExtraPackages(self):
"""Pass through to null FindExtra call."""
return []
diff --git a/src/lib/Client/Tools/Chkconfig.py b/src/lib/Client/Tools/Chkconfig.py
index 5dbb7b345..b7227ec3d 100644
--- a/src/lib/Client/Tools/Chkconfig.py
+++ b/src/lib/Client/Tools/Chkconfig.py
@@ -81,7 +81,9 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
self.logger.info("Installing Service %s" % (entry.get('name')))
pass1 = True
if entry.get('status') == 'off':
- rc = self.cmd.run(rcmd % (entry.get('name'), entry.get('status')) + " --level 0123456")[0]
+ rc = self.cmd.run(rcmd % (entry.get('name'),
+ entry.get('status')) + \
+ " --level 0123456")[0]
pass1 = rc == 0
rc = self.cmd.run(rcmd % (entry.get('name'), entry.get('status')))[0]
return pass1 and rc == 0
@@ -93,5 +95,7 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
self.logger.debug('Found active services:')
self.logger.debug(allsrv)
specified = [srv.get('name') for srv in self.getSupportedEntries()]
- return [Bcfg2.Client.XML.Element('Service', type='chkconfig', name=name) \
+ return [Bcfg2.Client.XML.Element('Service',
+ type='chkconfig',
+ name=name) \
for name in allsrv if name not in specified]
diff --git a/src/lib/Client/Tools/DebInit.py b/src/lib/Client/Tools/DebInit.py
index 0185f420c..aee8ffd65 100644
--- a/src/lib/Client/Tools/DebInit.py
+++ b/src/lib/Client/Tools/DebInit.py
@@ -1,16 +1,18 @@
"""Debian Init Support for Bcfg2"""
__revision__ = '$Revision$'
-import glob, os, re
+import glob
+import os
+import re
import Bcfg2.Client.Tools
-
# Debian squeeze and beyond uses a dependecy based boot sequence
DEBIAN_OLD_STYLE_BOOT_SEQUENCE = (
'etch', '4.0',
'lenny', '5.0', '5.0.1', '5.0.2', '5.0.3', '5.0.4', '5.0.4', '5.0.5',
)
+
class DebInit(Bcfg2.Client.Tools.SvcTool):
"""Debian Service Support for Bcfg2."""
name = 'DebInit'
@@ -35,7 +37,9 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
start_sequence = int(entry.get('sequence'))
kill_sequence = 100 - start_sequence
else:
- self.logger.warning("Your debian version boot sequence is dependency based \"sequence\" attribute wil be ignored.")
+ self.logger.warning("Your debian version boot sequence is "
+ "dependency based \"sequence\" attribute "
+ "will be ignored.")
else:
start_sequence = None
diff --git a/src/lib/Client/Tools/FreeBSDInit.py b/src/lib/Client/Tools/FreeBSDInit.py
index e597a294b..10f0f2e93 100644
--- a/src/lib/Client/Tools/FreeBSDInit.py
+++ b/src/lib/Client/Tools/FreeBSDInit.py
@@ -8,6 +8,7 @@ __revision__ = '$Rev$'
import os
import Bcfg2.Client.Tools
+
class FreeBSDInit(Bcfg2.Client.Tools.SvcTool):
"""FreeBSD service support for Bcfg2."""
name = 'FreeBSDInit'
diff --git a/src/lib/Client/Tools/FreeBSDPackage.py b/src/lib/Client/Tools/FreeBSDPackage.py
index 49cfa5bd2..04c05adaa 100644
--- a/src/lib/Client/Tools/FreeBSDPackage.py
+++ b/src/lib/Client/Tools/FreeBSDPackage.py
@@ -8,6 +8,7 @@ __revision__ = '$Rev$'
import re
import Bcfg2.Client.Tools
+
class FreeBSDPackage(Bcfg2.Client.Tools.PkgTool):
"""The FreeBSD toolset implements package operations and inherits
the rest from Toolset.Toolset."""
@@ -24,7 +25,7 @@ class FreeBSDPackage(Bcfg2.Client.Tools.PkgTool):
pattern = re.compile('(.*)-(\d.*)')
for pkg in packages:
if pattern.match(pkg):
- name = pattern.match(pkg).group(1)
+ name = pattern.match(pkg).group(1)
version = pattern.match(pkg).group(2)
self.installed[name] = version
diff --git a/src/lib/Client/Tools/IPS.py b/src/lib/Client/Tools/IPS.py
index d7d84617f..9afd23143 100644
--- a/src/lib/Client/Tools/IPS.py
+++ b/src/lib/Client/Tools/IPS.py
@@ -1,11 +1,12 @@
"""This is the Bcfg2 support for OpenSolaris packages."""
__revision__ = '$Revision$'
-import Bcfg2.Client.Tools
-
import pkg.client.image as image
import pkg.client.progress as progress
+import Bcfg2.Client.Tools
+
+
class IPS(Bcfg2.Client.Tools.PkgTool):
"""The IPS driver implements OpenSolaris package operations."""
name = 'IPS'
diff --git a/src/lib/Client/Tools/MacPorts.py b/src/lib/Client/Tools/MacPorts.py
index 70285fa3a..23b536451 100644
--- a/src/lib/Client/Tools/MacPorts.py
+++ b/src/lib/Client/Tools/MacPorts.py
@@ -3,6 +3,7 @@ __revision__ = '$Revision$'
import Bcfg2.Client.Tools
+
class MacPorts(Bcfg2.Client.Tools.PkgTool):
"""macports package support."""
name = 'MacPorts'
diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Client/Tools/POSIX.py
index 9d37b77d3..d2611130c 100644
--- a/src/lib/Client/Tools/POSIX.py
+++ b/src/lib/Client/Tools/POSIX.py
@@ -1,11 +1,11 @@
"""All POSIX Type client support for Bcfg2."""
__revision__ = '$Revision$'
-from datetime import datetime
from stat import S_ISVTX, S_ISGID, S_ISUID, S_IXUSR, S_IWUSR, S_IRUSR, S_IXGRP
from stat import S_IWGRP, S_IRGRP, S_IXOTH, S_IWOTH, S_IROTH, ST_MODE, S_ISDIR
from stat import S_IFREG, ST_UID, ST_GID, S_ISREG, S_IFDIR, S_ISLNK, ST_MTIME
import binascii
+from datetime import datetime
import difflib
import errno
import grp
@@ -14,7 +14,6 @@ import os
import pwd
import shutil
import stat
-import string
import time
import Bcfg2.Client.Tools
import Bcfg2.Options
@@ -44,19 +43,6 @@ def calcPerms(initial, perms):
tempperms |= perm
return tempperms
-def normUid(entry):
- """
- This takes a user name or uid and
- returns the corresponding uid or False.
- """
- try:
- try:
- return int(entry.get('owner'))
- except:
- return int(pwd.getpwnam(entry.get('owner'))[2])
- except (OSError, KeyError):
- log.error('UID normalization failed for %s' % (entry.get('name')))
- return False
def normGid(entry):
"""
@@ -72,38 +58,33 @@ def normGid(entry):
log.error('GID normalization failed for %s' % (entry.get('name')))
return False
-text_chars = "".join([chr(y) for y in range(32, 127)] + list("\n\r\t\b"))
-notrans = string.maketrans("", "")
-def isString(strng):
- """Returns true if a string contains no binary chars."""
- if "\0" in strng:
+def normUid(entry):
+ """
+ This takes a user name or uid and
+ returns the corresponding uid or False.
+ """
+ try:
+ try:
+ return int(entry.get('owner'))
+ except:
+ return int(pwd.getpwnam(entry.get('owner'))[2])
+ except (OSError, KeyError):
+ log.error('UID normalization failed for %s' % (entry.get('name')))
return False
- if not strng:
- return True
-
- return len(strng.translate(notrans, text_chars)) == 0
class POSIX(Bcfg2.Client.Tools.Tool):
"""POSIX File support code."""
name = 'POSIX'
- __handles__ = [('ConfigFile', None),
- ('Directory', None),
- ('Path', 'device'),
+ __handles__ = [('Path', 'device'),
('Path', 'directory'),
('Path', 'file'),
('Path', 'hardlink'),
('Path', 'nonexistent'),
('Path', 'permissions'),
- ('Path', 'symlink'),
- ('Permissions', None),
- ('SymLink', None)]
- __req__ = {'ConfigFile': ['name', 'owner', 'group', 'perms'],
- 'Directory': ['name', 'owner', 'group', 'perms'],
- 'Path': ['name', 'type'],
- 'Permissions': ['name', 'owner', 'group', 'perms'],
- 'SymLink': ['name', 'to']}
+ ('Path', 'symlink')]
+ __req__ = {'Path': ['name', 'type']}
# grab paranoid options from /etc/bcfg2.conf
opts = {'ppath': Bcfg2.Options.PARANOID_PATH,
@@ -116,63 +97,150 @@ class POSIX(Bcfg2.Client.Tools.Tool):
def canInstall(self, entry):
"""Check if entry is complete for installation."""
if Bcfg2.Client.Tools.Tool.canInstall(self, entry):
- if (entry.tag, entry.text, entry.get('empty', 'false')) == \
- ('ConfigFile', None, 'false'):
+ if (entry.tag,
+ entry.get('type'),
+ entry.text,
+ entry.get('empty', 'false')) == ('Path',
+ 'file',
+ None,
+ 'false'):
return False
return True
else:
return False
- def VerifySymLink(self, entry, _):
- """Verify SymLink Entry."""
- try:
- sloc = os.readlink(entry.get('name'))
- if sloc == entry.get('to'):
- return True
- self.logger.debug("Symlink %s points to %s, should be %s" % \
- (entry.get('name'), sloc, entry.get('to')))
- entry.set('current_to', sloc)
- entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'),
- entry.get('to')))
+ def gatherCurrentData(self, entry):
+ if entry.tag == 'Path' and entry.get('type') == 'file':
+ try:
+ ondisk = os.stat(entry.get('name'))
+ except OSError:
+ entry.set('current_exists', 'false')
+ self.logger.debug("%s %s does not exist" %
+ (entry.tag, entry.get('name')))
+ return False
+ try:
+ entry.set('current_owner', str(ondisk[ST_UID]))
+ entry.set('current_group', str(ondisk[ST_GID]))
+ except (OSError, KeyError):
+ pass
+ entry.set('perms', str(oct(ondisk[ST_MODE])[-4:]))
+ try:
+ content = open(entry.get('name')).read()
+ entry.set('current_bfile', binascii.b2a_base64(content))
+ except IOError, error:
+ self.logger.error("Failed to read %s: %s" % (error.filename,
+ error.strerror))
+
+ def Verifydevice(self, entry, _):
+ """Verify device entry."""
+ if entry.get('dev_type') == None or \
+ entry.get('owner') == None or \
+ entry.get('group') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % (entry.get('name')))
return False
+ if entry.get('dev_type') in ['block', 'char']:
+ # check if major/minor are properly specified
+ if entry.get('major') == None or \
+ entry.get('minor') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % (entry.get('name')))
+ return False
+ try:
+ # check for file existence
+ filestat = os.stat(entry.get('name'))
except OSError:
entry.set('current_exists', 'false')
- entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'),
- entry.get('to')))
+ self.logger.debug("%s %s does not exist" %
+ (entry.tag, entry.get('name')))
return False
- def InstallSymLink(self, entry):
- """Install SymLink entry."""
- self.logger.info("Installing Symlink %s" % (entry.get('name')))
- if os.path.lexists(entry.get('name')):
- try:
- fmode = os.lstat(entry.get('name'))[ST_MODE]
- if S_ISREG(fmode) or S_ISLNK(fmode):
- self.logger.debug("Non-directory entry already exists at "
- "%s. Unlinking entry." % \
- (entry.get('name')))
- os.unlink(entry.get('name'))
- elif S_ISDIR(fmode):
- self.logger.debug("Directory entry already exists at %s" %\
- (entry.get('name')))
- self.cmd.run("mv %s/ %s.bak" % \
- (entry.get('name'),
- entry.get('name')))
- else:
- os.unlink(entry.get('name'))
- except OSError:
- self.logger.info("Symlink %s cleanup failed" % (entry.get('name')))
try:
- os.symlink(entry.get('to'), entry.get('name'))
- return True
+ # attempt to verify device properties as specified in config
+ dev_type = entry.get('dev_type')
+ mode = calcPerms(device_map[dev_type],
+ entry.get('mode', '0600'))
+ owner = normUid(entry)
+ group = normGid(entry)
+ if dev_type in ['block', 'char']:
+ # check for incompletely specified entries
+ if entry.get('major') == None or \
+ entry.get('minor') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % (entry.get('name')))
+ return False
+ major = int(entry.get('major'))
+ minor = int(entry.get('minor'))
+ if major == os.major(filestat.st_rdev) and \
+ minor == os.minor(filestat.st_rdev) and \
+ mode == filestat.st_mode and \
+ owner == filestat.st_uid and \
+ group == filestat.st_gid:
+ return True
+ else:
+ return False
+ elif dev_type == 'fifo' and \
+ mode == filestat.st_mode and \
+ owner == filestat.st_uid and \
+ group == filestat.st_gid:
+ return True
+ else:
+ self.logger.info('Device properties for %s incorrect' % \
+ entry.get('name'))
+ return False
except OSError:
+ self.logger.debug("%s %s failed to verify" %
+ (entry.tag, entry.get('name')))
return False
- def VerifyDirectory(self, entry, modlist):
- """Verify Directory entry."""
+ def Installdevice(self, entry):
+ """Install device entries."""
+ try:
+ # check for existing paths and remove them
+ os.lstat(entry.get('name'))
+ try:
+ os.unlink(entry.get('name'))
+ exists = False
+ except OSError:
+ self.logger.info('Failed to unlink %s' % \
+ entry.get('name'))
+ return False
+ except OSError:
+ exists = False
+
+ if not exists:
+ try:
+ dev_type = entry.get('dev_type')
+ mode = calcPerms(device_map[dev_type],
+ entry.get('mode', '0600'))
+ if dev_type in ['block', 'char']:
+ # check if major/minor are properly specified
+ if entry.get('major') == None or \
+ entry.get('minor') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % (entry.get('name')))
+ return False
+ major = int(entry.get('major'))
+ minor = int(entry.get('minor'))
+ device = os.makedev(major, minor)
+ os.mknod(entry.get('name'), mode, device)
+ else:
+ os.mknod(entry.get('name'), mode)
+ os.chown(entry.get('name'), normUid(entry), normGid(entry))
+ return True
+ except KeyError:
+ self.logger.error('Failed to install %s' % entry.get('name'))
+ except OSError:
+ self.logger.error('Failed to install %s' % entry.get('name'))
+ return False
+
+ def Verifydirectory(self, entry, modlist):
+ """Verify Path type='directory' entry."""
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-repo-validate.' % (entry.get('name')))
return False
while len(entry.get('perms', '')) < 4:
entry.set('perms', '0' + entry.get('perms', ''))
@@ -205,8 +273,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
pruneTrue = True
ex_ents = []
if entry.get('prune', 'false') == 'true' \
- and (entry.tag == 'Directory' or entry.get('type') == 'directory'):
- # FIXME: need to verify both old and new POSIX types
+ and (entry.tag == 'Path' and entry.get('type') == 'directory'):
+ # check for any extra entries when prune='true' attribute is set
try:
entries = ['/'.join([entry.get('name'), ent]) \
for ent in os.listdir(entry.get('name'))]
@@ -217,10 +285,12 @@ class POSIX(Bcfg2.Client.Tools.Tool):
entry.get('name'))
self.logger.debug(ex_ents)
nqtext = entry.get('qtext', '') + '\n'
- nqtext += "Directory %s contains extra entries:" % entry.get('name')
+ nqtext += "Directory %s contains extra entries:" % \
+ entry.get('name')
nqtext += ":".join(ex_ents)
entry.set('qtest', nqtext)
- [entry.append(XML.Element('Prune', path=x)) for x in ex_ents]
+ [entry.append(XML.Element('Prune', path=x)) \
+ for x in ex_ents]
except OSError:
ex_ents = []
pruneTrue = True
@@ -236,7 +306,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
entry.set('qtext', nqtext)
if group != str(normGid(entry)):
entry.set('current_group', group)
- self.logger.debug("%s %s group wrong" % (entry.tag, entry.get('name')))
+ self.logger.debug("%s %s group wrong" % \
+ (entry.tag, entry.get('name')))
nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s group is %s should be %s" % \
(entry.get('name'), group, entry.get('group'))
@@ -244,10 +315,16 @@ class POSIX(Bcfg2.Client.Tools.Tool):
if perms != entry.get('perms'):
entry.set('current_perms', perms)
self.logger.debug("%s %s permissions are %s should be %s" %
- (entry.tag, entry.get('name'), perms, entry.get('perms')))
+ (entry.tag,
+ entry.get('name'),
+ perms,
+ entry.get('perms')))
nqtext = entry.get('qtext', '') + '\n'
- nqtext += "%s perms are %s should be %s" % \
- (entry.get('name'), perms, entry.get('perms'))
+ nqtext += "%s %s perms are %s should be %s" % \
+ (entry.tag,
+ entry.get('name'),
+ perms,
+ entry.get('perms'))
entry.set('qtext', nqtext)
if mtime != entry.get('mtime', '-1'):
entry.set('current_mtime', mtime)
@@ -258,21 +335,23 @@ class POSIX(Bcfg2.Client.Tools.Tool):
nqtext += "%s mtime is %s should be %s" % \
(entry.get('name'), mtime, entry.get('mtime'))
entry.set('qtext', nqtext)
- if entry.tag != 'ConfigFile':
+ if entry.get('type') != 'file':
nnqtext = entry.get('qtext')
- nnqtext += '\nInstall %s %s: (y/N) ' % (entry.tag, entry.get('name'))
+ nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'),
+ entry.get('name'))
entry.set('qtext', nnqtext)
return pTrue and pruneTrue
- def InstallDirectory(self, entry):
- """Install Directory entry."""
+ def Installdirectory(self, entry):
+ """Install Path type='directory' entry."""
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-repo-validate.' % (entry.get('name')))
+ 'Try running bcfg2-repo-validate.' % \
+ (entry.get('name')))
return False
- self.logger.info("Installing Directory %s" % (entry.get('name')))
+ self.logger.info("Installing directory %s" % (entry.get('name')))
try:
fmode = os.lstat(entry.get('name'))
if not S_ISDIR(fmode[ST_MODE]):
@@ -282,7 +361,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
os.unlink(entry.get('name'))
exists = False
except OSError:
- self.logger.info("Failed to unlink %s" % (entry.get('name')))
+ self.logger.info("Failed to unlink %s" % \
+ (entry.get('name')))
return False
else:
self.logger.debug("Found a pre-existing directory at %s" % \
@@ -327,7 +407,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
pname = pent.get('path')
ulfailed = False
if os.path.isdir(pname):
- self.logger.info("Not removing extra directory %s, please check and remove manually" % pname)
+ self.logger.info("Not removing extra directory %s, "
+ "please check and remove manually" % pname)
continue
try:
self.logger.debug("Unlinking file %s" % pname)
@@ -337,188 +418,12 @@ class POSIX(Bcfg2.Client.Tools.Tool):
ulfailed = True
if ulfailed:
return False
- return self.InstallPermissions(entry)
-
- def VerifyhardLink(self, entry, _):
- """Verify HardLink entry."""
- try:
- if os.path.samefile(entry.get('name'), entry.get('to')):
- return True
- self.logger.debug("Hardlink %s is incorrect" % \
- entry.get('name'))
- entry.set('qtext', "Link %s to %s? [y/N] " % \
- (entry.get('name'),
- entry.get('to')))
- return False
- except OSError:
- entry.set('current_exists', 'false')
- entry.set('qtext', "Link %s to %s? [y/N] " % \
- (entry.get('name'),
- entry.get('to')))
- return False
-
- def InstallhardLink(self, entry):
- """Install HardLink entry."""
- self.logger.info("Installing Hardlink %s" % (entry.get('name')))
- if os.path.lexists(entry.get('name')):
- try:
- fmode = os.lstat(entry.get('name'))[ST_MODE]
- if S_ISREG(fmode) or S_ISLNK(fmode):
- self.logger.debug("Non-directory entry already exists at "
- "%s. Unlinking entry." % (entry.get('name')))
- os.unlink(entry.get('name'))
- elif S_ISDIR(fmode):
- self.logger.debug("Directory entry already exists at %s" % \
- (entry.get('name')))
- self.cmd.run("mv %s/ %s.bak" % \
- (entry.get('name'),
- entry.get('name')))
- else:
- os.unlink(entry.get('name'))
- except OSError:
- self.logger.info("Hardlink %s cleanup failed" % (entry.get('name')))
- try:
- os.link(entry.get('to'), entry.get('name'))
- return True
- except OSError:
- return False
-
- def VerifyPermissions(self, entry, _):
- """Verify Permissions entry"""
- return self.VerifyDirectory(entry, _)
+ return self.Installpermissions(entry)
- def InstallPermissions(self, entry):
- """Install POSIX permissions"""
- 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-repo-validate.' % (entry.get('name')))
- return False
- try:
- os.chown(entry.get('name'), normUid(entry), normGid(entry))
- os.chmod(entry.get('name'), calcPerms(S_IFDIR, entry.get('perms')))
- return True
- except (OSError, KeyError):
- self.logger.error('Permission fixup failed for %s' % \
- (entry.get('name')))
- return False
-
- def Verifydevice(self, entry, _):
- """Verify device entry."""
- try:
- # check for file existence
- filestat = os.stat(entry.get('name'))
- except OSError:
- entry.set('current_exists', 'false')
- self.logger.debug("%s %s does not exist" %
- (entry.tag, entry.get('name')))
- return False
-
- try:
- # attempt to verify device properties as specified in config
- dev_type = entry.get('dev_type')
- mode = calcPerms(device_map[dev_type],
- entry.get('mode', '0600'))
- owner = entry.get('owner')
- group = entry.get('group')
- if dev_type in ['block', 'char']:
- major = int(entry.get('major'))
- minor = int(entry.get('minor'))
- if major == os.major(filestat.st_rdev) and \
- minor == os.minor(filestat.st_rdev) and \
- mode == filestat.st_mode and \
- owner == filestat.st_uid and \
- group == filestat.st_gid:
- return True
- else:
- return False
- elif dev_type == 'fifo' and \
- mode == filestat.st_mode and \
- owner == filestat.st_uid and \
- group == filestat.st_gid:
- return True
- else:
- self.logger.info('Device properties for %s incorrect' % \
- entry.get('name'))
- return False
- except OSError:
- self.logger.debug("%s %s failed to verify" %
- (entry.tag, entry.get('name')))
- return False
-
- def Installdevice(self, entry):
- """Install device entries."""
- try:
- # check for existing paths and remove them
- filestat = os.lstat(entry.get('name'))
- try:
- os.unlink(entry.get('name'))
- exists = False
- except OSError:
- self.logger.info('Failed to unlink %s' % \
- entry.get('name'))
- return False
- except OSError:
- exists = False
-
- if not exists:
- try:
- dev_type = entry.get('dev_type')
- mode = calcPerms(device_map[dev_type],
- entry.get('mode', '0600'))
- if dev_type in ['block', 'char']:
- major = int(entry.get('major'))
- minor = int(entry.get('minor'))
- device = os.makedev(major, minor)
- os.mknod(entry.get('name'), mode, device)
- else:
- os.mknod(entry.get('name'), mode)
- os.chown(entry.get('name'), normUid(entry), normGid(entry))
- return True
- except OSError:
- self.logger.error('Failed to install %s' % entry.get('name'))
- return False
-
- def Verifynonexistent(self, entry, _):
- """Verify nonexistent entry."""
- # return true if path does _not_ exist
- return not os.path.lexists(entry.get('name'))
-
- def Installnonexistent(self, entry):
- '''Remove nonexistent entries'''
- try:
- os.remove(entry.get('name'))
- return True
- except OSError:
- self.logger.error('Failed to remove %s' % entry.get('name'))
- return False
-
- def gatherCurrentData(self, entry):
- if entry.tag == 'ConfigFile':
- try:
- ondisk = os.stat(entry.get('name'))
- except OSError:
- entry.set('current_exists', 'false')
- self.logger.debug("%s %s does not exist" %
- (entry.tag, entry.get('name')))
- return False
- try:
- entry.set('current_owner', str(ondisk[ST_UID]))
- entry.set('current_group', str(ondisk[ST_GID]))
- except (OSError, KeyError):
- pass
- entry.set('perms', str(oct(ondisk[ST_MODE])[-4:]))
- try:
- content = open(entry.get('name')).read()
- entry.set('current_bfile', binascii.b2a_base64(content))
- except IOError, error:
- self.logger.error("Failed to read %s: %s" % (error.filename, error.strerror))
-
- def VerifyConfigFile(self, entry, _):
- """Install ConfigFile entry."""
- # configfile verify is permissions check + content check
- permissionStatus = self.VerifyDirectory(entry, _)
+ def Verifyfile(self, entry, _):
+ """Verify Path type='file' entry."""
+ # permissions check + content check
+ permissionStatus = self.Verifydirectory(entry, _)
tbin = False
if entry.get('encoding', 'ascii') == 'base64':
tempdata = binascii.a2b_base64(entry.text)
@@ -527,8 +432,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
tempdata = ''
else:
if entry.text == None:
- self.logger.error("Cannot verify incomplete ConfigFile %s" % \
- (entry.get('name')))
+ self.logger.error("Cannot verify incomplete Path type='%s' %s" % \
+ (entry.get('type'), entry.get('name')))
return False
tempdata = entry.text
if type(tempdata) == unicode:
@@ -551,7 +456,12 @@ class POSIX(Bcfg2.Client.Tools.Tool):
# md5sum so it would be faster for big binary files
contentStatus = content == tempdata
if not contentStatus:
- if tbin or not isString(content):
+ try:
+ content.decode('ascii')
+ isstring = True
+ except:
+ isstring = False
+ if tbin or not isstring:
entry.set('current_bfile', binascii.b2a_base64(content))
nqtext = entry.get('qtext', '')
nqtext += '\nBinary file, no printable diff'
@@ -560,7 +470,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
rawdiff = []
start = time.time()
longtime = False
- for x in difflib.ndiff(content.split('\n'), tempdata.split('\n')):
+ for x in difflib.ndiff(content.split('\n'),
+ tempdata.split('\n')):
now = time.time()
rawdiff.append(x)
if now - start > 5 and not longtime:
@@ -599,9 +510,9 @@ class POSIX(Bcfg2.Client.Tools.Tool):
entry.set('qtext', qtxt)
return contentStatus and permissionStatus
- def InstallConfigFile(self, entry):
- """Install ConfigFile entry."""
- self.logger.info("Installing ConfigFile %s" % (entry.get('name')))
+ def Installfile(self, entry):
+ """Install Path type='file' entry."""
+ self.logger.info("Installing file %s" % (entry.get('name')))
parent = "/".join(entry.get('name').split('/')[:-1])
if parent:
@@ -635,9 +546,9 @@ class POSIX(Bcfg2.Client.Tools.Tool):
self.setup.get("paranoid", False) and not \
(entry.get('current_exists', 'true') == 'false'):
bkupnam = entry.get('name').replace('/', '_')
- # current list of backups for this ConfigFile
+ # current list of backups for this file
bkuplist = [f for f in os.listdir(self.ppath) if
- f.startswith(bkupnam)]
+ f.startswith(bkupnam)]
bkuplist.sort()
if len(bkuplist) == int(self.max_copies):
# remove the oldest backup available
@@ -656,7 +567,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
self.logger.info("Backup of %s saved to %s" %
(entry.get('name'), self.ppath))
except IOError, e:
- self.logger.error("Failed to create backup file for ConfigFile %s" % \
+ self.logger.error("Failed to create backup file for %s" % \
(entry.get('name')))
self.logger.error(e)
return False
@@ -687,7 +598,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
os.utime(entry.get('name'), (int(entry.get('mtime')),
int(entry.get('mtime'))))
except:
- self.logger.error("ConfigFile %s mtime fix failed" \
+ self.logger.error("File %s mtime fix failed" \
% (entry.get('name')))
return False
return True
@@ -698,42 +609,158 @@ class POSIX(Bcfg2.Client.Tools.Tool):
print(err)
return False
- def Verifydirectory(self, entry, _):
- ret = getattr(self, 'VerifyDirectory')
- return ret(entry, _)
+ def Verifyhardlink(self, entry, _):
+ """Verify HardLink entry."""
+ if entry.get('to') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % \
+ (entry.get('name')))
+ return False
+ try:
+ if os.path.samefile(entry.get('name'), entry.get('to')):
+ return True
+ self.logger.debug("Hardlink %s is incorrect" % \
+ entry.get('name'))
+ entry.set('qtext', "Link %s to %s? [y/N] " % \
+ (entry.get('name'),
+ entry.get('to')))
+ return False
+ except OSError:
+ entry.set('current_exists', 'false')
+ entry.set('qtext', "Link %s to %s? [y/N] " % \
+ (entry.get('name'),
+ entry.get('to')))
+ return False
- def Installdirectory(self, entry):
- ret = getattr(self, 'InstallDirectory')
- return ret(entry)
+ def Installhardlink(self, entry):
+ """Install HardLink entry."""
+ if entry.get('to') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % \
+ (entry.get('name')))
+ return False
+ self.logger.info("Installing Hardlink %s" % (entry.get('name')))
+ if os.path.lexists(entry.get('name')):
+ try:
+ fmode = os.lstat(entry.get('name'))[ST_MODE]
+ if S_ISREG(fmode) or S_ISLNK(fmode):
+ self.logger.debug("Non-directory entry already exists at "
+ "%s. Unlinking entry." % (entry.get('name')))
+ os.unlink(entry.get('name'))
+ elif S_ISDIR(fmode):
+ self.logger.debug("Directory already exists at %s" % \
+ (entry.get('name')))
+ self.cmd.run("mv %s/ %s.bak" % \
+ (entry.get('name'),
+ entry.get('name')))
+ else:
+ os.unlink(entry.get('name'))
+ except OSError:
+ self.logger.info("Hardlink %s cleanup failed" % \
+ (entry.get('name')))
+ try:
+ os.link(entry.get('to'), entry.get('name'))
+ return True
+ except OSError:
+ return False
- def Verifyfile(self, entry, _):
- ret = getattr(self, 'VerifyConfigFile')
- return ret(entry, _)
+ def Verifynonexistent(self, entry, _):
+ """Verify nonexistent entry."""
+ # return true if path does _not_ exist
+ return not os.path.lexists(entry.get('name'))
- def Installfile(self, entry):
- ret = getattr(self, 'InstallConfigFile')
- return ret(entry)
+ def Installnonexistent(self, entry):
+ '''Remove nonexistent entries'''
+ try:
+ os.remove(entry.get('name'))
+ return True
+ except OSError:
+ self.logger.error('Failed to remove %s' % entry.get('name'))
+ return False
def Verifypermissions(self, entry, _):
- ret = getattr(self, 'VerifyPermissions')
- return ret(entry, _)
+ """Verify Path type='permissions' entry"""
+ return self.Verifydirectory(entry, _)
def Installpermissions(self, entry):
- ret = getattr(self, 'InstallPermissions')
- return ret(entry)
+ """Install POSIX permissions"""
+ 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-repo-validate.' % (entry.get('name')))
+ return False
+ try:
+ os.chown(entry.get('name'), normUid(entry), normGid(entry))
+ os.chmod(entry.get('name'), calcPerms(S_IFDIR, entry.get('perms')))
+ return True
+ except (OSError, KeyError):
+ self.logger.error('Permission fixup failed for %s' % \
+ (entry.get('name')))
+ return False
def Verifysymlink(self, entry, _):
- ret = getattr(self, 'VerifySymLink')
- return ret(entry, _)
+ """Verify Path type='symlink' entry."""
+ if entry.get('to') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % \
+ (entry.get('name')))
+ return False
+ try:
+ sloc = os.readlink(entry.get('name'))
+ if sloc == entry.get('to'):
+ return True
+ self.logger.debug("Symlink %s points to %s, should be %s" % \
+ (entry.get('name'), sloc, entry.get('to')))
+ entry.set('current_to', sloc)
+ entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'),
+ entry.get('to')))
+ return False
+ except OSError:
+ entry.set('current_exists', 'false')
+ entry.set('qtext', "Link %s to %s? [y/N] " % (entry.get('name'),
+ entry.get('to')))
+ return False
def Installsymlink(self, entry):
- ret = getattr(self, 'InstallSymLink')
- return ret(entry)
+ """Install Path type='symlink' entry."""
+ if entry.get('to') == None:
+ self.logger.error('Entry %s not completely specified. '
+ 'Try running bcfg2-repo-validate.' % \
+ (entry.get('name')))
+ return False
+ self.logger.info("Installing symlink %s" % (entry.get('name')))
+ if os.path.lexists(entry.get('name')):
+ try:
+ fmode = os.lstat(entry.get('name'))[ST_MODE]
+ if S_ISREG(fmode) or S_ISLNK(fmode):
+ self.logger.debug("Non-directory entry already exists at "
+ "%s. Unlinking entry." % \
+ (entry.get('name')))
+ os.unlink(entry.get('name'))
+ elif S_ISDIR(fmode):
+ self.logger.debug("Directory already exists at %s" %\
+ (entry.get('name')))
+ self.cmd.run("mv %s/ %s.bak" % \
+ (entry.get('name'),
+ entry.get('name')))
+ else:
+ os.unlink(entry.get('name'))
+ except OSError:
+ self.logger.info("Symlink %s cleanup failed" %\
+ (entry.get('name')))
+ try:
+ os.symlink(entry.get('to'), entry.get('name'))
+ return True
+ except OSError:
+ return False
def InstallPath(self, entry):
+ """Dispatch install to the proper method according to type"""
ret = getattr(self, 'Install%s' % entry.get('type'))
return ret(entry)
def VerifyPath(self, entry, _):
+ """Dispatch verify to the proper method according to type"""
ret = getattr(self, 'Verify%s' % entry.get('type'))
return ret(entry, _)
diff --git a/src/lib/Client/Tools/Portage.py b/src/lib/Client/Tools/Portage.py
index 765e981fe..58d2aad29 100644
--- a/src/lib/Client/Tools/Portage.py
+++ b/src/lib/Client/Tools/Portage.py
@@ -4,9 +4,10 @@ __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."""
+ """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')]
@@ -47,8 +48,7 @@ class Portage(Bcfg2.Client.Tools.PkgTool):
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 check '=%s-%s' 2>&1 |grep '!!!' | awk '{print $2}'" \
+ output = self.cmd.run("/usr/bin/equery 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]:
diff --git a/src/lib/Client/Tools/RcUpdate.py b/src/lib/Client/Tools/RcUpdate.py
index a91562c30..159172b78 100644
--- a/src/lib/Client/Tools/RcUpdate.py
+++ b/src/lib/Client/Tools/RcUpdate.py
@@ -87,5 +87,7 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
self.logger.debug('Found active services:')
self.logger.debug(allsrv)
specified = [srv.get('name') for srv in self.getSupportedEntries()]
- return [Bcfg2.Client.XML.Element('Service', type='rc-update', name=name) \
+ return [Bcfg2.Client.XML.Element('Service',
+ type='rc-update',
+ name=name) \
for name in allsrv if name not in specified]
diff --git a/src/lib/Client/Tools/SMF.py b/src/lib/Client/Tools/SMF.py
index 733228d18..f0bc6bd05 100644
--- a/src/lib/Client/Tools/SMF.py
+++ b/src/lib/Client/Tools/SMF.py
@@ -1,15 +1,18 @@
"""SMF support for Bcfg2"""
__revision__ = '$Revision$'
-import glob, os
+import glob
+import os
+
import Bcfg2.Client.Tools
+
class SMF(Bcfg2.Client.Tools.SvcTool):
"""Support for Solaris SMF Services."""
__handles__ = [('Service', 'smf')]
__execs__ = ['/usr/sbin/svcadm', '/usr/bin/svcs']
name = 'SMF'
- __req__ = {'Service':['name', 'status']}
+ __req__ = {'Service': ['name', 'status']}
__ireq__ = {'Service': ['name', 'status', 'FMRI']}
def get_svc_command(self, service, action):
@@ -53,7 +56,8 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
(entry.get("FMRI"), ":".join(files)))
return entry.get('status') == 'on'
else:
- self.logger.debug("No service matching %s" % (entry.get("FMRI")))
+ self.logger.debug("No service matching %s" % \
+ (entry.get("FMRI")))
return entry.get('status') == 'off'
try:
srvdata = self.cmd.run("/usr/bin/svcs -H -o STA %s" % \
@@ -79,7 +83,8 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
os.rename(loc, loc.replace('/S', '/DISABLED.S'))
return True
except OSError:
- self.logger.error("Failed to rename init script %s" % (loc))
+ self.logger.error("Failed to rename init script %s" % \
+ (loc))
return False
else:
cmdrc = self.cmd.run("/usr/sbin/svcadm disable %s" % \
@@ -94,8 +99,8 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
os.rename(loc.replace('/S', '/DISABLED.S'), loc)
cmdrc = 0
except OSError:
- self.logger.debug("Failed to rename %s to %s" \
- % (loc.replace('/S', '/DISABLED.S'), loc))
+ self.logger.debug("Failed to rename %s to %s" % \
+ (loc.replace('/S', '/DISABLED.S'), loc))
cmdrc = 1
else:
srvdata = self.cmd.run("/usr/bin/svcs -H -o STA %s" %
diff --git a/src/lib/Client/Tools/SYSV.py b/src/lib/Client/Tools/SYSV.py
index c2b0eb2cc..b5e1f1c59 100644
--- a/src/lib/Client/Tools/SYSV.py
+++ b/src/lib/Client/Tools/SYSV.py
@@ -2,7 +2,10 @@
"""This provides bcfg2 support for Solaris SYSV packages."""
__revision__ = '$Revision$'
-import tempfile, Bcfg2.Client.Tools, Bcfg2.Client.XML
+import tempfile
+
+import Bcfg2.Client.Tools
+import Bcfg2.Client.XML
noask = '''
@@ -19,6 +22,7 @@ action=nocheck
basedir=default
'''
+
class SYSV(Bcfg2.Client.Tools.PkgTool):
"""Solaris SYSV package support."""
__execs__ = ["/usr/sbin/pkgadd", "/usr/bin/pkginfo"]
diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Client/Tools/YUMng.py
index 077d71508..9cdcdca40 100644
--- a/src/lib/Client/Tools/YUMng.py
+++ b/src/lib/Client/Tools/YUMng.py
@@ -4,7 +4,6 @@ __revision__ = '$Revision$'
import ConfigParser
import copy
import os.path
-import sys
import yum
import yum.packages
import yum.rpmtrans
@@ -21,6 +20,7 @@ try:
except NameError:
from sets import Set as set
+
def build_yname(pkgname, inst):
"""Build yum appropriate package name."""
d = {}
@@ -39,6 +39,7 @@ def build_yname(pkgname, inst):
d['arch'] = inst.get('arch')
return d
+
def short_yname(nevra):
d = nevra.copy()
if 'version' in d:
@@ -49,17 +50,19 @@ def short_yname(nevra):
del d['release']
return d
+
def nevraString(p):
if isinstance(p, yum.packages.PackageObject):
return str(p)
else:
ret = ""
- for i, j in [('epoch','%s:'), ('name','%s'), ('version','-%s'),
- ('release','-%s'), ('arch','.%s')]:
+ for i, j in [('epoch', '%s:'), ('name', '%s'), ('version', '-%s'),
+ ('release', '-%s'), ('arch', '.%s')]:
if i in p:
ret = "%s%s" % (ret, j % p[i])
return ret
+
class Parser(ConfigParser.ConfigParser):
def get(self, section, option, default):
@@ -73,6 +76,7 @@ class Parser(ConfigParser.ConfigParser):
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
return default
+
class RPMDisplay(yum.rpmtrans.RPMBaseCallback):
"""We subclass the default RPM transaction callback so that we
can control Yum's verbosity and pipe it through the right logger."""
@@ -83,11 +87,11 @@ class RPMDisplay(yum.rpmtrans.RPMBaseCallback):
self.state = None
self.package = None
- def event(self, package, action, te_current, te_total,
+ def event(self, package, action, te_current, te_total,
ts_current, ts_total):
- """
+ """
@param package: A yum package object or simple string of a package name
- @param action: A yum.constant transaction set state or in the obscure
+ @param action: A yum.constant transaction set state or in the obscure
rpm repackage case it could be the string 'repackaging'
@param te_current: Current number of bytes processed in the transaction
element being processed
@@ -114,6 +118,7 @@ class RPMDisplay(yum.rpmtrans.RPMBaseCallback):
"""Deal with error reporting."""
self.logger.error(msg)
+
class YumDisplay(yum.callbacks.ProcessTransBaseCallback):
"""Class to handle display of what step we are in the Yum transaction
such as downloading packages, etc."""
@@ -141,10 +146,10 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
def __init__(self, logger, setup, config):
self.yb = yum.YumBase()
Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config)
- self.ignores = [ entry.get('name') for struct in config \
- for entry in struct \
- if entry.tag == 'Path' and \
- entry.get('type') == 'ignore' ]
+ self.ignores = [entry.get('name') for struct in config \
+ for entry in struct \
+ if entry.tag == 'Path' and \
+ entry.get('type') == 'ignore']
self.instance_status = {}
self.extra_instances = []
self.modlists = {}
@@ -152,7 +157,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
self.__important__ = self.__important__ + \
[entry.get('name') for struct in config \
for entry in struct \
- if entry.tag in ['Path', 'ConfigFile'] and \
+ if entry.tag == 'Path' and \
(entry.get('name').startswith('/etc/yum.d') \
or entry.get('name').startswith('/etc/yum.repos.d')) \
or entry.get('name') == '/etc/yum.conf']
@@ -168,7 +173,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
except yum.Errors.YumBaseError, e:
self.logger.error("YUMng error: %s" % e)
raise Bcfg2.Client.Tools.toolInstantiationError
-
+
yup = self.yb.doPackageLists(pkgnarrow='updates')
if hasattr(self.yb.rpmdb, 'pkglist'):
yinst = self.yb.rpmdb.pkglist
@@ -201,7 +206,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
in truth
self.doInstall = CP.get(self.name, "installed_action",
"install").lower() == "install"
- self.doUpgrade = CP.get(self.name,
+ self.doUpgrade = CP.get(self.name,
"version_fail_action", "upgrade").lower() == "upgrade"
self.doReinst = CP.get(self.name, "verify_fail_action",
"reinstall").lower() == "reinstall"
@@ -254,7 +259,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
instance = Bcfg2.Client.XML.SubElement(entry, 'Package')
for attrib in list(entry.attrib.keys()):
instance.attrib[attrib] = entry.attrib[attrib]
- instances = [ instance ]
+ instances = [instance]
return instances
@@ -287,18 +292,20 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
else:
results = verify(po)
self.verifyCache[key] = results
- if not rpmUtils.arch.isMultiLibArch(): return results
+ if not rpmUtils.arch.isMultiLibArch():
+ return results
# Okay deal with a buggy yum multilib and verify
packages = self.yb.rpmdb.searchNevra(name=po.name, epoch=po.epoch,
ver=po.version, rel=po.release) # find all arches of pkg
- if len(packages) == 1:
+ if len(packages) == 1:
return results # No mathcing multilib packages
files = set(po.returnFileEntries()) # Will be the list of common fns
common = {}
for p in packages:
- if p != po: files = files & set(p.returnFileEntries())
+ if p != po:
+ files = files & set(p.returnFileEntries())
for p in packages:
k = (p.name, p.epoch, p.version, p.release, p.arch)
self.logger.debug("Multilib Verify: comparing %s to %s" \
@@ -320,10 +327,11 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
# this fn had verify problems in all but one of the multilib
# packages. That means its correct in the package that's
# "on top." Therefore, this is a fake verify problem.
- if fn in results: del results[fn]
+ if fn in results:
+ del results[fn]
return results
-
+
def RefreshPackages(self):
"""
Creates self.installed{} which is a dict of installed packages.
@@ -407,12 +415,13 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
virtPkg = True
self.logger.info("%s appears to be provided by:" \
% entry.get('name'))
- for p in POs: self.logger.info(" %s" % p)
+ for p in POs:
+ self.logger.info(" %s" % p)
for inst in instances:
nevra = build_yname(entry.get('name'), inst)
snevra = short_yname(nevra)
- if nevra in packageCache:
+ if nevra in packageCache:
continue # Ignore duplicate instances
else:
packageCache.append(nevra)
@@ -436,15 +445,16 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
qtext_versions.append("I(%s)" % nevra)
continue
- if not pkg_checks: continue
+ if not pkg_checks:
+ continue
# Check EVR
if virtPkg:
self.logger.debug(" Not checking version for virtual package")
- _POs = [ po for po in POs ] # Make a copy
+ _POs = [po for po in POs] # Make a copy
elif entry.get('name') == 'gpg-pubkey':
- _POs = [ p for p in POs if p.version == nevra['version'] \
- and p.release == nevra['release'] ]
+ _POs = [p for p in POs if p.version == nevra['version'] \
+ and p.release == nevra['release']]
else:
_POs = self.yb.rpmdb.searchNevra(**snevra)
if len(_POs) == 0:
@@ -452,7 +462,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
stat['version_fail'] = True
# Just chose the first pkg for the error message
self.logger.info(" Wrong version installed. "\
- "Want %s, but have %s" % (nevraString(nevra),
+ "Want %s, but have %s" % (nevraString(nevra),
nevraString(POs[0])))
qtext_versions.append("U(%s)" % str(POs[0]))
continue
@@ -485,7 +495,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
[ig.get('name') for ig in inst.findall('Ignore')] + \
self.ignores
for fn, probs in vResult.items():
- if fn in modlist:
+ if fn in modlist:
self.logger.debug(" %s in modlist, skipping" % fn)
continue
if fn in ignores:
@@ -499,18 +509,19 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
tmp.append((p.type, p.message))
if tmp != []:
stat['verify'][fn] = tmp
-
+
if stat['verify'] != {}:
stat['verify_fail'] = True
package_fail = True
self.logger.debug(" Verify Problems:")
for fn, probs in stat['verify'].items():
self.logger.debug(" %s" % fn)
- for p in probs: self.logger.debug(" %s: %s" % p)
+ for p in probs:
+ self.logger.debug(" %s: %s" % p)
if len(POs) > 0:
# Is this an install only package? We just look at the first one
- provides = set([ p[0] for p in POs[0].provides ] + [ POs[0].name ])
+ provides = set([p[0] for p in POs[0].provides] + [POs[0].name])
install_only = len(set(self.installOnlyPkgs) & provides) > 0
else:
install_only = False
@@ -530,7 +541,6 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
return not package_fail
-
def FindExtraInstances(self, entry, POs):
"""
Check for installed instances that are not in the config.
@@ -538,12 +548,13 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
are no Instances to remove.
"""
- if len(POs) == 0: return None
+ if len(POs) == 0:
+ return None
name = entry.get('name')
- extra_entry = Bcfg2.Client.XML.Element('Package', name=name,
+ extra_entry = Bcfg2.Client.XML.Element('Package', name=name,
type=self.pkgtype)
instances = self._buildInstances(entry)
- _POs = [ p for p in POs ] # Shallow copy
+ _POs = [p for p in POs] # Shallow copy
# Algorythm is sensitive to duplicates, check for them
checked = []
@@ -565,19 +576,19 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
epoch=p.epoch, name=p.name, version=p.version,
release=p.release, arch=p.arch)
- if _POs == []:
+ if _POs == []:
return None
else:
return extra_entry
def FindExtraPackages(self):
"""Find extra packages."""
- packages = [ e.get('name') for e in self.getSupportedEntries() ]
+ packages = [e.get('name') for e in self.getSupportedEntries()]
extras = []
for p in self.installed.keys():
if p not in packages:
- entry = Bcfg2.Client.XML.Element('Package', name=p,
+ entry = Bcfg2.Client.XML.Element('Package', name=p,
type=self.pkgtype)
for i in self.installed[p]:
inst = Bcfg2.Client.XML.SubElement(entry, 'Instance', \
@@ -608,16 +619,16 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
rel = yum.misc.keyIdToRPMVer(gpg['timestamp'])
if not (ver == inst.get('version') and rel == inst.get('release')):
self.logger.info("GPG key file %s does not match gpg-pubkey-%s-%s"\
- % (key_file, inst.get('version'),
+ % (key_file, inst.get('version'),
inst.get('release')))
return False
- if not yum.misc.keyInstalled(ts, gpg['keyid'],
+ if not yum.misc.keyInstalled(ts, gpg['keyid'],
gpg['timestamp']) == 0:
result = ts.pgpImportPubkey(yum.misc.procgpgkey(rawkey))
else:
self.logger.debug("gpg-pubkey-%s-%s already installed"\
- % (inst.get('version'),
+ % (inst.get('version'),
inst.get('release')))
return True
diff --git a/src/lib/Client/Tools/__init__.py b/src/lib/Client/Tools/__init__.py
index 1d89e6d02..8a90e130c 100644
--- a/src/lib/Client/Tools/__init__.py
+++ b/src/lib/Client/Tools/__init__.py
@@ -172,8 +172,7 @@ class Tool:
'''Build a list of potentially modified POSIX paths for this entry'''
return [entry.get('name') for struct in self.config.getchildren() \
for entry in struct.getchildren() \
- if entry.tag in ['ConfigFile', 'SymLink', 'Directory',
- 'Permissions', 'Ignore', 'Path']]
+ if entry.tag in ['Ignore', 'Path']]
def gatherCurrentData(self, entry):
"""Default implementation of the information gathering routines."""
diff --git a/src/lib/Client/Tools/launchd.py b/src/lib/Client/Tools/launchd.py
index a8b785016..db6d94c1b 100644
--- a/src/lib/Client/Tools/launchd.py
+++ b/src/lib/Client/Tools/launchd.py
@@ -2,20 +2,23 @@
__revision__ = '$Revision$'
import os
-import Bcfg2.Client.Tools
import popen2
+import Bcfg2.Client.Tools
+
+
class launchd(Bcfg2.Client.Tools.Tool):
"""Support for Mac OS X launchd services."""
__handles__ = [('Service', 'launchd')]
__execs__ = ['/bin/launchctl', '/usr/bin/defaults']
name = 'launchd'
- __req__ = {'Service':['name', 'status']}
+ __req__ = {'Service': ['name', 'status']}
'''
Currently requires the path to the plist to load/unload,
and Name is acually a reverse-fqdn (or the label).
'''
+
def __init__(self, logger, setup, config):
Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config)
@@ -77,7 +80,6 @@ class launchd(Bcfg2.Client.Tools.Tool):
return 'on'
return False
-
def InstallService(self, entry):
"""Enable or disable launchd item."""
name = entry.get('name')
@@ -95,18 +97,19 @@ class launchd(Bcfg2.Client.Tools.Tool):
"""Remove Extra launchd entries."""
pass
-
-
def FindExtra(self):
"""Find Extra launchd services."""
try:
- allsrv = self.cmd.run("/bin/launchctl list")[1]
+ allsrv = self.cmd.run("/bin/launchctl list")[1]
except IndexError:
allsrv = []
[allsrv.remove(svc) for svc in [entry.get("name") for entry
in self.getSupportedEntries()] if svc in allsrv]
- return [Bcfg2.Client.XML.Element("Service", type='launchd', name=name, status='on') for name in allsrv]
+ return [Bcfg2.Client.XML.Element("Service",
+ type='launchd',
+ name=name,
+ status='on') for name in allsrv]
def BundleUpdated(self, bundle, states):
"""Reload launchd plist."""
@@ -126,4 +129,3 @@ class launchd(Bcfg2.Client.Tools.Tool):
#only if necessary....
self.cmd.run("/bin/launchctl stop %s" % name)
self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry)))
-
diff --git a/src/lib/Server/Admin/Bundle.py b/src/lib/Server/Admin/Bundle.py
index e293c6a4f..41cd5727e 100644
--- a/src/lib/Server/Admin/Bundle.py
+++ b/src/lib/Server/Admin/Bundle.py
@@ -91,7 +91,7 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
tree = lxml.etree.parse(bundle_list[int(lineno)])
#Prints bundle content
#print lxml.etree.tostring(tree)
- names = ['Action', 'ConfigFile', 'Directory', 'Package', 'Permission', 'Service', 'SymLink']
+ names = ['Action', 'Package', 'Path', 'Service']
for name in names:
for node in tree.findall("//" + name):
print "%s:\t%s" % (name, node.attrib["name"])
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index 0458a4ce0..95569e3ac 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -33,8 +33,8 @@ info_regex = re.compile( \
'encoding:(\s)*(?P<encoding>\w+)|' +
'group:(\s)*(?P<group>\S+)|' +
'important:(\s)*(?P<important>\S+)|' +
- 'mtime:(\s)*(?P<mtime>\w+)$' +
- '^owner:(\s)*(?P<owner>\S+)|' +
+ 'mtime:(\s)*(?P<mtime>\w+)|' +
+ 'owner:(\s)*(?P<owner>\S+)|' +
'paranoid:(\s)*(?P<paranoid>\S+)|' +
'perms:(\s)*(?P<perms>\w+)|')
diff --git a/src/lib/Server/Plugins/Guppy.py b/src/lib/Server/Plugins/Guppy.py
new file mode 100644
index 000000000..b217378d6
--- /dev/null
+++ b/src/lib/Server/Plugins/Guppy.py
@@ -0,0 +1,63 @@
+"""
+This plugin is used to trace memory leaks within the bcfg2-server
+process using Guppy. By default the remote debugger is started
+when this plugin is enabled. The debugger can be shutoff in a running
+process using "bcfg2-admin xcmd Guppy.Disable" and reenabled using
+"bcfg2-admin xcmd Guppy.Enable".
+
+To attach the console run:
+
+python -c "from guppy import hpy;hpy().monitor()"
+
+For example:
+
+# python -c "from guppy import hpy;hpy().monitor()"
+<Monitor>
+*** Connection 1 opened ***
+<Monitor> lc
+CID PID ARGV
+ 1 25063 ['/usr/sbin/bcfg2-server', '-D', '/var/run/bcfg2-server.pid']
+<Monitor> sc 1
+Remote connection 1. To return to Monitor, type <Ctrl-C> or .<RETURN>
+<Annex> int
+Remote interactive console. To return to Annex, type '-'.
+>>> hp.heap()
+...
+
+
+"""
+import re
+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
+ __rmi__ = Bcfg2.Server.Plugin.Plugin.__rmi__ + ['Enable','Disable']
+
+ def __init__(self, core, datastore):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+
+ self.Enable()
+
+ def Enable(self):
+ """Enable remote debugging"""
+ try:
+ from guppy.heapy import Remote
+ Remote.on()
+ except:
+ self.logger.error("Failed to create Heapy context")
+ raise Bcfg2.Server.Plugin.PluginInitError
+
+ def Disable(self):
+ """Disable remote debugging"""
+ try:
+ from guppy.heapy import Remote
+ Remote.off()
+ except:
+ self.logger.error("Failed to disable Heapy")
+ raise Bcfg2.Server.Plugin.PluginInitError
+
diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Server/Plugins/Metadata.py
index 3161a69e3..81fd3e173 100644
--- a/src/lib/Server/Plugins/Metadata.py
+++ b/src/lib/Server/Plugins/Metadata.py
@@ -39,14 +39,21 @@ class ClientMetadata(object):
"""Test to see if client is a member of group."""
return group in self.groups
+ def group_in_category(self, category):
+ for grp in self.query.all_groups_in_category(category):
+ if grp in self.groups:
+ return grp
+ return ''
+
class MetadataQuery(object):
- def __init__(self, by_name, get_clients, by_groups, by_profiles, all_groups):
+ def __init__(self, by_name, get_clients, by_groups, by_profiles, all_groups, all_groups_in_category):
# resolver is set later
self.by_name = by_name
self.names_by_groups = by_groups
self.names_by_profiles = by_profiles
self.all_clients = get_clients
self.all_groups = all_groups
+ self.all_groups_in_category = all_groups_in_category
def by_groups(self, groups):
return [self.by_name(name) for name in self.names_by_groups(groups)]
@@ -105,7 +112,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
lambda:self.clients.keys(),
self.get_client_names_by_groups,
self.get_client_names_by_profiles,
- self.get_all_group_names)
+ self.get_all_group_names,
+ self.get_all_groups_in_category)
@classmethod
def init_repo(cls, repo, groups, os_selection, clients):
@@ -592,6 +600,12 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
[all_groups.update(g[1]) for g in self.groups.values()]
return all_groups
+ def get_all_groups_in_category(self, category):
+ all_groups = set()
+ [all_groups.add(g) for g in self.categories \
+ if self.categories[g] == category]
+ return all_groups
+
def get_client_names_by_profiles(self, profiles):
return [client for client, profile in self.clients.iteritems() \
if profile in profiles]
diff --git a/src/lib/Server/Plugins/POSIXCompat.py b/src/lib/Server/Plugins/POSIXCompat.py
deleted file mode 100644
index fc16e4b48..000000000
--- a/src/lib/Server/Plugins/POSIXCompat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""This plugin provides a compatibility layer which turns new-style
-POSIX entries into old-style entries.
-"""
-
-__revision__ = '$Revision$'
-
-import Bcfg2.Server.Plugin
-
-COMPAT_DICT = {'file': 'ConfigFile',
- 'directory': 'Directory',
- 'permissions': 'Permissions',
- 'symlink': 'SymLink'}
-
-
-class POSIXCompat(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.GoalValidator):
- """POSIXCompat is a goal validator plugin for POSIX entries."""
- name = 'POSIXCompat'
- __version__ = '$Id$'
- __author__ = 'bcfg-dev@mcs.anl.gov'
-
- def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.GoalValidator.__init__(self)
-
- def validate_goals(self, metadata, goals):
- """Verify that we are generating correct old
- Cfg/Directory/Symlink entries.
- """
- for goal in goals:
- for entry in goal.getchildren():
- if entry.tag == 'Path' and \
- entry.get('type') not in ['nonexistent', 'device']:
- # Use new entry 'type' attribute to map old entry tags
- oldentry = entry
- entry.tag = COMPAT_DICT[entry.get('type')]
- del entry.attrib['type']
- goal.replace(oldentry, entry)
diff --git a/src/lib/Server/Plugins/__init__.py b/src/lib/Server/Plugins/__init__.py
index 758f1219d..c69c37452 100644
--- a/src/lib/Server/Plugins/__init__.py
+++ b/src/lib/Server/Plugins/__init__.py
@@ -22,7 +22,6 @@ __all__ = [
'Properties',
'Probes',
'Pkgmgr',
- 'POSIXCompat',
'Rules',
'SSHbase',
'Snapshots',
diff --git a/src/lib/Server/Reports/reports/templates/base.html b/src/lib/Server/Reports/reports/templates/base.html
index 7a36c9893..9bd9da218 100644
--- a/src/lib/Server/Reports/reports/templates/base.html
+++ b/src/lib/Server/Reports/reports/templates/base.html
@@ -1,3 +1,5 @@
+{% load bcfg2_tags %}
+
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
@@ -10,19 +12,19 @@
<meta name="robots" content="noindex, nofollow" />
<meta http-equiv="cache-control" content="no-cache" />
-<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}/bcfg2_base.css" media="all" />
-<script type="text/javascript" src="{{ MEDIA_URL }}/bcfg2.js"></script>
-<script type="text/javascript" src="{{ MEDIA_URL }}/date.js"></script>
-<script type="text/javascript" src="{{ MEDIA_URL }}/AnchorPosition.js"></script>
-<script type="text/javascript" src="{{ MEDIA_URL }}/CalendarPopup.js"></script>
-<script type="text/javascript" src="{{ MEDIA_URL }}/PopupWindow.js"></script>
+<link rel="stylesheet" type="text/css" href="{% to_media_url bcfg2_base.css %}" media="all" />
+<script type="text/javascript" src="{% to_media_url bcfg2.js %}"></script>
+<script type="text/javascript" src="{% to_media_url date.js %}"></script>
+<script type="text/javascript" src="{% to_media_url AnchorPosition.js %}"></script>
+<script type="text/javascript" src="{% to_media_url CalendarPopup.js %}"></script>
+<script type="text/javascript" src="{% to_media_url PopupWindow.js %}"></script>
{% block extra_header_info %}{% endblock %}
</head>
<body onload="{% block body_onload %}{% endblock %}">
<div id="header">
- <a href="http://bcfg2.org"><img src='{{ MEDIA_URL }}/bcfg2_logo.png'
+ <a href="http://bcfg2.org"><img src='{% to_media_url bcfg2_logo.png %}'
height='115' width='300' alt='Bcfg2' style='float:left; height: 115px' /></a>
</div>
diff --git a/src/lib/Server/Reports/reports/templates/config_items/item.html b/src/lib/Server/Reports/reports/templates/config_items/item.html
index 41474922b..58aed1684 100644
--- a/src/lib/Server/Reports/reports/templates/config_items/item.html
+++ b/src/lib/Server/Reports/reports/templates/config_items/item.html
@@ -88,7 +88,7 @@ div.entry_list h3 {
<div class='entry_list'>
<div class='entry_list_head'>
- <h3>Occurances on {{ timestamp|date:"Y-m-d" }}</h3>
+ <h3>Occurences on {{ timestamp|date:"Y-m-d" }}</h3>
</div>
{% if associated_list %}
<table class="entry_list" cellpadding="3">
diff --git a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py b/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
index 2c27aab04..7fffe289d 100644
--- a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
+++ b/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
@@ -18,7 +18,7 @@ def page_navigator(context):
"""
fragment = dict()
try:
- path = context['request'].path
+ path = context['request'].META['PATH_INFO']
total_pages = int(context['total_pages'])
records_per_page = int(context['records_per_page'])
except KeyError, e:
@@ -96,7 +96,7 @@ def page_navigator(context):
@register.inclusion_tag('widgets/filter_bar.html', takes_context=True)
def filter_navigator(context):
try:
- path = context['request'].path
+ path = context['request'].META['PATH_INFO']
view, args, kwargs = resolve(path)
# Strip any page limits and numbers
@@ -179,7 +179,7 @@ class AddUrlFilter(template.Node):
def render(self, context):
link = '#'
try:
- path = context['request'].path
+ path = context['request'].META['PATH_INFO']
view, args, kwargs = resolve(path)
filter_value = self.filter_value.resolve(context, True)
if filter_value:
@@ -237,3 +237,38 @@ def sortwell(value):
configItems.sort(lambda x,y: cmp(x.entry.kind, y.entry.kind))
return configItems
+class MediaTag(template.Node):
+ def __init__(self, filter_value):
+ self.filter_value = filter_value
+
+ def render(self, context):
+ base = context['MEDIA_URL']
+ try:
+ request = context['request']
+ try:
+ base = request.environ['bcfg2.media_url']
+ except:
+ if request.path != request.META['PATH_INFO']:
+ offset = request.path.find(request.META['PATH_INFO'])
+ if offset > 0:
+ base = "%s/%s" % (request.path[:offset], \
+ context['MEDIA_URL'].strip('/'))
+ except:
+ pass
+ return "%s/%s" % (base, self.filter_value)
+
+@register.tag
+def to_media_url(parser, token):
+ """
+ Return a url relative to the media_url.
+
+ {% to_media_url /bcfg2.css %}
+ """
+ try:
+ tag_name, filter_value = token.split_contents()
+ filter_value = parser.compile_filter(filter_value)
+ except ValueError:
+ raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
+
+ return MediaTag(filter_value)
+
diff --git a/src/lib/Server/Reports/reports/views.py b/src/lib/Server/Reports/reports/views.py
index a061a3964..00d35c092 100644
--- a/src/lib/Server/Reports/reports/views.py
+++ b/src/lib/Server/Reports/reports/views.py
@@ -46,7 +46,7 @@ def timeview(fn):
if cal_date.find(' ') > -1:
fmt += " %H:%M"
timestamp = datetime(*strptime(cal_date, fmt)[0:6])
- view, args, kw = resolve(request.path)
+ view, args, kw = resolve(request.META['PATH_INFO'])
kw['year'] = "%0.4d" % timestamp.year
kw['month'] = "%02.d" % timestamp.month
kw['day'] = "%02.d" % timestamp.day
@@ -367,7 +367,7 @@ def prepare_paginated_list(request, context, paged_list, page=1, max_results=25)
if page > total_pages:
# If we passed beyond the end send back
try:
- view, args, kwargs = resolve(request.path)
+ view, args, kwargs = resolve(request.META['PATH_INFO'])
kwargs['page_number'] = total_pages
raise PaginationError, HttpResponseRedirect( reverse(view, kwargs=kwargs) )
except (Resolver404, NoReverseMatch, ValueError):
diff --git a/src/lib/Server/Reports/urls.py b/src/lib/Server/Reports/urls.py
index 5d298c974..d7ff1eee5 100644
--- a/src/lib/Server/Reports/urls.py
+++ b/src/lib/Server/Reports/urls.py
@@ -3,24 +3,12 @@ from django.http import HttpResponsePermanentRedirect
handler500 = 'Bcfg2.Server.Reports.reports.views.server_error'
-from ConfigParser import ConfigParser, NoSectionError, NoOptionError
-c = ConfigParser()
-c.read(['/etc/bcfg2.conf', '/etc/bcfg2-web.conf'])
-
-# web_prefix should have a trailing slash, but no leading slash
-# e.g. web_prefix = bcfg2/
-# web_prefix_root is a workaround for the index
-if c.has_option('statistics', 'web_prefix'):
- web_prefix = c.get('statistics', 'web_prefix').lstrip('/')
-else:
- web_prefix = ''
-
urlpatterns = patterns('',
- (r'^%s' % web_prefix, include('Bcfg2.Server.Reports.reports.urls'))
+ (r'^', include('Bcfg2.Server.Reports.reports.urls'))
)
-urlpatterns += patterns("django.views",
- url(r"media/(?P<path>.*)$", "static.serve", {
- "document_root": '/Users/tlaszlo/svn/bcfg2/reports/site_media/',
- })
-)
+#urlpatterns += patterns("django.views",
+# url(r"media/(?P<path>.*)$", "static.serve", {
+# "document_root": '/Users/tlaszlo/svn/bcfg2/reports/site_media/',
+# })
+#)
diff --git a/src/sbin/bcfg2-repo-validate b/src/sbin/bcfg2-repo-validate
index 1889c9892..3d5efb093 100755
--- a/src/sbin/bcfg2-repo-validate
+++ b/src/sbin/bcfg2-repo-validate
@@ -88,14 +88,14 @@ if __name__ == '__main__':
# (as defined in doc/server/configurationentries)
# TODO: See if it is possible to do this in the schema instead
required_configuration_attrs = {
- 'device':['name', 'owner', 'group', 'dev_type'],
- 'directory':['name', 'owner', 'group', 'perms'],
- 'file':['name', 'owner', 'group', 'perms'],
- 'hardlink':['name', 'to'],
- 'symlink':['name', 'to'],
- 'ignore':['name'],
- 'nonexist':['name'],
- 'permissions':['name', 'owner', 'group', 'perms']}
+ 'device': ['name', 'owner', 'group', 'dev_type'],
+ 'directory': ['name', 'owner', 'group', 'perms'],
+ 'file': ['name', 'owner', 'group', 'perms'],
+ 'hardlink': ['name', 'to'],
+ 'symlink': ['name', 'to'],
+ 'ignore': ['name'],
+ 'nonexist': ['name'],
+ 'permissions': ['name', 'owner', 'group', 'perms']}
for rfile in rules_list:
try:
xdata = lxml.etree.parse(rfile)
@@ -110,6 +110,11 @@ if __name__ == '__main__':
+ ['type'])
except KeyError:
continue
+ if 'dev_type' in required_attrs:
+ dev_type = posixpath.get('dev_type')
+ if dev_type in ['block', 'char']:
+ # check if major/minor are specified
+ required_attrs |= set(['major', 'minor'])
if pathset.issuperset(required_attrs):
continue
else:
@@ -146,16 +151,16 @@ if __name__ == '__main__':
else:
pset.add(ptuple)
- filesets = {'metadata':(metadata_list, "%s/metadata.xsd"),
- 'clients':(clients_list, "%s/clients.xsd"),
- 'info':(info_list, "%s/info.xsd"),
- 'bundle':(bundle_list, "%s/bundle.xsd"),
- 'pkglist':(pkg_list, "%s/pkglist.xsd"),
- 'base':(base_list, "%s/base.xsd"),
- 'rules':(rules_list, "%s/rules.xsd"),
- 'imageinfo':(imageinfo_list, "%s/report-configuration.xsd"),
- 'services':(services_list, "%s/services.xsd"),
- 'deps':(deps_list, "%s/deps.xsd"),
+ filesets = {'metadata': (metadata_list, "%s/metadata.xsd"),
+ 'clients': (clients_list, "%s/clients.xsd"),
+ 'info': (info_list, "%s/info.xsd"),
+ 'bundle': (bundle_list, "%s/bundle.xsd"),
+ 'pkglist': (pkg_list, "%s/pkglist.xsd"),
+ 'base': (base_list, "%s/base.xsd"),
+ 'rules': (rules_list, "%s/rules.xsd"),
+ 'imageinfo': (imageinfo_list, "%s/report-configuration.xsd"),
+ 'services': (services_list, "%s/services.xsd"),
+ 'deps': (deps_list, "%s/deps.xsd"),
'decisions': (dec_list, "%s/decisions.xsd"),
'packages': (pkgcfg_list, "%s/packages.xsd"),
'grouppatterns': (gp_list, "%s/grouppatterns.xsd"),