summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xdebian/bcfg2-server.init3
-rwxr-xr-xdebian/bcfg2.cron.daily2
-rwxr-xr-xdebian/bcfg2.cron.hourly2
-rw-r--r--doc/help/troubleshooting.txt2
-rw-r--r--doc/installation/distributions.txt14
-rw-r--r--doc/reports/dynamic.txt2
-rw-r--r--misc/bcfg2.spec7
-rw-r--r--schemas/servicetype.xsd15
-rw-r--r--schemas/types.xsd7
-rw-r--r--solaris-ips/MANIFEST.bcfg2-server.header5
-rw-r--r--solaris-ips/MANIFEST.bcfg2.header6
-rw-r--r--solaris-ips/Makefile20
-rw-r--r--solaris-ips/README22
-rw-r--r--solaris-ips/gen-manifests.sh15
-rw-r--r--solaris-ips/pkginfo.bcfg210
-rw-r--r--solaris-ips/pkginfo.bcfg2-server10
-rw-r--r--src/lib/Bcfg2/Client/Frame.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/Chkconfig.py100
-rw-r--r--src/lib/Bcfg2/Client/Tools/DebInit.py103
-rw-r--r--src/lib/Bcfg2/Client/Tools/RcUpdate.py108
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py16
-rw-r--r--src/lib/Bcfg2/Reporting/models.py32
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base.html29
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/detail.html32
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html6
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/index.html8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/manage.html8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/common.html5
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html10
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/item.html20
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/listing.html8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/displays/summary.html6
-rw-r--r--src/lib/Bcfg2/Reporting/templates/displays/timing.html14
-rw-r--r--src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py36
-rw-r--r--src/lib/Bcfg2/Reporting/templatetags/syntax_coloring.py16
-rw-r--r--src/lib/Bcfg2/Reporting/views.py2
-rw-r--r--src/lib/Bcfg2/Server/Admin/Client.py12
-rw-r--r--src/lib/Bcfg2/Server/Admin/Compare.py3
-rw-r--r--src/lib/Bcfg2/Server/Admin/Minestruct.py15
-rw-r--r--src/lib/Bcfg2/Server/Admin/Pull.py5
-rw-r--r--src/lib/Bcfg2/Server/Admin/Reports.py23
-rw-r--r--src/lib/Bcfg2/Server/Admin/Syncdb.py9
-rw-r--r--src/lib/Bcfg2/Server/Admin/Xcmd.py17
-rw-r--r--src/lib/Bcfg2/Server/Core.py3
-rwxr-xr-xsrc/lib/Bcfg2/Server/Lint/Genshi.py6
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py83
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py32
-rw-r--r--src/lib/Bcfg2/settings.py2
-rwxr-xr-xsrc/sbin/bcfg2-admin4
-rwxr-xr-xsrc/sbin/bcfg2-info12
-rwxr-xr-xsrc/sbin/bcfg2-lint7
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py37
-rwxr-xr-xtools/export.py42
53 files changed, 677 insertions, 340 deletions
diff --git a/debian/bcfg2-server.init b/debian/bcfg2-server.init
index 04774c063..b1c3aba21 100755
--- a/debian/bcfg2-server.init
+++ b/debian/bcfg2-server.init
@@ -17,7 +17,8 @@
### END INIT INFO
# Include lsb functions
-. /lib/lsb/init-functions
+test -f "/lib/lsb/init-functions" && . /lib/lsb/init-functions # debian
+test -f "/etc/init.d/functions" && . /etc/init.d/functions # redhat
# Commonly used stuff
DAEMON=/usr/sbin/bcfg2-server
diff --git a/debian/bcfg2.cron.daily b/debian/bcfg2.cron.daily
index f2d1efb9f..b872887cb 100755
--- a/debian/bcfg2.cron.daily
+++ b/debian/bcfg2.cron.daily
@@ -10,4 +10,4 @@ else
echo "No bcfg2-cron command found"
exit 1
fi
-$BCFG2CRON --daily 2>&1 | logger -t bcfg2-cron -p daemon.info
+$BCFG2CRON --daily 2>&1 | logger -t bcfg2-cron -p daemon.info -i
diff --git a/debian/bcfg2.cron.hourly b/debian/bcfg2.cron.hourly
index 73aae7606..9f666e083 100755
--- a/debian/bcfg2.cron.hourly
+++ b/debian/bcfg2.cron.hourly
@@ -10,4 +10,4 @@ else
echo "No bcfg2-cron command found"
exit 1
fi
-$BCFG2CRON --hourly 2>&1 | logger -t bcfg2-cron -p daemon.info
+$BCFG2CRON --hourly 2>&1 | logger -t bcfg2-cron -p daemon.info -i
diff --git a/doc/help/troubleshooting.txt b/doc/help/troubleshooting.txt
index aac831ae0..72fec4c63 100644
--- a/doc/help/troubleshooting.txt
+++ b/doc/help/troubleshooting.txt
@@ -54,7 +54,7 @@ the debug level individually on a given plugin, e.g.::
bcfg2-admin xcmd Probes.toggle_debug
Finally, the File Activity Monitor has its own analogue to these two
-methods, for setting the debug level of the FAM:
+methods, for setting the debug level of the FAM::
bcfg2-admin xcmd Inotify.toggle_debug
bcfg2-admin xcmd Inotify.set_debug false
diff --git a/doc/installation/distributions.txt b/doc/installation/distributions.txt
index 3dcfd7721..9db111682 100644
--- a/doc/installation/distributions.txt
+++ b/doc/installation/distributions.txt
@@ -66,19 +66,7 @@ This way is not recommended on production systems. Only for testing.
Gentoo
======
-Early in July 2008, Bcfg2 was added to the Gentoo portage tree. So far
-it's still keyworded for all architectures, but we are actively working
-to get it marked as stable.
-
-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-python/lxml``
-
-Clients will need at least:
-
-* ``app-portage/gentoolkit``
+Bcfg2 can be installed via portage.
OS X
====
diff --git a/doc/reports/dynamic.txt b/doc/reports/dynamic.txt
index 9de3f868f..6b8a1f467 100644
--- a/doc/reports/dynamic.txt
+++ b/doc/reports/dynamic.txt
@@ -39,7 +39,7 @@ Prerequisites
* sqlite3
* pysqlite2 (if using python 2.4)
-* `Django <http://www.djangoproject.com>`_ >= 1.2
+* `Django <http://www.djangoproject.com>`_ >= 1.3
* mod-wsgi
.. warning::
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index f7289f1dd..10d913d7c 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -99,8 +99,6 @@ Requires: bcfg2 = %{version}
Requires: python-ssl
%endif
Requires: python-lxml >= 1.2.1
-%if "%{_vendor}" == "redhat"
-%endif
%if 0%{?suse_version}
Requires: python-pyinotify
Requires: python-python-daemon
@@ -151,7 +149,7 @@ Group: System Tools
Requires: bcfg2 = %{version}
Requires: bcfg2-server = %{version}
-# cherrypy 3.2.3 actually doesn't exist yet, but 3.2.2 has bugs that
+# cherrypy 3.3 actually doesn't exist yet, but 3.2 has bugs that
# prevent it from working:
# https://bitbucket.org/cherrypy/cherrypy/issue/1154/assertionerror-in-recv-when-ssl-is-enabled
Requires: python-cherrypy > 3.3
@@ -297,6 +295,8 @@ This package includes the Bcfg2 reports web frontend.
%{?pythonpath: export PYTHONPATH="%{pythonpath}"}
%{__python}%{pythonversion} setup.py build_sphinx
+sed -i "s/apache2/httpd/g" misc/apache/bcfg2.conf
+
%install
rm -rf %{buildroot}
%{__python}%{pythonversion} setup.py install --root=%{buildroot} --record=INSTALLED_FILES --prefix=/usr
@@ -333,7 +333,6 @@ cp -r tools/* %{buildroot}%{_defaultdocdir}/bcfg2-server-%{version}
cp -r build/sphinx/html/* %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}
%{__install} -d %{buildroot}%{apache_conf}/conf.d
-sed -i "s/apache2/httpd/g" misc/apache/bcfg2.conf
%{__install} -m 644 misc/apache/bcfg2.conf %{buildroot}%{apache_conf}/conf.d/wsgi_bcfg2.conf
%{__mkdir_p} %{buildroot}%{_localstatedir}/cache/%{name}
diff --git a/schemas/servicetype.xsd b/schemas/servicetype.xsd
index 4d5ac7c31..4c7e1b803 100644
--- a/schemas/servicetype.xsd
+++ b/schemas/servicetype.xsd
@@ -34,12 +34,21 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="bootstatus" type="BootStatusEnum" default="off">
+ <xsd:annotation>
+ <xsd:documentation>
+ Whether the service should start at boot. The default value
+ corresponds to the value of the status attribute.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="status" type="StatusEnum" default="off">
<xsd:annotation>
<xsd:documentation>
- Whether the service should start at boot. If this is set to
- "ignore", then the boot-time status of the service will not
- be checked.
+ Whether the service should be on or off when the bcfg2 client
+ is run. This attribute may have different behavior depending
+ on the characteristics of the client tool. If set to "ignore",
+ then the status of the service will not be checked.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
diff --git a/schemas/types.xsd b/schemas/types.xsd
index 05bf674ad..5f6328a62 100644
--- a/schemas/types.xsd
+++ b/schemas/types.xsd
@@ -53,6 +53,13 @@
</xsd:restriction>
</xsd:simpleType>
+ <xsd:simpleType name='BootStatusEnum'>
+ <xsd:restriction base='xsd:string'>
+ <xsd:enumeration value='on'/>
+ <xsd:enumeration value='off'/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
<xsd:simpleType name='StatusEnum'>
<xsd:restriction base='xsd:string'>
<xsd:enumeration value='on'/>
diff --git a/solaris-ips/MANIFEST.bcfg2-server.header b/solaris-ips/MANIFEST.bcfg2-server.header
new file mode 100644
index 000000000..efa11181f
--- /dev/null
+++ b/solaris-ips/MANIFEST.bcfg2-server.header
@@ -0,0 +1,5 @@
+license ../../LICENSE license=simplified_bsd
+set name=description value="Configuration management server"
+set name=pkg.summary value="Configuration management server"
+set name=pkg.fmri value="pkg://bcfg2/bcfg2-server@1.3.1"
+
diff --git a/solaris-ips/MANIFEST.bcfg2.header b/solaris-ips/MANIFEST.bcfg2.header
new file mode 100644
index 000000000..8358aafca
--- /dev/null
+++ b/solaris-ips/MANIFEST.bcfg2.header
@@ -0,0 +1,6 @@
+license ../../LICENSE license=simplified_bsd
+set name=description value="Configuration management client"
+set name=pkg.summary value="Configuration management client"
+set name=pkg.fmri value="pkg://bcfg2/bcfg2@1.3.1"
+
+file usr/bin/bcfg2 group=bin mode=0755 owner=root path=usr/bin/bcfg2
diff --git a/solaris-ips/Makefile b/solaris-ips/Makefile
new file mode 100644
index 000000000..343150dc5
--- /dev/null
+++ b/solaris-ips/Makefile
@@ -0,0 +1,20 @@
+#!/usr/bin/gmake
+
+VERS=1.2.4-1
+PYVERSION := $(shell python -c "import sys; print sys.version[0:3]")
+
+default: clean package
+
+package:
+ -mkdir tmp tmp/bcfg2-server tmp/bcfg2
+ -mkdir -p build/lib/$(PYVERSION)/site-packages
+ -cd ../ && PYTHONPATH=$(PYTHONPATH):$(PWD)/build/lib/python2.6/site-packages/ python setup.py install --single-version-externally-managed --record=/dev/null --prefix=$(PWD)/build/usr
+ #setuptools appears to use a restictive umask
+ -chmod -R o+r build/
+ -chmod +x build/usr/bin/bcfg2
+ -sh ./gen-manifests.sh
+
+clean:
+ -rm -rf tmp build
+ -rm -rf MANIFEST.bcfg2
+ -rm -rf MANIFEST.bcfg2-server
diff --git a/solaris-ips/README b/solaris-ips/README
new file mode 100644
index 000000000..24021b992
--- /dev/null
+++ b/solaris-ips/README
@@ -0,0 +1,22 @@
+BUILDING
+--------
+
+Dependancies:
+ gmake
+
+Usage:
+ gmake
+
+
+PUBLISHING
+----------
+
+Modify MANIFEST.bcfg2 and MANIFEST.bcfg2-server to set your publisher name in the fmri, e.g. Change
+ set name=pkg.fmri value="pkg://bcfg2/bcfg2@1.2.4"
+to
+ set name=pkg.fmri value="pkg://example.com/bcfg2@1.2.4"
+
+
+Then run the pkgsend publish, i.e.
+ pkgsend publish -s http://example.com/path/to/repo -d build MANIFEST.bcfg2
+ pkgsend publish -s http://example.com/path/to/repo -d build MANIFEST.bcfg2-server
diff --git a/solaris-ips/gen-manifests.sh b/solaris-ips/gen-manifests.sh
new file mode 100644
index 000000000..3b4cd30df
--- /dev/null
+++ b/solaris-ips/gen-manifests.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/sh
+
+#bcfg2
+cat MANIFEST.bcfg2.header > MANIFEST.bcfg2
+pkgsend generate build | grep man[15] >> MANIFEST.bcfg2
+pkgsend generate build | grep Bcfg2/[^/]*.py$ >> MANIFEST.bcfg2
+pkgsend generate build | grep Bcfg2/Client/.*.py$ >> MANIFEST.bcfg2
+
+#bcfg2-server
+cat MANIFEST.bcfg2-server.header > MANIFEST.bcfg2-server
+pkgsend generate build | grep man[8] >> MANIFEST.bcfg2-server
+pkgsend generate build | grep share/bcfg2 >> MANIFEST.bcfg2-server
+pkgsend generate build | grep bin/bcfg2- >> MANIFEST.bcfg2-server
+pkgsend generate build | grep Bcfg2/Server/.*.py$ >> MANIFEST.bcfg2-server
+
diff --git a/solaris-ips/pkginfo.bcfg2 b/solaris-ips/pkginfo.bcfg2
new file mode 100644
index 000000000..90c628c53
--- /dev/null
+++ b/solaris-ips/pkginfo.bcfg2
@@ -0,0 +1,10 @@
+PKG="SCbcfg2"
+NAME="bcfg2"
+ARCH="sparc"
+VERSION="1.2.4"
+CATEGORY="application"
+VENDOR="Argonne National Labratory"
+EMAIL="bcfg-dev@mcs.anl.gov"
+PSTAMP="Bcfg2 Developers"
+BASEDIR="/opt/csw"
+CLASSES="none"
diff --git a/solaris-ips/pkginfo.bcfg2-server b/solaris-ips/pkginfo.bcfg2-server
new file mode 100644
index 000000000..0e865522c
--- /dev/null
+++ b/solaris-ips/pkginfo.bcfg2-server
@@ -0,0 +1,10 @@
+PKG="SCbcfg2-server"
+NAME="bcfg2-server"
+ARCH="sparc"
+VERSION="1.2.4"
+CATEGORY="application"
+VENDOR="Argonne National Labratory"
+EMAIL="bcfg-dev@mcs.anl.gov"
+PSTAMP="Bcfg2 Developers"
+BASEDIR="/usr"
+CLASSES="none"
diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py
index d30708e83..6bef77081 100644
--- a/src/lib/Bcfg2/Client/Frame.py
+++ b/src/lib/Bcfg2/Client/Frame.py
@@ -97,8 +97,8 @@ class Frame(object):
self.logger.warning(deprecated)
experimental = [tool.name for tool in self.tools if tool.experimental]
if experimental:
- self.logger.warning("Loaded experimental tool drivers:")
- self.logger.warning(experimental)
+ self.logger.info("Loaded experimental tool drivers:")
+ self.logger.info(experimental)
# find entries not handled by any tools
self.unhandled = [entry for struct in config
diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
index 1fce5515b..256c28255 100644
--- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py
+++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
@@ -19,26 +19,22 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
def get_svc_command(self, service, action):
return "/sbin/service %s %s" % (service.get('name'), action)
- def VerifyService(self, entry, _):
- """Verify Service status for entry."""
- entry.set('target_status', entry.get('status'))
- if entry.get('status') == 'ignore':
- return True
-
+ def verify_bootstatus(self, entry, bootstatus):
+ """Verify bootstatus for entry."""
rv = self.cmd.run("/sbin/chkconfig --list %s " % entry.get('name'))
if rv.success:
srvdata = rv.stdout.splitlines()[0].split()
else:
# service not installed
- entry.set('current_status', 'off')
+ entry.set('current_bootstatus', 'service not installed')
return False
if len(srvdata) == 2:
# This is an xinetd service
- if entry.get('status') == srvdata[1]:
+ if bootstatus == srvdata[1]:
return True
else:
- entry.set('current_status', srvdata[1])
+ entry.set('current_bootstatus', srvdata[1])
return False
try:
@@ -47,40 +43,74 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
except IndexError:
onlevels = []
- pstatus = self.check_service(entry)
- if entry.get('status') == 'on':
- status = (len(onlevels) > 0 and pstatus)
+ if bootstatus == 'on':
+ current_bootstatus = (len(onlevels) > 0)
else:
- status = (len(onlevels) == 0 and not pstatus)
+ current_bootstatus = (len(onlevels) == 0)
+ return current_bootstatus
+
+ def VerifyService(self, entry, _):
+ """Verify Service status for entry."""
+ entry.set('target_status', entry.get('status')) # for reporting
+ bootstatus = self.get_bootstatus(entry)
+ if bootstatus is None:
+ return True
+ current_bootstatus = self.verify_bootstatus(entry, bootstatus)
- if not status:
+ if entry.get('status') == 'ignore':
+ # 'ignore' should verify
+ current_svcstatus = True
+ else:
+ svcstatus = self.check_service(entry)
if entry.get('status') == 'on':
- entry.set('current_status', 'off')
- else:
- entry.set('current_status', 'on')
- return status
+ if svcstatus:
+ current_svcstatus = True
+ else:
+ current_svcstatus = False
+ elif entry.get('status') == 'off':
+ if svcstatus:
+ current_svcstatus = False
+ else:
+ current_svcstatus = True
+
+ if svcstatus:
+ entry.set('current_status', 'on')
+ else:
+ entry.set('current_status', 'off')
+
+ return current_bootstatus and current_svcstatus
def InstallService(self, entry):
"""Install Service entry."""
- rcmd = "/sbin/chkconfig %s %s"
- self.cmd.run("/sbin/chkconfig --add %s" % (entry.attrib['name']))
+ self.cmd.run("/sbin/chkconfig --add %s" % (entry.get('name')))
self.logger.info("Installing Service %s" % (entry.get('name')))
- rv = True
- if (entry.get('status') == 'off' or
- self.setup["servicemode"] == "build"):
- rv &= self.cmd.run((rcmd + " --level 0123456") %
- (entry.get('name'),
- entry.get('status'))).success
- if entry.get("current_status") == "on" and \
- self.setup["servicemode"] != "disabled":
- rv &= self.stop_service(entry).success
+ bootstatus = entry.get('bootstatus')
+ if bootstatus is not None:
+ if bootstatus == 'on':
+ # make sure service is enabled on boot
+ bootcmd = '/sbin/chkconfig %s %s --level 0123456' % \
+ (entry.get('name'), entry.get('bootstatus'))
+ elif bootstatus == 'off':
+ # make sure service is disabled on boot
+ bootcmd = '/sbin/chkconfig %s %s' % (entry.get('name'),
+ entry.get('bootstatus'))
+ bootcmdrv = self.cmd.run(bootcmd).success
+ if self.setup['servicemode'] == 'disabled':
+ # 'disabled' means we don't attempt to modify running svcs
+ return bootcmdrv
+ buildmode = self.setup['servicemode'] == 'build'
+ if (entry.get('status') == 'on' and not buildmode) and \
+ entry.get('current_status') == 'off':
+ svccmdrv = self.start_service(entry)
+ elif (entry.get('status') == 'off' or buildmode) and \
+ entry.get('current_status') == 'on':
+ svccmdrv = self.stop_service(entry)
+ else:
+ svccmdrv = True # ignore status attribute
+ return bootcmdrv and svccmdrv
else:
- rv &= self.cmd.run(rcmd % (entry.get('name'),
- entry.get('status'))).success
- if entry.get("current_status") == "off" and \
- self.setup["servicemode"] != "disabled":
- rv &= self.start_service(entry).success
- return rv
+ # when bootstatus is 'None', status == 'ignore'
+ return True
def FindExtra(self):
"""Locate extra chkconfig Services."""
diff --git a/src/lib/Bcfg2/Client/Tools/DebInit.py b/src/lib/Bcfg2/Client/Tools/DebInit.py
index d916b1662..761c51db7 100644
--- a/src/lib/Bcfg2/Client/Tools/DebInit.py
+++ b/src/lib/Bcfg2/Client/Tools/DebInit.py
@@ -18,13 +18,11 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
svcre = \
re.compile(r'/etc/.*/(?P<action>[SK])(?P<sequence>\d+)(?P<name>\S+)')
- # implement entry (Verify|Install) ops
- def VerifyService(self, entry, _):
- """Verify Service status for entry."""
-
- if entry.get('status') == 'ignore':
- return True
+ def get_svc_command(self, service, action):
+ return '/usr/sbin/invoke-rc.d %s %s' % (service.get('name'), action)
+ def verify_bootstatus(self, entry, bootstatus):
+ """Verify bootstatus for entry."""
rawfiles = glob.glob("/etc/rc*.d/[SK]*%s" % (entry.get('name')))
files = []
@@ -54,9 +52,9 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
continue
if match.group('name') == entry.get('name'):
files.append(filename)
- if entry.get('status') == 'off':
+ if bootstatus == 'off':
if files:
- entry.set('current_status', 'on')
+ entry.set('current_bootstatus', 'on')
return False
else:
return True
@@ -72,12 +70,47 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
return False
return True
else:
- entry.set('current_status', 'off')
+ entry.set('current_bootstatus', 'off')
return False
+ def VerifyService(self, entry, _):
+ """Verify Service status for entry."""
+ entry.set('target_status', entry.get('status')) # for reporting
+ bootstatus = self.get_bootstatus(entry)
+ if bootstatus is None:
+ return True
+ current_bootstatus = self.verify_bootstatus(entry, bootstatus)
+
+ if entry.get('status') == 'ignore':
+ # 'ignore' should verify
+ current_svcstatus = True
+ svcstatus = True
+ else:
+ svcstatus = self.check_service(entry)
+ if entry.get('status') == 'on':
+ if svcstatus:
+ current_svcstatus = True
+ else:
+ current_svcstatus = False
+ elif entry.get('status') == 'off':
+ if svcstatus:
+ current_svcstatus = False
+ else:
+ current_svcstatus = True
+
+ if svcstatus:
+ entry.set('current_status', 'on')
+ else:
+ entry.set('current_status', 'off')
+
+ return current_bootstatus and current_svcstatus
+
def InstallService(self, entry):
- """Install Service for entry."""
+ """Install Service entry."""
self.logger.info("Installing Service %s" % (entry.get('name')))
+ bootstatus = entry.get('bootstatus')
+
+ # check if init script exists
try:
os.stat('/etc/init.d/%s' % entry.get('name'))
except OSError:
@@ -85,20 +118,41 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
entry.get('name'))
return False
- if entry.get('status') == 'off':
- self.cmd.run("/usr/sbin/invoke-rc.d %s stop" % (entry.get('name')))
- return self.cmd.run("/usr/sbin/update-rc.d -f %s remove" %
- entry.get('name')).success
+ if bootstatus is not None:
+ seqcmdrv = True
+ if bootstatus == 'on':
+ # make sure service is enabled on boot
+ bootcmd = '/usr/sbin/update-rc.d %s defaults' % \
+ entry.get('name')
+ if entry.get('sequence'):
+ seqcmd = '/usr/sbin/update-rc.d -f %s remove' % \
+ entry.get('name')
+ seqcmdrv = self.cmd.run(seqcmd)
+ start_sequence = int(entry.get('sequence'))
+ kill_sequence = 100 - start_sequence
+ bootcmd = '%s %d %d' % (bootcmd, start_sequence,
+ kill_sequence)
+ elif bootstatus == 'off':
+ # make sure service is disabled on boot
+ bootcmd = '/usr/sbin/update-rc.d -f %s remove' % \
+ entry.get('name')
+ bootcmdrv = self.cmd.run(bootcmd)
+ if self.setup['servicemode'] == 'disabled':
+ # 'disabled' means we don't attempt to modify running svcs
+ return bootcmdrv and seqcmdrv
+ buildmode = self.setup['servicemode'] == 'build'
+ if (entry.get('status') == 'on' and not buildmode) and \
+ entry.get('current_status') == 'off':
+ svccmdrv = self.start_service(entry)
+ elif (entry.get('status') == 'off' or buildmode) and \
+ entry.get('current_status') == 'on':
+ svccmdrv = self.stop_service(entry)
+ else:
+ svccmdrv = True # ignore status attribute
+ return bootcmdrv and svccmdrv and seqcmdrv
else:
- command = "/usr/sbin/update-rc.d %s defaults" % (entry.get('name'))
- if entry.get('sequence'):
- if not self.cmd.run("/usr/sbin/update-rc.d -f %s remove" %
- entry.get('name')).success:
- return False
- start_sequence = int(entry.get('sequence'))
- kill_sequence = 100 - start_sequence
- command = "%s %d %d" % (command, start_sequence, kill_sequence)
- return self.cmd.run(command).success
+ # when bootstatus is 'None', status == 'ignore'
+ return True
def FindExtra(self):
"""Find Extra Debian Service entries."""
@@ -116,6 +170,3 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
# Extra service removal is nonsensical
# Extra services need to be reflected in the config
return
-
- def get_svc_command(self, service, action):
- return '/usr/sbin/invoke-rc.d %s %s' % (service.get('name'), action)
diff --git a/src/lib/Bcfg2/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
index 4b78581f7..8e9626521 100644
--- a/src/lib/Bcfg2/Client/Tools/RcUpdate.py
+++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
@@ -21,21 +21,38 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
'-s']).stdout.splitlines()
if 'started' in line]
+ def get_default_svcs(self):
+ """Return a list of services in the 'default' runlevel."""
+ return [line.split()[0]
+ for line in self.cmd.run(['/sbin/rc-update',
+ 'show']).stdout.splitlines()
+ if 'default' in line]
+
+ def verify_bootstatus(self, entry, bootstatus):
+ """Verify bootstatus for entry."""
+ # get a list of all started services
+ allsrv = self.get_default_svcs()
+ # set current_bootstatus attribute
+ if entry.get('name') in allsrv:
+ entry.set('current_bootstatus', 'on')
+ else:
+ entry.set('current_bootstatus', 'off')
+ if bootstatus == 'on':
+ return entry.get('name') in allsrv
+ else:
+ return entry.get('name') not in allsrv
+
def VerifyService(self, entry, _):
"""
Verify Service status for entry.
Assumes we run in the "default" runlevel.
"""
- if entry.get('status') == 'ignore':
+ entry.set('target_status', entry.get('status')) # for reporting
+ bootstatus = self.get_bootstatus(entry)
+ if bootstatus is None:
return True
-
- # get a list of all started services
- allsrv = self.get_enabled_svcs()
-
- # check if service is enabled
- result = self.cmd.run(["/sbin/rc-update", "show", "default"]).stdout
- is_enabled = entry.get("name") in result
+ current_bootstatus = self.verify_bootstatus(entry, bootstatus)
# check if init script exists
try:
@@ -45,39 +62,58 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
entry.get('name'))
return False
- # check if service is enabled
- is_running = entry.get('name') in allsrv
-
- if entry.get('status') == 'on' and not (is_enabled and is_running):
- entry.set('current_status', 'off')
- return False
-
- elif entry.get('status') == 'off' and (is_enabled or is_running):
+ if entry.get('status') == 'ignore':
+ # 'ignore' should verify
+ current_svcstatus = True
+ svcstatus = True
+ else:
+ svcstatus = self.check_service(entry)
+ if entry.get('status') == 'on':
+ if svcstatus:
+ current_svcstatus = True
+ else:
+ current_svcstatus = False
+ elif entry.get('status') == 'off':
+ if svcstatus:
+ current_svcstatus = False
+ else:
+ current_svcstatus = True
+
+ if svcstatus:
entry.set('current_status', 'on')
- return False
+ else:
+ entry.set('current_status', 'off')
- return True
+ return current_bootstatus and current_svcstatus
def InstallService(self, entry):
- """
- Install Service entry
-
- """
+ """Install Service entry."""
self.logger.info('Installing Service %s' % entry.get('name'))
- if entry.get('status') == 'on':
- if entry.get('current_status') == 'off':
- self.start_service(entry)
- # make sure it's enabled
- cmd = '/sbin/rc-update add %s default'
- return self.cmd.run(cmd % entry.get('name')).success
- elif entry.get('status') == 'off':
- if entry.get('current_status') == 'on':
- self.stop_service(entry)
- # make sure it's disabled
- cmd = '/sbin/rc-update del %s default'
- return self.cmd.run(cmd % entry.get('name')).success
-
- return False
+ bootstatus = entry.get('bootstatus')
+ if bootstatus is not None:
+ if bootstatus == 'on':
+ # make sure service is enabled on boot
+ bootcmd = '/sbin/rc-update add %s default'
+ elif bootstatus == 'off':
+ # make sure service is disabled on boot
+ bootcmd = '/sbin/rc-update del %s default'
+ bootcmdrv = self.cmd.run(bootcmd % entry.get('name')).success
+ if self.setup['servicemode'] == 'disabled':
+ # 'disabled' means we don't attempt to modify running svcs
+ return bootcmdrv
+ buildmode = self.setup['servicemode'] == 'build'
+ if (entry.get('status') == 'on' and not buildmode) and \
+ entry.get('current_status') == 'off':
+ svccmdrv = self.start_service(entry)
+ elif (entry.get('status') == 'off' or buildmode) and \
+ entry.get('current_status') == 'on':
+ svccmdrv = self.stop_service(entry)
+ else:
+ svccmdrv = True # ignore status attribute
+ return bootcmdrv and svccmdrv
+ else:
+ # when bootstatus is 'None', status == 'ignore'
+ return True
def FindExtra(self):
"""Locate extra rc-update services."""
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index c5a5ee4d6..11fe55bd6 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -519,6 +519,22 @@ class SvcTool(Tool):
"""
return '/etc/init.d/%s %s' % (service.get('name'), action)
+ def get_bootstatus(self, service):
+ """ Return the bootstatus attribute if it exists.
+
+ :param service: The service entry
+ :type service: lxml.etree._Element
+ :returns: string or None - Value of bootstatus if it exists. If
+ bootstatus is unspecified and status is not *ignore*,
+ return value of status. If bootstatus is unspecified
+ and status is *ignore*, return None.
+ """
+ if service.get('bootstatus') is not None:
+ return service.get('bootstatus')
+ elif service.get('status') != 'ignore':
+ return service.get('status')
+ return None
+
def start_service(self, service):
""" Start a service.
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index 28c38e741..598e1c6ec 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -26,6 +26,8 @@ TYPE_CHOICES = (
(TYPE_EXTRA, 'Extra'),
)
+_our_backend = None
+
def convert_entry_type_to_id(type_name):
"""Convert a entry type to its entry id"""
@@ -49,7 +51,6 @@ def hash_entry(entry_dict):
return hash(cPickle.dumps(dataset))
-_our_backend = None
def _quote(value):
"""
Quote a string to use as a table name or column
@@ -59,7 +60,10 @@ def _quote(value):
"""
global _our_backend
if not _our_backend:
- _our_backend = backend.DatabaseOperations(connection)
+ try:
+ _our_backend = backend.DatabaseOperations(connection)
+ except TypeError:
+ _our_backend = backend.DatabaseOperations()
return _our_backend.quote_name(value)
@@ -113,7 +117,6 @@ class InteractionManager(models.Manager):
pass
return []
-
def recent(self, maxdate=None):
"""
Returns the most recent interactions for clients as of a date
@@ -133,7 +136,7 @@ class Interaction(models.Model):
timestamp = models.DateTimeField(db_index=True) # 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
- server = models.CharField(max_length=256) # Name of the server used for the interaction
+ server = models.CharField(max_length=256) # server used for interaction
good_count = models.IntegerField() # of good config-items
total_count = models.IntegerField() # of total config-items
bad_count = models.IntegerField(default=0)
@@ -230,7 +233,7 @@ class Interaction(models.Model):
rv = []
for entry in self.entry_types:
if entry == 'failures':
- continue
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_BAD))
return rv
@@ -238,7 +241,7 @@ class Interaction(models.Model):
rv = []
for entry in self.entry_types:
if entry == 'failures':
- continue
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_MODIFIED))
return rv
@@ -246,7 +249,7 @@ class Interaction(models.Model):
rv = []
for entry in self.entry_types:
if entry == 'failures':
- continue
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_EXTRA))
return rv
@@ -258,7 +261,8 @@ class Interaction(models.Model):
class Performance(models.Model):
"""Object representing performance data for any interaction."""
- interaction = models.ForeignKey(Interaction, related_name="performance_items")
+ interaction = models.ForeignKey(Interaction,
+ related_name="performance_items")
metric = models.CharField(max_length=128)
value = models.DecimalField(max_digits=32, decimal_places=16)
@@ -291,11 +295,11 @@ class Group(models.Model):
class Meta:
ordering = ('name',)
-
@staticmethod
def prune_orphans():
'''Prune unused groups'''
- Group.objects.filter(interaction__isnull=True, group__isnull=True).delete()
+ Group.objects.filter(interaction__isnull=True,
+ group__isnull=True).delete()
class Bundle(models.Model):
@@ -313,11 +317,11 @@ class Bundle(models.Model):
class Meta:
ordering = ('name',)
-
@staticmethod
def prune_orphans():
'''Prune unused bundles'''
- Bundle.objects.filter(interaction__isnull=True, group__isnull=True).delete()
+ Bundle.objects.filter(interaction__isnull=True,
+ group__isnull=True).delete()
# new interaction models
@@ -420,7 +424,7 @@ class BaseEntry(models.Model):
def prune_orphans(cls):
'''Remove unused entries'''
# yeat another sqlite hack
- cls_orphans = [x['id'] \
+ cls_orphans = [x['id']
for x in cls.objects.filter(interaction__isnull=True).values("id")]
i = 0
while i < len(cls_orphans):
@@ -695,7 +699,7 @@ class PathEntry(SuccessEntry):
acls = models.ManyToManyField(FileAcl)
detail_type = models.IntegerField(default=0,
- choices=DETAIL_CHOICES)
+ choices=DETAIL_CHOICES)
details = models.TextField(default='')
ENTRY_TYPE = r"Path"
diff --git a/src/lib/Bcfg2/Reporting/templates/base.html b/src/lib/Bcfg2/Reporting/templates/base.html
index c73339911..7f1fcba3b 100644
--- a/src/lib/Bcfg2/Reporting/templates/base.html
+++ b/src/lib/Bcfg2/Reporting/templates/base.html
@@ -1,4 +1,8 @@
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -25,8 +29,9 @@
<div id="header">
<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>
+ height='115' width='300' alt='Bcfg2'
+ style='float:left; height: 115px' /></a>
+ </div>
<div id="document">
<div id="content"><div id="contentwrapper">
@@ -46,26 +51,26 @@
<li>Overview</li>
</ul>
<ul class='menu-level2'>
- <li><a href="{% url reports_summary %}">Summary</a></li>
- <li><a href="{% url reports_history %}">Recent Interactions</a></li>
- <li><a href="{% url reports_timing %}">Timing</a></li>
+ <li><a href="{% url "reports_summary" %}">Summary</a></li>
+ <li><a href="{% url "reports_history" %}">Recent Interactions</a></li>
+ <li><a href="{% url "reports_timing" %}">Timing</a></li>
</ul>
<ul class='menu-level1'>
<li>Clients</li>
</ul>
<ul class='menu-level2'>
- <li><a href="{% url reports_grid_view %}">Grid View</a></li>
- <li><a href="{% url reports_detailed_list %}">Detailed List</a></li>
- <li><a href="{% url reports_client_manage %}">Manage</a></li>
+ <li><a href="{% url "reports_grid_view" %}">Grid View</a></li>
+ <li><a href="{% url "reports_detailed_list" %}">Detailed List</a></li>
+ <li><a href="{% url "reports_client_manage" %}">Manage</a></li>
</ul>
<ul class='menu-level1'>
<li>Entries Configured</li>
</ul>
<ul class='menu-level2'>
- <li><a href="{% url reports_common_problems %}">Common problems</a></li>
- <li><a href="{% url reports_item_list "bad" %}">Bad</a></li>
- <li><a href="{% url reports_item_list "modified" %}">Modified</a></li>
- <li><a href="{% url reports_item_list "extra" %}">Extra</a></li>
+ <li><a href="{% url "reports_common_problems" %}">Common problems</a></li>
+ <li><a href="{% url "reports_item_list" "bad" %}">Bad</a></li>
+ <li><a href="{% url "reports_item_list" "modified" %}">Modified</a></li>
+ <li><a href="{% url "reports_item_list" "extra" %}">Extra</a></li>
</ul>
{% comment %}
TODO
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/detail.html b/src/lib/Bcfg2/Reporting/templates/clients/detail.html
index 4608ce6f1..e890589a7 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/detail.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/detail.html
@@ -1,24 +1,28 @@
{% extends "base.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Client {{client.name}}{% endblock %}
{% block extra_header_info %}
<style type="text/css">
.node_data {
- border: 1px solid #98DBCC;
- margin: 10px;
- padding-left: 18px;
+ border: 1px solid #98DBCC;
+ margin: 10px;
+ padding-left: 18px;
}
.node_data td {
- padding: 1px 20px 1px 2px;
+ padding: 1px 20px 1px 2px;
}
span.history_links {
- font-size: 90%;
- margin-left: 50px;
+ font-size: 90%;
+ margin-left: 50px;
}
span.history_links a {
- font-size: 90%;
+ font-size: 90%;
}
</style>
{% endblock %}
@@ -30,12 +34,12 @@ span.history_links a {
{% block content %}
<div class='detail_header'>
<h2>{{client.name}}</h2>
- <a href='{% url reports_client_manage %}#{{ client.name }}'>[manage]</a>
- <span class='history_links'><a href="{% url reports_client_history client.name %}">View History</a> | Jump to&nbsp;
+ <a href='{% url "reports_client_manage" %}#{{ client.name }}'>[manage]</a>
+ <span class='history_links'><a href="{% url "reports_client_history" client.name %}">View History</a> | Jump to&nbsp;
<select id="quick" name="quick" onchange="javascript:pageJump('quick');">
<option value="" selected="selected">--- Time ---</option>
{% for i in client.interactions.all|slice:":25" %}
- <option value="{% url reports_client_detail_pk hostname=client.name, pk=i.id %}">{{i.timestamp|date:"c"}}</option>
+ <option value="{% url "reports_client_detail_pk" hostname=client.name pk=i.id %}">{{i.timestamp|date:"c"}}</option>
{% endfor %}
</select></span>
</div>
@@ -110,7 +114,7 @@ span.history_links a {
{% for entry in entry_list %}
<tr class='{% cycle listview,listview_alt %}'>
<td class='entry_list_type'>{{entry.entry_type}}</td>
- <td><a href="{% url reports_item entry.class_name entry.pk interaction.pk %}">
+ <td><a href="{% url "reports_item" entry.class_name entry.pk interaction.pk %}">
{{entry.name}}</a></td>
</tr>
{% endfor %}
@@ -129,7 +133,7 @@ span.history_links a {
{% for failure in interaction.failures.all %}
<tr class='{% cycle listview,listview_alt %}'>
<td class='entry_list_type'>{{failure.entry_type}}</td>
- <td><a href="{% url reports_item failure.class_name failure.pk interaction.pk %}">
+ <td><a href="{% url "reports_item" failure.class_name failure.pk interaction.pk %}">
{{failure.name}}</a></td>
</tr>
{% endfor %}
@@ -140,11 +144,11 @@ span.history_links a {
{% if entry_list %}
<div class="entry_list recent_history_wrapper">
<div class="entry_list_head" style="border-bottom: 2px solid #98DBCC;">
- <h4 style="display: inline"><a href="{% url reports_client_history client.name %}">Recent Interactions</a></h4>
+ <h4 style="display: inline"><a href="{% url "reports_client_history" client.name %}">Recent Interactions</a></h4>
</div>
<div class='recent_history_box'>
{% include "widgets/interaction_list.inc" %}
- <div style='padding-left: 5px'><a href="{% url reports_client_history client.name %}">more...</a></div>
+ <div style='padding-left: 5px'><a href="{% url "reports_client_history" client.name %}">more...</a></div>
</div>
</div>
{% endif %}
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html b/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
index fd9a545ce..33c78a5f0 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Detailed Client Listing{% endblock %}
{% block pagebanner %}Clients - Detailed View{% endblock %}
@@ -21,7 +25,7 @@
</tr>
{% for entry in entry_list %}
<tr class='{% cycle listview,listview_alt %}'>
- <td class='left_column'><a href='{% url Bcfg2.Reporting.views.client_detail hostname=entry.client.name, pk=entry.id %}'>{{ entry.client.name }}</a></td>
+ <td class='left_column'><a href='{% url "Bcfg2.Reporting.views.client_detail" hostname=entry.client.name pk=entry.id %}'>{{ entry.client.name }}</a></td>
<td class='right_column' style='width:75px'><a href='{% add_url_filter state=entry.state %}'
class='{{entry|determine_client_state}}'>{{ entry.state }}</a></td>
<td class='right_column_narrow'>{{ entry.good_count }}</td>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/index.html b/src/lib/Bcfg2/Reporting/templates/clients/index.html
index d9c415c20..eba83670b 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/index.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/index.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block extra_header_info %}
{% endblock%}
@@ -17,9 +21,9 @@
<td class='{{ inter|determine_client_state }}'>
<a href="{% spaceless %}
{% if not timestamp %}
- {% url reports_client_detail inter.client.name %}
+ {% url "reports_client_detail" inter.client.name %}
{% else %}
- {% url reports_client_detail_pk inter.client.name,inter.id %}
+ {% url "reports_client_detail_pk" inter.client.name inter.id %}
{% endif %}
{% endspaceless %}">{{ inter.client.name }}</a>
</td>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/manage.html b/src/lib/Bcfg2/Reporting/templates/clients/manage.html
index 443ec8ccb..03918aad7 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/manage.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/manage.html
@@ -1,4 +1,8 @@
{% extends "base.html" %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block extra_header_info %}
{% endblock%}
@@ -24,10 +28,10 @@
<td><span id="{{ client.name }}"> </span>
<span id="ttag-{{ client.name }}"> </span>
<span id="s-ttag-{{ client.name }}"> </span>
- <a href="{% url reports_client_detail client.name %}">{{ client.name }}</a></td>
+ <a href='{% url "reports_client_detail" client.name %}'>{{ client.name }}</a></td>
<td>{% firstof client.expiration 'Active' %}</td>
<td>
- <form method="post" action="{% url reports_client_manage %}">
+ <form method="post" action='{% url "reports_client_manage" %}'>
<div> {# here for no reason other then to validate #}
<input type="hidden" name="client_name" value="{{ client.name }}" />
<input type="hidden" name="client_action" value="{% if client.expiration %}unexpire{% else %}expire{% endif %}" />
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/common.html b/src/lib/Bcfg2/Reporting/templates/config_items/common.html
index 57191ec39..91f37d7dc 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/common.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/common.html
@@ -1,5 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% load url from future %}
{% block title %}Bcfg2 - Common Problems{% endblock %}
@@ -29,9 +30,9 @@
{% for item in type_list %}
<tr class='{% cycle listview,listview_alt %}'>
<td>{{ item.ENTRY_TYPE }}</td>
- <td><a href="{% url reports_entry item.class_name, item.pk %}">{{ item.name }}</a></td>
+ <td><a href='{% url "reports_entry" item.class_name item.pk %}'>{{ item.name }}</a></td>
<td>{{ item.num_entries }}</td>
- <td><a href="{% url reports_item item.ENTRY_TYPE, item.pk %}">{{ item.short_list|join:"," }}</a></td>
+ <td><a href='{% url "reports_item" item.ENTRY_TYPE item.pk %}'>{{ item.short_list|join:"," }}</a></td>
</tr>
{% endfor %}
</table>
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html b/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
index e940889ab..e3befb0eb 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Entry Status{% endblock %}
@@ -17,10 +21,10 @@
{% for item, inters in items %}
{% for inter in inters %}
<tr class='{% cycle listview,listview_alt %}'>
- <td><a href='{% url reports_client_detail hostname=inter.client.name %}'>{{inter.client.name}}</a></td>
- <td><a href='{% url reports_client_detail_pk hostname=inter.client.name, pk=inter.pk %}'>{{inter.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe}}</a></td>
+ <td><a href='{% url "reports_client_detail" hostname=inter.client.name %}'>{{inter.client.name}}</a></td>
+ <td><a href='{% url "reports_client_detail_pk" hostname=inter.client.name pk=inter.pk %}'>{{inter.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe}}</a></td>
<td>{{ item.get_state_display }}</td>
- <td style='white-space: nowrap'><a href="{% url reports_item entry_type=item.class_name pk=item.pk %}">({{item.pk}}) {{item.short_list|join:","}}</a></td>
+ <td style='white-space: nowrap'><a href='{% url "reports_item" entry_type=item.class_name pk=item.pk %}'>({{item.pk}}) {{item.short_list|join:","}}</a></td>
</tr>
{% endfor %}
{% endfor %}
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/item.html b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
index 259414399..b03d48045 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/item.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
@@ -1,6 +1,10 @@
{% extends "base.html" %}
{% load split %}
{% load syntax_coloring %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Element Details{% endblock %}
@@ -9,20 +13,20 @@
{% block extra_header_info %}
<style type="text/css">
#table_list_header {
- font-size: 100%;
+ font-size: 100%;
}
table.entry_list {
- width: auto;
+ width: auto;
}
div.information_wrapper {
- margin: 15px;
+ margin: 15px;
}
div.diff_wrapper {
- overflow: auto;
+ overflow: auto;
}
div.entry_list h3 {
- font-size: 90%;
- padding: 5px;
+ font-size: 90%;
+ padding: 5px;
}
</style>
{% endblock%}
@@ -131,9 +135,9 @@ div.entry_list h3 {
{% if associated_list %}
<table class="entry_list" cellpadding="3">
{% for inter in associated_list %}
- <tr><td><a href="{% url reports_client_detail inter.client.name %}"
+ <tr><td><a href='{% url "reports_client_detail" inter.client.name %}'
>{{inter.client.name}}</a></td>
- <td><a href="{% url reports_client_detail_pk hostname=inter.client.name,pk=inter.id %}"
+ <td><a href='{% url "reports_client_detail_pk" hostname=inter.client.name pk=inter.id %}'
>{{inter.timestamp}}</a></td>
</tr>
{% endfor %}
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/listing.html b/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
index 864392754..0e4812e85 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Element Listing{% endblock %}
@@ -21,9 +25,9 @@
<tr style='text-align: left' ><th>Name</th><th>Count</th><th>Reason</th></tr>
{% for entry in type_data %}
<tr class='{% cycle listview,listview_alt %}'>
- <td><a href="{% url reports_entry entry.class_name entry.pk %}">{{entry.name}}</a></td>
+ <td><a href='{% url "reports_entry" entry.class_name entry.pk %}'>{{entry.name}}</a></td>
<td>{{entry.num_entries}}</td>
- <td><a href="{% url reports_item entry.class_name entry.pk %}">{{entry.short_list|join:","}}</a></td>
+ <td><a href='{% url "reports_item" entry.class_name entry.pk %}'>{{entry.short_list|join:","}}</a></td>
</tr>
{% endfor %}
</table>
diff --git a/src/lib/Bcfg2/Reporting/templates/displays/summary.html b/src/lib/Bcfg2/Reporting/templates/displays/summary.html
index b9847cf96..ffafd52e0 100644
--- a/src/lib/Bcfg2/Reporting/templates/displays/summary.html
+++ b/src/lib/Bcfg2/Reporting/templates/displays/summary.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Client Summary{% endblock %}
{% block pagebanner %}Clients - Summary{% endblock %}
@@ -30,7 +34,7 @@ hide_tables[{{ forloop.counter0 }}] = "table_{{ summary.name }}";
<table id='table_{{ summary.name }}' class='entry_list'>
{% for node in summary.nodes|sort_interactions_by_name %}
<tr class='{% cycle listview,listview_alt %}'>
- <td><a href="{% url reports_client_detail_pk hostname=node.client.name,pk=node.id %}">{{ node.client.name }}</a></td>
+ <td><a href='{% url "reports_client_detail_pk" hostname=node.client.name pk=node.id %}'>{{ node.client.name }}</a></td>
</tr>
{% endfor %}
</table>
diff --git a/src/lib/Bcfg2/Reporting/templates/displays/timing.html b/src/lib/Bcfg2/Reporting/templates/displays/timing.html
index ff775ded5..8ac5e49bb 100644
--- a/src/lib/Bcfg2/Reporting/templates/displays/timing.html
+++ b/src/lib/Bcfg2/Reporting/templates/displays/timing.html
@@ -1,5 +1,9 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
+{% comment %}
+This is needed for Django versions less than 1.5
+{% endcomment %}
+{% load url from future %}
{% block title %}Bcfg2 - Performance Metrics{% endblock %}
{% block pagebanner %}Performance Metrics{% endblock %}
@@ -12,7 +16,7 @@
<div class='client_list_box'>
{% if metrics %}
<table cellpadding="3">
- <tr id='table_list_header' class='listview'>
+ <tr id='table_list_header' class='listview'>
<td>Name</td>
<td>Parse</td>
<td>Probe</td>
@@ -21,15 +25,15 @@
<td>Config</td>
<td>Total</td>
</tr>
- {% for metric in metrics|dictsort:"name" %}
+ {% for metric in metrics|dictsort:"name" %}
<tr class='{% cycle listview,listview_alt %}'>
<td><a style='font-size: 100%'
- href="{% url reports_client_detail hostname=metric.name %}">{{ metric.name }}</a></td>
+ href='{% url "reports_client_detail" hostname=metric.name %}'>{{ metric.name }}</a></td>
{% for mitem in metric|build_metric_list %}
<td>{{ mitem }}</td>
{% endfor %}
- </tr>
- {% endfor %}
+ </tr>
+ {% endfor %}
</table>
{% else %}
<p>No metric data available</p>
diff --git a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
index f5f2e7528..489682f30 100644
--- a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
+++ b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
@@ -5,9 +5,8 @@ from django import template
from django.conf import settings
from django.core.urlresolvers import resolve, reverse, \
Resolver404, NoReverseMatch
-from django.template.loader import get_template, \
- get_template_from_string,TemplateDoesNotExist
-from django.utils.encoding import smart_unicode, smart_str
+from django.template.loader import get_template_from_string
+from django.utils.encoding import smart_str
from django.utils.safestring import mark_safe
from datetime import datetime, timedelta
from Bcfg2.Reporting.utils import filter_list
@@ -133,19 +132,22 @@ def filter_navigator(context):
del myargs[filter]
filters.append((filter,
reverse(view, args=args, kwargs=myargs) + qs))
- filters.sort(lambda x, y: cmp(x[0], y[0]))
+ filters.sort(key=lambda x: x[0])
myargs = kwargs.copy()
- selected=True
+ selected = True
if 'group' in myargs:
del myargs['group']
- selected=False
- groups = [('---', reverse(view, args=args, kwargs=myargs) + qs, selected)]
+ selected = False
+ groups = [('---',
+ reverse(view, args=args, kwargs=myargs) + qs,
+ selected)]
for group in Group.objects.values('name'):
myargs['group'] = group['name']
- groups.append((group['name'], reverse(view, args=args, kwargs=myargs) + qs,
- group['name'] == kwargs.get('group', '')))
-
+ groups.append((group['name'],
+ reverse(view, args=args, kwargs=myargs) + qs,
+ group['name'] == kwargs.get('group', '')))
+
return {'filters': filters, 'groups': groups}
except (Resolver404, NoReverseMatch, ValueError, KeyError):
pass
@@ -205,7 +207,7 @@ def sort_interactions_by_name(value):
Sort an interaction list by client name
"""
inters = list(value)
- inters.sort(lambda a, b: cmp(a.client.name, b.client.name))
+ inters.sort(key=lambda a: a.client.name)
return inters
@@ -223,7 +225,7 @@ class AddUrlFilter(template.Node):
filter_value = self.filter_value.resolve(context, True)
if filter_value:
filter_name = smart_str(self.filter_name)
- filter_value = smart_unicode(filter_value)
+ filter_value = smart_str(filter_value)
kwargs[filter_name] = filter_value
# These two don't make sense
if filter_name == 'server' and 'hostname' in kwargs:
@@ -306,6 +308,7 @@ def to_media_url(parser, token):
return MediaTag(filter_value)
+
@register.filter
def determine_client_state(entry):
"""
@@ -338,10 +341,11 @@ def do_qs(parser, token):
try:
tag, name, value = token.split_contents()
except ValueError:
- raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" \
- % token.contents.split()[0]
+ raise template.TemplateSyntaxError("%r tag requires exactly two arguments"
+ % token.contents.split()[0])
return QsNode(name, value)
+
class QsNode(template.Node):
def __init__(self, name, value):
self.name = template.Variable(name)
@@ -359,7 +363,7 @@ class QsNode(template.Node):
return ''
except KeyError:
if settings.TEMPLATE_DEBUG:
- raise Exception, "'qs' tag requires context['request']"
+ raise Exception("'qs' tag requires context['request']")
return ''
except:
return ''
@@ -380,6 +384,7 @@ def sort_link(parser, token):
return SortLinkNode(sort_key, text)
+
class SortLinkNode(template.Node):
__TMPL__ = "{% load bcfg2_tags %}<a href='{% qs 'sort' key %}'>{{ text }}</a>"
@@ -420,4 +425,3 @@ class SortLinkNode(template.Node):
raise
raise
return ''
-
diff --git a/src/lib/Bcfg2/Reporting/templatetags/syntax_coloring.py b/src/lib/Bcfg2/Reporting/templatetags/syntax_coloring.py
index 2712d6395..22700689f 100644
--- a/src/lib/Bcfg2/Reporting/templatetags/syntax_coloring.py
+++ b/src/lib/Bcfg2/Reporting/templatetags/syntax_coloring.py
@@ -1,11 +1,8 @@
-import sys
from django import template
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import smart_str
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
-from Bcfg2.Compat import u_str
-
register = template.Library()
# pylint: disable=E0611
@@ -33,9 +30,9 @@ def syntaxhilight(value, arg="diff", autoescape=None):
if colorize:
try:
- output = u_str('<style type="text/css">') \
- + smart_unicode(HtmlFormatter().get_style_defs('.highlight')) \
- + u_str('</style>')
+ output = smart_str('<style type="text/css">') \
+ + smart_str(HtmlFormatter().get_style_defs('.highlight')) \
+ + smart_str('</style>')
lexer = get_lexer_by_name(arg)
output += highlight(value, lexer, HtmlFormatter())
@@ -43,6 +40,7 @@ def syntaxhilight(value, arg="diff", autoescape=None):
except:
return value
else:
- return mark_safe(u_str('<div class="note-box">Tip: Install pygments '
- 'for highlighting</div><pre>%s</pre>') % value)
+ return mark_safe(smart_str(
+ '<div class="note-box">Tip: Install pygments '
+ 'for highlighting</div><pre>%s</pre>') % value)
syntaxhilight.needs_autoescape = True
diff --git a/src/lib/Bcfg2/Reporting/views.py b/src/lib/Bcfg2/Reporting/views.py
index 6cba7bf8c..c7c2a503f 100644
--- a/src/lib/Bcfg2/Reporting/views.py
+++ b/src/lib/Bcfg2/Reporting/views.py
@@ -338,6 +338,8 @@ def client_detail(request, hostname=None, pk=None):
for label in etypes.values():
edict[label] = []
for ekind in inter.entry_types:
+ if ekind == 'failures':
+ continue
for ent in getattr(inter, ekind).all():
edict[etypes[ent.state]].append(ent)
context['entry_types'] = edict
diff --git a/src/lib/Bcfg2/Server/Admin/Client.py b/src/lib/Bcfg2/Server/Admin/Client.py
index 570e993ed..187ccfd71 100644
--- a/src/lib/Bcfg2/Server/Admin/Client.py
+++ b/src/lib/Bcfg2/Server/Admin/Client.py
@@ -18,19 +18,15 @@ class Client(Bcfg2.Server.Admin.MetadataCore):
try:
self.metadata.add_client(args[1])
except MetadataConsistencyError:
- err = sys.exc_info()[1]
- print("Error in adding client: %s" % err)
- raise SystemExit(1)
+ self.errExit("Error in adding client: %s" % sys.exc_info()[1])
elif args[0] in ['delete', 'remove', 'del', 'rm']:
try:
self.metadata.remove_client(args[1])
except MetadataConsistencyError:
- err = sys.exc_info()[1]
- print("Error in deleting client: %s" % err)
- raise SystemExit(1)
+ self.errExit("Error in deleting client: %s" %
+ sys.exc_info()[1])
elif args[0] in ['list', 'ls']:
for client in self.metadata.list_clients():
print(client)
else:
- print("No command specified")
- raise SystemExit(1)
+ self.errExit("No command specified")
diff --git a/src/lib/Bcfg2/Server/Admin/Compare.py b/src/lib/Bcfg2/Server/Admin/Compare.py
index c56dd0a8f..e3648a6d0 100644
--- a/src/lib/Bcfg2/Server/Admin/Compare.py
+++ b/src/lib/Bcfg2/Server/Admin/Compare.py
@@ -145,5 +145,4 @@ class Compare(Bcfg2.Server.Admin.Mode):
(old, new) = args
return self.compareSpecifications(new, old)
except IndexError:
- print(self.__call__.__doc__)
- raise SystemExit(1)
+ self.errExit(self.__call__.__doc__)
diff --git a/src/lib/Bcfg2/Server/Admin/Minestruct.py b/src/lib/Bcfg2/Server/Admin/Minestruct.py
index 93e42305c..37ca74894 100644
--- a/src/lib/Bcfg2/Server/Admin/Minestruct.py
+++ b/src/lib/Bcfg2/Server/Admin/Minestruct.py
@@ -20,9 +20,8 @@ class Minestruct(Bcfg2.Server.Admin.StructureMode):
"Please see bcfg2-admin minestruct help for usage.")
try:
(opts, args) = getopt.getopt(args, 'f:g:h')
- except:
- self.log.error(self.__doc__)
- raise SystemExit(1)
+ except getopt.GetoptError:
+ self.errExit(self.__doc__)
client = args[0]
output = sys.stdout
@@ -33,8 +32,7 @@ class Minestruct(Bcfg2.Server.Admin.StructureMode):
try:
output = open(optarg, 'w')
except IOError:
- self.log.error("Failed to open file: %s" % (optarg))
- raise SystemExit(1)
+ self.errExit("Failed to open file: %s" % (optarg))
elif opt == '-g':
groups = optarg.split(':')
@@ -43,10 +41,9 @@ class Minestruct(Bcfg2.Server.Admin.StructureMode):
for source in self.bcore.plugins_by_type(PullSource):
for item in source.GetExtra(client):
extra.add(item)
- except:
- self.log.error("Failed to find extra entry info for client %s" %
- client)
- raise SystemExit(1)
+ except: # pylint: disable=W0702
+ self.errExit("Failed to find extra entry info for client %s" %
+ client)
root = lxml.etree.Element("Base")
self.log.info("Found %d extra entries" % (len(extra)))
add_point = root
diff --git a/src/lib/Bcfg2/Server/Admin/Pull.py b/src/lib/Bcfg2/Server/Admin/Pull.py
index 8001425df..459fcec65 100644
--- a/src/lib/Bcfg2/Server/Admin/Pull.py
+++ b/src/lib/Bcfg2/Server/Admin/Pull.py
@@ -32,9 +32,8 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
use_stdin = False
try:
opts, gargs = getopt.getopt(args, 'vfIs')
- except:
- print(self.__doc__)
- raise SystemExit(1)
+ except getopt.GetoptError:
+ self.errExit(self.__doc__)
for opt in opts:
if opt[0] == '-v':
self.log = True
diff --git a/src/lib/Bcfg2/Server/Admin/Reports.py b/src/lib/Bcfg2/Server/Admin/Reports.py
index 6e313e84b..849df8025 100644
--- a/src/lib/Bcfg2/Server/Admin/Reports.py
+++ b/src/lib/Bcfg2/Server/Admin/Reports.py
@@ -79,8 +79,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
def __call__(self, args):
if len(args) == 0 or args[0] == '-h':
- print(self.__usage__)
- raise SystemExit(0)
+ self.errExit(self.__usage__)
# FIXME - dry run
@@ -101,9 +100,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
management.call_command("syncdb", verbosity=vrb)
management.call_command("migrate", verbosity=vrb)
except:
- print("Update failed: %s" %
- traceback.format_exc().splitlines()[-1])
- raise SystemExit(1)
+ self.errExit("Update failed: %s" % sys.exc_info()[1])
elif args[0] == 'purge':
expired = False
client = None
@@ -124,22 +121,20 @@ class Reports(Bcfg2.Server.Admin.Mode):
maxdate = datetime.datetime.now() - \
datetime.timedelta(days=int(args[i + 1]))
except:
- self.log.error("Invalid number of days: %s" %
- args[i + 1])
- raise SystemExit(-1)
+ self.errExit("Invalid number of days: %s" %
+ args[i + 1])
i = i + 1
elif args[i] == '--expired':
expired = True
i = i + 1
if expired:
if state:
- self.log.error("--state is not valid with --expired")
- raise SystemExit(-1)
+ self.errExit("--state is not valid with --expired")
self.purge_expired(maxdate)
else:
self.purge(client, maxdate, state)
else:
- print("Unknown command: %s" % args[0])
+ self.errExit("Unknown command: %s" % args[0])
@transaction.commit_on_success
def scrub(self):
@@ -155,8 +150,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
(start_count - cls.objects.count(), cls.__class__.__name__))
except:
print("Failed to prune %s: %s" %
- (cls.__class__.__name__,
- traceback.format_exc().splitlines()[-1]))
+ (cls.__class__.__name__, sys.exc_info()[1]))
def django_command_proxy(self, command):
'''Call a django command'''
@@ -180,8 +174,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
cobj = Client.objects.get(name=client)
ipurge = ipurge.filter(client=cobj)
except Client.DoesNotExist:
- self.log.error("Client %s not in database" % client)
- raise SystemExit(-1)
+ self.errExit("Client %s not in database" % client)
self.log.debug("Filtering by client: %s" % client)
if maxdate:
diff --git a/src/lib/Bcfg2/Server/Admin/Syncdb.py b/src/lib/Bcfg2/Server/Admin/Syncdb.py
index 4ba840b86..53cfd1bec 100644
--- a/src/lib/Bcfg2/Server/Admin/Syncdb.py
+++ b/src/lib/Bcfg2/Server/Admin/Syncdb.py
@@ -22,10 +22,7 @@ class Syncdb(Bcfg2.Server.Admin.Mode):
call_command("syncdb", interactive=False, verbosity=0)
self._database_available = True
except ImproperlyConfigured:
- err = sys.exc_info()[1]
- self.log.error("Django configuration problem: %s" % err)
- raise SystemExit(1)
+ self.errExit("Django configuration problem: %s" %
+ sys.exc_info()[1])
except:
- err = sys.exc_info()[1]
- self.log.error("Database update failed: %s" % err)
- raise SystemExit(1)
+ self.errExit("Database update failed: %s" % sys.exc_info()[1])
diff --git a/src/lib/Bcfg2/Server/Admin/Xcmd.py b/src/lib/Bcfg2/Server/Admin/Xcmd.py
index be556bed4..036129a1b 100644
--- a/src/lib/Bcfg2/Server/Admin/Xcmd.py
+++ b/src/lib/Bcfg2/Server/Admin/Xcmd.py
@@ -4,7 +4,6 @@ import sys
import Bcfg2.Options
import Bcfg2.Proxy
import Bcfg2.Server.Admin
-from Bcfg2.Compat import xmlrpclib
class Xcmd(Bcfg2.Server.Admin.Mode):
@@ -31,27 +30,15 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
ca=setup['ca'],
timeout=setup['timeout'])
if len(setup['args']) == 0:
- print("Usage: xcmd <xmlrpc method> <optional arguments>")
- return
+ self.errExit("Usage: xcmd <xmlrpc method> <optional arguments>")
cmd = setup['args'][0]
args = ()
if len(setup['args']) > 1:
args = tuple(setup['args'][1:])
try:
data = getattr(proxy, cmd)(*args)
- except xmlrpclib.Fault:
- flt = sys.exc_info()[1]
- if flt.faultCode == 7:
- print("Unknown method %s" % cmd)
- return
- elif flt.faultCode == 20:
- return
- else:
- raise
except Bcfg2.Proxy.ProxyError:
- err = sys.exc_info()[1]
- print("Proxy Error: %s" % err)
- return
+ self.errExit("Proxy Error: %s" % sys.exc_info()[1])
if data is not None:
print(data)
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index c246860c1..6e0d38418 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -785,7 +785,8 @@ class BaseCore(object):
while self.fam.pending() != 0:
time.sleep(1)
- self.set_debug(None, self.debug_flag)
+ if self.debug_flag:
+ self.set_debug(None, self.debug_flag)
self._block()
def _daemonize(self):
diff --git a/src/lib/Bcfg2/Server/Lint/Genshi.py b/src/lib/Bcfg2/Server/Lint/Genshi.py
index 7edeb8a49..1ecb6da42 100755
--- a/src/lib/Bcfg2/Server/Lint/Genshi.py
+++ b/src/lib/Bcfg2/Server/Lint/Genshi.py
@@ -37,6 +37,12 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
err = sys.exc_info()[1]
self.LintError("genshi-syntax-error",
"Genshi syntax error: %s" % err)
+ except:
+ etype, err = sys.exc_info()[:2]
+ self.LintError(
+ "genshi-syntax-error",
+ "Unexpected Genshi error on %s: %s: %s" %
+ (entry.name, etype.__name__, err))
def check_tgenshi(self):
""" Check templates in TGenshi for syntax errors. """
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index c6ac9d8dc..842202a9c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -10,6 +10,7 @@ import lxml.etree
import Bcfg2.Options
import Bcfg2.Server.Plugin
import Bcfg2.Server.Lint
+from itertools import chain
from Bcfg2.Server.Plugin import PluginExecutionError
# pylint: disable=W0622
from Bcfg2.Compat import u_str, unicode, b64encode, walk_packages, \
@@ -35,6 +36,24 @@ SETUP = None
#: facility for passing it otherwise.
CFG = None
+_HANDLERS = []
+
+
+def handlers():
+ """ A list of Cfg handler classes. Loading the handlers must
+ be done at run-time, not at compile-time, or it causes a
+ circular import and Bad Things Happen."""
+ if not _HANDLERS:
+ for submodule in walk_packages(path=__path__, prefix=__name__ + "."):
+ mname = submodule[1].rsplit('.', 1)[-1]
+ module = getattr(__import__(submodule[1]).Server.Plugins.Cfg,
+ mname)
+ hdlr = getattr(module, mname)
+ if issubclass(hdlr, CfgBaseFileMatcher):
+ _HANDLERS.append(hdlr)
+ _HANDLERS.sort(key=operator.attrgetter("__priority__"))
+ return _HANDLERS
+
class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
Bcfg2.Server.Plugin.Debuggable):
@@ -459,7 +478,6 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
entry_type, encoding)
Bcfg2.Server.Plugin.Debuggable.__init__(self)
self.specific = None
- self._handlers = None
__init__.__doc__ = Bcfg2.Server.Plugin.EntrySet.__doc__
def set_debug(self, debug):
@@ -468,24 +486,6 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
entry.set_debug(debug)
return rv
- @property
- def handlers(self):
- """ A list of Cfg handler classes. Loading the handlers must
- be done at run-time, not at compile-time, or it causes a
- circular import and Bad Things Happen."""
- if self._handlers is None:
- self._handlers = []
- for submodule in walk_packages(path=__path__,
- prefix=__name__ + "."):
- mname = submodule[1].rsplit('.', 1)[-1]
- module = getattr(__import__(submodule[1]).Server.Plugins.Cfg,
- mname)
- hdlr = getattr(module, mname)
- if CfgBaseFileMatcher in hdlr.__mro__:
- self._handlers.append(hdlr)
- self._handlers.sort(key=operator.attrgetter("__priority__"))
- return self._handlers
-
def handle_event(self, event):
""" Dispatch a FAM event to :func:`entry_init` or the
appropriate child handler object.
@@ -502,7 +502,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
# process a bogus changed event like a created
return
- for hdlr in self.handlers:
+ for hdlr in handlers():
if hdlr.handles(event, basename=self.path):
if action == 'changed':
# warn about a bogus 'changed' event, but
@@ -888,12 +888,15 @@ class CfgLint(Bcfg2.Server.Lint.ServerPlugin):
for basename, entry in list(self.core.plugins['Cfg'].entries.items()):
self.check_delta(basename, entry)
self.check_pubkey(basename, entry)
+ self.check_missing_files()
@classmethod
def Errors(cls):
return {"cat-file-used": "warning",
"diff-file-used": "warning",
- "no-pubkey-xml": "warning"}
+ "no-pubkey-xml": "warning",
+ "unknown-cfg-files": "error",
+ "extra-cfg-files": "error"}
def check_delta(self, basename, entry):
""" check that no .cat or .diff files are in use """
@@ -927,3 +930,41 @@ class CfgLint(Bcfg2.Server.Lint.ServerPlugin):
self.LintError("no-pubkey-xml",
"%s has no corresponding pubkey.xml at %s" %
(basename, pubkey))
+
+ def check_missing_files(self):
+ """ check that all files on the filesystem are known to Cfg """
+ cfg = self.core.plugins['Cfg']
+
+ # first, collect ignore patterns from handlers
+ ignore = []
+ for hdlr in handlers():
+ ignore.extend(hdlr.__ignore__)
+
+ # next, get a list of all non-ignored files on the filesystem
+ all_files = set()
+ for root, _, files in os.walk(cfg.data):
+ all_files.update(os.path.join(root, fname)
+ for fname in files
+ if not any(fname.endswith("." + i)
+ for i in ignore))
+
+ # next, get a list of all files known to Cfg
+ cfg_files = set()
+ for root, eset in cfg.entries.items():
+ cfg_files.update(os.path.join(cfg.data, root.lstrip("/"), fname)
+ for fname in eset.entries.keys())
+
+ # finally, compare the two
+ unknown_files = all_files - cfg_files
+ extra_files = cfg_files - all_files
+ if unknown_files:
+ self.LintError(
+ "unknown-cfg-files",
+ "Files on the filesystem could not be understood by Cfg: %s" %
+ "; ".join(unknown_files))
+ if extra_files:
+ self.LintError(
+ "extra-cfg-files",
+ "Cfg has entries for files that do not exist on the "
+ "filesystem: %s\nThis is probably a bug." %
+ "; ".join(extra_files))
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index bb7caab0d..20b2c9500 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -675,7 +675,10 @@ class YumCollection(Collection):
gdicts.append(dict(group=group, type=ptype))
if self.use_yum:
- return self.call_helper("get_groups", inputdata=gdicts)
+ try:
+ return self.call_helper("get_groups", inputdata=gdicts)
+ except ValueError:
+ return dict()
else:
pkgs = dict()
for gdict in gdicts:
@@ -838,12 +841,13 @@ class YumCollection(Collection):
return Collection.complete(self, packagelist)
if packagelist:
- result = \
- self.call_helper("complete",
- dict(packages=list(packagelist),
- groups=list(self.get_relevant_groups())))
- if not result:
- # some sort of error, reported by call_helper()
+ try:
+ result = self.call_helper(
+ "complete",
+ dict(packages=list(packagelist),
+ groups=list(self.get_relevant_groups())))
+ except ValueError:
+ # error reported by call_helper()
return set(), packagelist
# json doesn't understand sets or tuples, so we get back a
# lists of lists (packages) and a list of unicode strings
@@ -905,7 +909,7 @@ class YumCollection(Collection):
err = sys.exc_info()[1]
self.logger.error("Packages: error reading bcfg2-yum-helper "
"output: %s" % err)
- return None
+ raise
def setup_data(self, force_update=False):
""" Do any collection-level data setup tasks. This is called
@@ -931,13 +935,21 @@ class YumCollection(Collection):
if force_update:
# we call this twice: one to clean up data from the old
# config, and once to clean up data from the new config
- self.call_helper("clean")
+ try:
+ self.call_helper("clean")
+ except ValueError:
+ # error reported by call_helper
+ pass
os.unlink(self.cfgfile)
self.write_config()
if force_update:
- self.call_helper("clean")
+ try:
+ self.call_helper("clean")
+ except ValueError:
+ # error reported by call_helper
+ pass
class YumSource(Source):
diff --git a/src/lib/Bcfg2/settings.py b/src/lib/Bcfg2/settings.py
index 9393830a8..9adfd66bf 100644
--- a/src/lib/Bcfg2/settings.py
+++ b/src/lib/Bcfg2/settings.py
@@ -32,6 +32,8 @@ TIME_ZONE = None
DEBUG = False
TEMPLATE_DEBUG = DEBUG
+ALLOWED_HOSTS = ['*']
+
MEDIA_URL = '/site_media/'
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 31e49c00b..14d188342 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -83,7 +83,7 @@ def main():
raise SystemExit(1)
mode = mode_cls(setup)
try:
- mode(setup['args'][1:])
+ return mode(setup['args'][1:])
finally:
mode.shutdown()
else:
@@ -93,6 +93,6 @@ def main():
if __name__ == '__main__':
try:
- main()
+ sys.exit(main())
except KeyboardInterrupt:
raise SystemExit(1)
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 6aafd24d1..4e71ba35a 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -231,10 +231,14 @@ class InfoCore(cmd.Cmd, Bcfg2.Server.Core.BaseCore):
print("Refusing to write files outside of /tmp without -f "
"option")
return
- lxml.etree.ElementTree(self.BuildConfiguration(client)).write(
- ofile,
- encoding='UTF-8', xml_declaration=True,
- pretty_print=True)
+ try:
+ lxml.etree.ElementTree(self.BuildConfiguration(client)).write(
+ ofile,
+ encoding='UTF-8', xml_declaration=True,
+ pretty_print=True)
+ except IOError:
+ err = sys.exc_info()[1]
+ print("Failed to write File %s: %s" % (ofile, err))
else:
print(self._get_usage(self.do_build))
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 9a98eaaaa..ab3b6450f 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -3,6 +3,7 @@
"""This tool examines your Bcfg2 specifications for errors."""
import sys
+import time
import inspect
import logging
import Bcfg2.Logger
@@ -52,7 +53,11 @@ def run_plugin(plugin, plugin_name, setup=None, errorhandler=None,
args.append(setup)
# python 2.5 doesn't support mixing *magic and keyword arguments
- return plugin(*args, **dict(files=files, errorhandler=errorhandler)).Run()
+ start = time.time()
+ rv = plugin(*args, **dict(files=files, errorhandler=errorhandler)).Run()
+ LOGGER.debug(" Ran %s in %0.2f seconds" % (plugin_name,
+ time.time() - start))
+ return rv
def get_errorhandler(setup):
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
index 2e758774e..f838030e2 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
@@ -298,21 +298,20 @@ class TestCfgEntrySet(TestEntrySet):
for submodule in walk_packages(path=Bcfg2.Server.Plugins.Cfg.__path__,
prefix="Bcfg2.Server.Plugins.Cfg."):
expected.append(submodule[1].rsplit('.', 1)[-1])
- eset = self.get_obj()
- self.assertItemsEqual(expected,
- [h.__name__ for h in eset.handlers])
+ self.assertItemsEqual(expected, [h.__name__ for h in handlers()])
- def test_handle_event(self):
+ @patch("Bcfg2.Server.Plugins.Cfg.handlers")
+ def test_handle_event(self, mock_handlers):
eset = self.get_obj()
eset.entry_init = Mock()
- eset._handlers = [Mock(), Mock(), Mock()]
- for hdlr in eset.handlers:
+ mock_handlers.return_value = [Mock(), Mock(), Mock()]
+ for hdlr in mock_handlers.return_value:
hdlr.__name__ = "handler"
eset.entries = dict()
def reset():
eset.entry_init.reset_mock()
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
hdlr.reset_mock()
# test that a bogus deleted event is discarded
@@ -322,7 +321,7 @@ class TestCfgEntrySet(TestEntrySet):
eset.handle_event(evt)
self.assertFalse(eset.entry_init.called)
self.assertItemsEqual(eset.entries, dict())
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
self.assertFalse(hdlr.handles.called)
self.assertFalse(hdlr.ignore.called)
@@ -333,7 +332,7 @@ class TestCfgEntrySet(TestEntrySet):
evt.filename = os.path.join(datastore, "test.txt")
# test with no handler that handles
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
hdlr.handles.return_value = False
hdlr.ignore.return_value = False
@@ -341,16 +340,16 @@ class TestCfgEntrySet(TestEntrySet):
eset.handle_event(evt)
self.assertFalse(eset.entry_init.called)
self.assertItemsEqual(eset.entries, dict())
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
hdlr.handles.assert_called_with(evt, basename=eset.path)
hdlr.ignore.assert_called_with(evt, basename=eset.path)
# test with a handler that handles the entry
reset()
- eset.handlers[-1].handles.return_value = True
+ mock_handlers.return_value[-1].handles.return_value = True
eset.handle_event(evt)
- eset.entry_init.assert_called_with(evt, eset.handlers[-1])
- for hdlr in eset.handlers:
+ eset.entry_init.assert_called_with(evt, mock_handlers.return_value[-1])
+ for hdlr in mock_handlers.return_value:
hdlr.handles.assert_called_with(evt, basename=eset.path)
if not hdlr.return_value:
hdlr.ignore.assert_called_with(evt, basename=eset.path)
@@ -358,14 +357,14 @@ class TestCfgEntrySet(TestEntrySet):
# test with a handler that ignores the entry before one
# that handles it
reset()
- eset.handlers[0].ignore.return_value = True
+ mock_handlers.return_value[0].ignore.return_value = True
eset.handle_event(evt)
self.assertFalse(eset.entry_init.called)
- eset.handlers[0].handles.assert_called_with(evt,
+ mock_handlers.return_value[0].handles.assert_called_with(evt,
basename=eset.path)
- eset.handlers[0].ignore.assert_called_with(evt,
+ mock_handlers.return_value[0].ignore.assert_called_with(evt,
basename=eset.path)
- for hdlr in eset.handlers[1:]:
+ for hdlr in mock_handlers.return_value[1:]:
self.assertFalse(hdlr.handles.called)
self.assertFalse(hdlr.ignore.called)
@@ -377,7 +376,7 @@ class TestCfgEntrySet(TestEntrySet):
eset.entries[evt.filename] = Mock()
eset.handle_event(evt)
self.assertFalse(eset.entry_init.called)
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
self.assertFalse(hdlr.handles.called)
self.assertFalse(hdlr.ignore.called)
eset.entries[evt.filename].handle_event.assert_called_with(evt)
@@ -387,7 +386,7 @@ class TestCfgEntrySet(TestEntrySet):
evt.code2str.return_value = "deleted"
eset.handle_event(evt)
self.assertFalse(eset.entry_init.called)
- for hdlr in eset.handlers:
+ for hdlr in mock_handlers.return_value:
self.assertFalse(hdlr.handles.called)
self.assertFalse(hdlr.ignore.called)
self.assertItemsEqual(eset.entries, dict())
diff --git a/tools/export.py b/tools/export.py
index 0f4724e6b..7c3c56db2 100755
--- a/tools/export.py
+++ b/tools/export.py
@@ -136,8 +136,7 @@ E.G. 1.2.0pre1 is a valid version.
tarname = '/tmp/%s-%s.tar.gz' % (pkgname, version)
- newchangelog = \
-"""bcfg2 (%s-0.0) unstable; urgency=low
+ newchangelog = """bcfg2 (%s-0.0) unstable; urgency=low
* New upstream release
@@ -177,7 +176,9 @@ E.G. 1.2.0pre1 is a valid version.
"- New upstream release\n", "\n"]
# write out the new RPM changelog
- specs = ["misc/bcfg2.spec", "misc/bcfg2-selinux.spec", "redhat/bcfg2.spec.in"]
+ specs = ["misc/bcfg2.spec",
+ "misc/bcfg2-selinux.spec",
+ "redhat/bcfg2.spec.in"]
if options.dryrun:
print("*** Add the following to the top of the %%changelog section in %s:\n%s\n"
% (rpmchangelog, " and ".join(specs)))
@@ -227,6 +228,29 @@ E.G. 1.2.0pre1 is a valid version.
'VERSION="%s"\n' % version,
startswith=True,
dryrun=options.dryrun)
+ # update solaris IPS version
+ find_and_replace('solaris-ips/Makefile', 'VERS=',
+ 'VERS=%s-1\n' % version,
+ startswith=True,
+ dryrun=options.dryrun)
+ find_and_replace('solaris-ips/MANIFEST.bcfg2.header',
+ 'set name=pkg.fmri value="pkg://bcfg2/bcfg2@',
+ 'set name=pkg.fmri value="pkg://bcfg2/bcfg2@%s"' % version,
+ startswith=True,
+ dryrun=options.dryrun)
+ find_and_replace('solaris-ips/MANIFEST.bcfg2-server.header',
+ 'set name=pkg.fmri value="pkg://bcfg2/bcfg2-server@',
+ 'set name=pkg.fmri value="pkg://bcfg2/bcfg2-server@%s"' % version,
+ startswith=True,
+ dryrun=options.dryrun)
+ find_and_replace('solaris-ips/pkginfo.bcfg2', 'VERSION=',
+ 'VERSION="%s"\n' % version,
+ startswith=True,
+ dryrun=options.dryrun)
+ find_and_replace('solaris-ips/pkginfo.bcfg2-server', 'VERSION=',
+ 'VERSION="%s"\n' % version,
+ startswith=True,
+ dryrun=options.dryrun)
# set new version in Bcfg2/version.py
find_and_replace('src/lib/Bcfg2/version.py',
'__version__ =',
@@ -249,30 +273,30 @@ E.G. 1.2.0pre1 is a valid version.
else:
find_and_replace('misc/bcfg2.spec', 'Release: ',
'Release: 0.%s.%s\n' %
- (version_info['build'][-1], version_info['build']),
+ (version_info['build'][-1], version_info['build']),
dryrun=options.dryrun)
find_and_replace('misc/bcfg2-selinux.spec', 'Release: ',
'Release: 0.%s.%s\n' %
- (version_info['build'][-1], version_info['build']),
+ (version_info['build'][-1], version_info['build']),
dryrun=options.dryrun)
find_and_replace('misc/bcfg2.spec', '%setup',
'%%setup -q -n %%{name}-%%{version}%s\n' %
- version_info['build'],
+ version_info['build'],
startswith=True,
dryrun=options.dryrun)
find_and_replace('misc/bcfg2-selinux.spec', '%setup',
'%%setup -q -n %%{name}-%%{version}%s\n' %
- version_info['build'],
+ version_info['build'],
startswith=True,
dryrun=options.dryrun)
find_and_replace('misc/bcfg2.spec', 'BuildRoot',
'BuildRoot: %%{_tmppath}/%%{name}-%%{version}%s-%%{release}-root-%%(%%{__id_u} -n)\n' %
- version_info['build'],
+ version_info['build'],
startswith=True,
dryrun=options.dryrun)
find_and_replace('misc/bcfg2-selinux.spec', 'BuildRoot',
'BuildRoot: %%{_tmppath}/%%{name}-%%{version}%s-%%{release}-root-%%(%%{__id_u} -n)\n' %
- version_info['build'],
+ version_info['build'],
startswith=True,
dryrun=options.dryrun)
# fix pre problem noted in