summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNarayan Desai <desai@mcs.anl.gov>2011-05-10 11:24:28 -0500
committerNarayan Desai <desai@mcs.anl.gov>2011-05-10 11:24:28 -0500
commit0e75875e9bd9900a6a3c7ab118c448e48829eaef (patch)
tree391204747f48598c4e978d3724afbd5b8aa1d12c
parentf2d218ccd2de93ef639347933ba127ef081b4401 (diff)
parent91634f9a3b888eee3cd5f9a777fcb075fc666c9a (diff)
downloadbcfg2-0e75875e9bd9900a6a3c7ab118c448e48829eaef.tar.gz
bcfg2-0e75875e9bd9900a6a3c7ab118c448e48829eaef.tar.bz2
bcfg2-0e75875e9bd9900a6a3c7ab118c448e48829eaef.zip
Merge branch 'master' of git.mcs.anl.gov:bcfg2
-rw-r--r--AUTHORS3
-rw-r--r--debian/changelog6
-rw-r--r--debian/control2
-rw-r--r--doc/appendix/guides/bootstrap.txt39
-rw-r--r--doc/appendix/guides/centos.txt31
-rw-r--r--doc/appendix/guides/converging_rhel5.txt2
-rw-r--r--doc/appendix/guides/fedora.txt28
-rw-r--r--doc/appendix/guides/import-existing-ssh-keys.txt2
-rw-r--r--doc/appendix/guides/ubuntu.txt54
-rw-r--r--doc/client/metadata.txt33
-rw-r--r--doc/client/tools.txt37
-rw-r--r--doc/client/tools/vcs.txt15
-rw-r--r--doc/client/tools/yumng.txt16
-rw-r--r--doc/development/client-driver.txt1
-rw-r--r--doc/development/docstyleguide.txt6
-rw-r--r--doc/development/plugins.txt6
-rw-r--r--doc/getting_started/index.txt13
-rw-r--r--doc/help/faq/general.txt8
-rw-r--r--doc/help/troubleshooting.txt41
-rw-r--r--doc/installation/prerequisites.txt41
-rw-r--r--doc/server/configurationentries.txt12
-rw-r--r--doc/server/plugins/connectors/properties.txt30
-rw-r--r--doc/server/plugins/generators/cfg.txt24
-rw-r--r--doc/server/plugins/generators/nagiosgen.txt34
-rw-r--r--doc/server/plugins/generators/packages.txt19
-rw-r--r--doc/server/plugins/generators/rules.txt71
-rw-r--r--doc/server/plugins/generators/sslca.txt52
-rw-r--r--doc/server/plugins/generators/tcheetah.txt16
-rw-r--r--doc/server/plugins/generators/tgenshi/index.txt7
-rw-r--r--doc/server/plugins/grouping/metadata.txt6
-rw-r--r--doc/server/plugins/structures/bundler/index.txt46
-rw-r--r--doc/unsorted/help.txt45
-rw-r--r--examples/bcfg2-lint.conf25
-rw-r--r--man/bcfg2-admin.84
-rw-r--r--man/bcfg2-lint.8169
-rw-r--r--man/bcfg2-lint.conf.5166
-rw-r--r--man/bcfg2-ping-sweep.820
-rw-r--r--man/bcfg2-repo-validate.822
-rw-r--r--man/bcfg2-reports.8163
-rw-r--r--man/bcfg2-server.82
-rw-r--r--misc/bcfg2.spec14
-rw-r--r--osx/Introduction.txt16
-rw-r--r--osx/Makefile6
-rw-r--r--osx/preflight2
-rw-r--r--redhat/RELEASE2
-rw-r--r--redhat/bcfg2.spec.in14
-rw-r--r--reports/site_media/CalendarPopup.js1386
-rw-r--r--schemas/atom.xsd8
-rw-r--r--schemas/bundle.xsd68
-rw-r--r--schemas/decisions.xsd2
-rw-r--r--schemas/genshi.xsd97
-rw-r--r--schemas/info.xsd12
-rw-r--r--schemas/metadata.xsd18
-rw-r--r--schemas/nagiosgen.xsd31
-rw-r--r--schemas/pathentry.xsd9
-rw-r--r--schemas/pkgtype.xsd28
-rw-r--r--schemas/rules.xsd73
-rw-r--r--schemas/servicetype.xsd14
-rw-r--r--schemas/xinclude.xsd41
-rw-r--r--schemas/xs3p.xsl8503
-rw-r--r--setup.py3
-rw-r--r--solaris/Makefile2
-rw-r--r--solaris/pkginfo.bcfg22
-rw-r--r--solaris/pkginfo.bcfg2-server2
-rw-r--r--src/lib/Bcfg2Py3Incompat.py2
-rw-r--r--src/lib/Bcfg2Py3k.py81
-rw-r--r--src/lib/Client/Frame.py33
-rw-r--r--src/lib/Client/Tools/APT.py6
-rw-r--r--src/lib/Client/Tools/Action.py7
-rw-r--r--src/lib/Client/Tools/Chkconfig.py5
-rw-r--r--src/lib/Client/Tools/DebInit.py9
-rw-r--r--src/lib/Client/Tools/POSIX.py29
-rw-r--r--src/lib/Client/Tools/Pacman.py2
-rw-r--r--src/lib/Client/Tools/RcUpdate.py5
-rw-r--r--src/lib/Client/Tools/SMF.py5
-rw-r--r--src/lib/Client/Tools/Systemd.py59
-rw-r--r--src/lib/Client/Tools/Upstart.py9
-rw-r--r--src/lib/Client/Tools/VCS.py137
-rw-r--r--src/lib/Client/Tools/YUM24.py54
-rw-r--r--src/lib/Client/Tools/YUMng.py35
-rw-r--r--src/lib/Client/Tools/__init__.py73
-rw-r--r--src/lib/Client/Tools/launchd.py5
-rw-r--r--src/lib/Component.py61
-rw-r--r--src/lib/Logger.py73
-rw-r--r--src/lib/Options.py14
-rw-r--r--src/lib/Proxy.py86
-rw-r--r--src/lib/SSLServer.py52
-rw-r--r--src/lib/Server/Admin/Backup.py3
-rw-r--r--src/lib/Server/Admin/Bundle.py50
-rw-r--r--src/lib/Server/Admin/Client.py15
-rw-r--r--src/lib/Server/Admin/Compare.py59
-rw-r--r--src/lib/Server/Admin/Group.py15
-rw-r--r--src/lib/Server/Admin/Init.py120
-rw-r--r--src/lib/Server/Admin/Perf.py11
-rw-r--r--src/lib/Server/Admin/Pull.py46
-rw-r--r--src/lib/Server/Admin/Query.py18
-rw-r--r--src/lib/Server/Admin/Reports.py109
-rw-r--r--src/lib/Server/Admin/Snapshots.py8
-rw-r--r--src/lib/Server/Admin/Tidy.py14
-rw-r--r--src/lib/Server/Admin/Viz.py16
-rw-r--r--src/lib/Server/Admin/Xcmd.py25
-rw-r--r--src/lib/Server/Admin/__init__.py31
-rw-r--r--src/lib/Server/Core.py81
-rw-r--r--src/lib/Server/FileMonitor.py18
-rw-r--r--src/lib/Server/Hostbase/ldapauth.py82
-rw-r--r--src/lib/Server/Hostbase/media/base.css10
-rw-r--r--src/lib/Server/Hostbase/media/global.css16
-rw-r--r--src/lib/Server/Hostbase/media/layout.css124
-rw-r--r--src/lib/Server/Hostbase/settings.py2
-rw-r--r--src/lib/Server/Lint/Bundles.py64
-rw-r--r--src/lib/Server/Lint/Comments.py188
-rw-r--r--src/lib/Server/Lint/Duplicates.py82
-rw-r--r--src/lib/Server/Lint/InfoXML.py43
-rw-r--r--src/lib/Server/Lint/Pkgmgr.py38
-rw-r--r--src/lib/Server/Lint/RequiredAttrs.py72
-rw-r--r--src/lib/Server/Lint/Validate.py186
-rw-r--r--src/lib/Server/Lint/__init__.py155
-rw-r--r--src/lib/Server/Plugin.py169
-rw-r--r--src/lib/Server/Plugins/Account.py38
-rw-r--r--src/lib/Server/Plugins/Base.py12
-rw-r--r--src/lib/Server/Plugins/Bundler.py6
-rw-r--r--src/lib/Server/Plugins/Cfg.py63
-rw-r--r--src/lib/Server/Plugins/DBStats.py6
-rw-r--r--src/lib/Server/Plugins/Decisions.py3
-rw-r--r--src/lib/Server/Plugins/Deps.py21
-rw-r--r--src/lib/Server/Plugins/Editor.py20
-rw-r--r--src/lib/Server/Plugins/GroupPatterns.py16
-rw-r--r--src/lib/Server/Plugins/Hostbase.py104
-rw-r--r--src/lib/Server/Plugins/Ldap.py14
-rw-r--r--src/lib/Server/Plugins/Metadata.py79
-rw-r--r--src/lib/Server/Plugins/NagiosGen.py209
-rw-r--r--src/lib/Server/Plugins/Ohai.py8
-rw-r--r--src/lib/Server/Plugins/Packages.py83
-rw-r--r--src/lib/Server/Plugins/Pkgmgr.py49
-rw-r--r--src/lib/Server/Plugins/Probes.py16
-rw-r--r--src/lib/Server/Plugins/Properties.py49
-rw-r--r--src/lib/Server/Plugins/SGenshi.py10
-rw-r--r--src/lib/Server/Plugins/SSHbase.py38
-rw-r--r--src/lib/Server/Plugins/SSLCA.py49
-rw-r--r--src/lib/Server/Plugins/Snapshots.py45
-rw-r--r--src/lib/Server/Plugins/Statistics.py4
-rw-r--r--src/lib/Server/Plugins/Svn2.py44
-rw-r--r--src/lib/Server/Plugins/TCheetah.py8
-rw-r--r--src/lib/Server/Plugins/TGenshi.py21
-rw-r--r--src/lib/Server/Reports/backends.py27
-rwxr-xr-xsrc/lib/Server/Reports/importscript.py138
-rwxr-xr-xsrc/lib/Server/Reports/manage.py2
-rw-r--r--src/lib/Server/Reports/nisauth.py19
-rw-r--r--src/lib/Server/Reports/reports/models.py108
-rw-r--r--src/lib/Server/Reports/reports/templates/base.html2
-rw-r--r--src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py20
-rw-r--r--src/lib/Server/Reports/reports/templatetags/syntax_coloring.py13
-rw-r--r--src/lib/Server/Reports/reports/views.py168
-rw-r--r--src/lib/Server/Reports/settings.py54
-rw-r--r--src/lib/Server/Reports/updatefix.py33
-rwxr-xr-xsrc/lib/Server/Reports/utils.py22
-rw-r--r--src/lib/Server/Snapshots/__init__.py5
-rw-r--r--src/lib/Server/Snapshots/model.py112
-rw-r--r--src/lib/Statistics.py2
-rwxr-xr-xsrc/sbin/bcfg229
-rwxr-xr-xsrc/sbin/bcfg2-admin10
-rwxr-xr-xsrc/sbin/bcfg2-build-reports62
-rwxr-xr-xsrc/sbin/bcfg2-info108
-rwxr-xr-xsrc/sbin/bcfg2-lint194
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep23
l---------[-rwxr-xr-x]src/sbin/bcfg2-repo-validate228
-rwxr-xr-xsrc/sbin/bcfg2-reports24
-rwxr-xr-xsrc/sbin/bcfg2-server3
-rw-r--r--testsuite/TestFrame.py29
-rw-r--r--testsuite/TestOptions.py22
-rw-r--r--testsuite/TestPlugin.py54
-rw-r--r--tools/basebuilder.py8
-rwxr-xr-xtools/batchadd.py66
-rw-r--r--tools/create-debian-pkglist-gp.py65
-rwxr-xr-xtools/create-debian-pkglist.py105
-rw-r--r--tools/create-rpm-pkglist.py32
-rwxr-xr-xtools/export.py27
-rwxr-xr-xtools/hostbase.py42
-rwxr-xr-xtools/hostbasepush.py10
-rwxr-xr-xtools/hostinfo.py79
-rwxr-xr-xtools/nagiosgen-convert.py75
-rwxr-xr-xtools/pkgmgr_gen.py254
-rwxr-xr-xtools/pkgmgr_update.py121
-rw-r--r--tools/rpmlisting.py96
-rw-r--r--tools/yum-listpkgs-xml.py55
185 files changed, 14729 insertions, 3214 deletions
diff --git a/AUTHORS b/AUTHORS
index dea48089c..4fe9da9ba 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -59,3 +59,6 @@ In chronological order:
which was used in the Bcfg2 client.
- Michael Jinks <mjinks@uchicago.edu> wrote the gentoo tool drivers.
+
+- Chris St. Pierre <stpierreca@ornl.gov> (re)wrote bcfg2-lint and has
+ made other miscellaneous contributions.
diff --git a/debian/changelog b/debian/changelog
index 61ea813b7..01e4478d0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+bcfg2 (1.2.0-0.0pre2) unstable; urgency=low
+
+ * New upstream release
+
+ -- Sol Jerome <sol.jerome@gmail.com> Mon, 25 Apr 2011 11:21:13 -0500
+
bcfg2 (1.2.0-0.0pre1) unstable; urgency=low
* New upstream release
diff --git a/debian/control b/debian/control
index 881052dd6..88e5071b6 100644
--- a/debian/control
+++ b/debian/control
@@ -21,7 +21,7 @@ Description: Configuration management client
Package: bcfg2-server
Architecture: all
-Depends: ${python:Depends}, ${misc:Depends}, python-lxml (>= 0.9), libxml2-utils (>= 2.6.23), lsb-base (>= 3.1-9), ucf, bcfg2 (= ${binary:Version}), openssl, python-ssl | python2.6 | python3.0 | python3.1 | python3.2, gamin | fam, python-gamin | python-fam, python-gamin | fam, gamin | python-fam
+Depends: ${python:Depends}, ${misc:Depends}, python-lxml (>= 0.9), libxml2-utils (>= 2.6.23), lsb-base (>= 3.1-9), ucf, bcfg2 (= ${binary:Version}), openssl, python-ssl | python2.6 | python3.0 | python3.1 | python3.2, python-gamin
XB-Python-Version: >= 2.4
Recommends: graphviz
Suggests: python-cheetah, python-genshi (>= 0.4.4), python-profiler, sqlalchemy (>= 0.5.0), python-django, mail-transport-agent
diff --git a/doc/appendix/guides/bootstrap.txt b/doc/appendix/guides/bootstrap.txt
new file mode 100644
index 000000000..b9b5f318e
--- /dev/null
+++ b/doc/appendix/guides/bootstrap.txt
@@ -0,0 +1,39 @@
+.. -*- mode: rst -*-
+
+.. _appendix-guides-bootstrap:
+
+=========
+Bootstrap
+=========
+
+Once you have your bcfg2 server setup and working, a common next step to
+take is automating the addition of new clients.
+
+The method for bootstrapping your clients will vary depending on your
+needs. The simplest way to go about this is to create a public default
+group in ``Metadata/groups.xml``. Example:
+
+.. code-block:: xml
+
+ <Group profile='true' name='basic' public='true' default='true'>
+
+This allows clients to freely associate themselves with this group so that
+you will not be required to manually add them to ``Metadata/clients.xml``
+prior to running the client.
+
+.. note::
+
+ Reverse DNS will need to work in order to automate the process of
+ bootstrapping clients without first adding them to
+ ``Metadata/clients.xml``.
+
+There are command line options available on the client which allow
+you to specify the options that are normally found in the client's
+``/etc/bcfg2.conf``::
+
+ bcfg2 -x password -p basic -S https://bcfg2-server:6789
+
+The above command will add the client to ``Metadata/clients.xml`` with the
+profile *basic*. Generally, the configuration given to the client by the
+bcfg2 server in this initial run will include the ``/etc/bcfg2.conf`` file
+so that the client won't need to specify these options on future runs.
diff --git a/doc/appendix/guides/centos.txt b/doc/appendix/guides/centos.txt
index d89d532ba..d788891de 100644
--- a/doc/appendix/guides/centos.txt
+++ b/doc/appendix/guides/centos.txt
@@ -211,13 +211,17 @@ line of ``bcfg2.conf``. Then create Packages layout (as per
</YUMSource>
</Sources>
-Due to the `Magic Groups`_, we need to modify our Metadata. Let's
-add a **centos5.4** group which inherits a **centos** group
-(this should replace the existing **redhat** group) present in
-``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file should look
-something like this
+Due to the :ref:`server-plugins-generators-packages-magic-groups`,
+we need to modify our Metadata. Let's add a **centos5.4** group which
+inherits a **centos** group (this should replace the existing **redhat**
+group) present in ``/var/lib/bcfg2/Metadata/groups.xml``. The resulting
+file should look something like this
-.. _Magic Groups: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Plugins/Packages#MagicGroups
+.. note::
+
+ The reason we are creating a release-specific group in this case is
+ that the YUMSource above is specific to the 5.4 release of centos.
+ That is, it should not apply to other releases (5.1, 5.3, etc).
.. code-block:: xml
@@ -240,7 +244,7 @@ something like this
.. note::
When editing your xml files by hand, it is useful to occasionally run
- `bcfg2-repo-validate` to ensure that your xml validates properly.
+ `bcfg2-lint` to ensure that your xml validates properly.
The final thing we need is for the client to have the proper
arch group membership. For this, we will make use of the
@@ -431,7 +435,7 @@ packages. Currently, the way to manage them is using :ref:`BoundEntries
.. code-block:: xml
<Bundle name='base-packages'>
- <BoundPackage name="gpg-pubkey" type="rpm">
+ <BoundPackage name="gpg-pubkey" type="rpm" version="foo">
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5" version="e8562897" release="459f07a4"/>
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL" version="217521f6" release="45e8a532"/>
</BoundPackage>
@@ -452,6 +456,10 @@ packages. Currently, the way to manage them is using :ref:`BoundEntries
<Package name='yum'/>
</Bundle>
+.. note::
+
+ version="foo" is just a dummy attribute for the gpg-pubkey Package
+
To actually push the gpg keys out via Bcfg2, you will need to manage the
files as well. This can be done by adding Path entries for each of the
gpg keys you want to manage
@@ -461,7 +469,7 @@ gpg keys you want to manage
<Bundle name='base-packages'>
<Path name='/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5'/>
<Path name='/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL'/>
- <BoundPackage name="gpg-pubkey" type="rpm">
+ <BoundPackage name="gpg-pubkey" type="rpm" version="foo">
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5" version="e8562897" release="459f07a4"/>
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL" version="217521f6" release="45e8a532"/>
</BoundPackage>
@@ -578,3 +586,8 @@ Dynamic (web) reports
=====================
See installation instructions at :ref:`reports-dynamic`
+
+Next Steps
+==========
+
+:ref:`getting_started-index-next-steps`
diff --git a/doc/appendix/guides/converging_rhel5.txt b/doc/appendix/guides/converging_rhel5.txt
index 7581d307f..d6a9d2d1c 100644
--- a/doc/appendix/guides/converging_rhel5.txt
+++ b/doc/appendix/guides/converging_rhel5.txt
@@ -81,7 +81,7 @@ For a "Package"
<Bundle name='keys'>
<!-- GPG keys -->
- <BoundPackage name="gpg-pubkey" type="yum">
+ <BoundPackage name="gpg-pubkey" type="rpm" version="foo">
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL" version="217521f6" release="45e8a532"/>
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" version="37017186" release="45761324"/>
</BoundPackage>
diff --git a/doc/appendix/guides/fedora.txt b/doc/appendix/guides/fedora.txt
index 4e3244eaa..db23e94bf 100644
--- a/doc/appendix/guides/fedora.txt
+++ b/doc/appendix/guides/fedora.txt
@@ -216,13 +216,18 @@ mirror near your location according the `Mirror list`_ .
</YUMSource>
</Sources>
-.. _Magic Groups: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Plugins/Packages#MagicGroups
-Due to the `Magic Groups`_, we need to modify our Metadata. Let's
-add a **fedora13** group which inherits a **fedora** group
-(this should replace the existing **redhat** group) present in
-``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file should look
-something like this
+Due to the :ref:`server-plugins-generators-packages-magic-groups`,
+we need to modify our Metadata. Let's add a **fedora13** group which
+inherits a **fedora** group (this should replace the existing **redhat**
+group) present in ``/var/lib/bcfg2/Metadata/groups.xml``. The resulting
+file should look something like this
+
+.. note::
+
+ The reason we are creating a release-specific group in this case is
+ that the YUMSource above is specific to the 13th release of fedora.
+ That is, it should not apply to other releases (14, 15, etc).
.. code-block:: xml
@@ -244,8 +249,7 @@ something like this
.. note::
When editing your xml files by hand, it is useful to occasionally
- run ``bcfg2-repo-validate`` to ensure that your xml validates
- properly.
+ run ``bcfg2-lint`` to ensure that your xml validates properly.
Add a probe
+++++++++++
@@ -339,7 +343,7 @@ Bundle now looks like this
.. code-block:: xml
<Bundle name='base-packages'>
- <BoundPackage name="gpg-pubkey" type="rpm">
+ <BoundPackage name="gpg-pubkey" type="rpm" version="foo">
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5" version="e8562897" release="459f07a4"/>
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL" version="217521f6" release="45e8a532"/>
</BoundPackage>
@@ -360,6 +364,10 @@ Bundle now looks like this
<Package name='yum'/>
</Bundle>
+.. note::
+
+ version="foo" is just a dummy attribute for the gpg-pubkey Package
+
To actually push the gpg keys out via Bcfg2, you will need to manage
the files as well. This can be done by adding Path entries for each
of the gpg keys you want to manage
@@ -369,7 +377,7 @@ of the gpg keys you want to manage
<Bundle name='base-packages'>
<Path name='/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5'/>
<Path name='/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL'/>
- <BoundPackage name="gpg-pubkey" type="rpm">
+ <BoundPackage name="gpg-pubkey" type="rpm" version="foo">
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5" version="e8562897" release="459f07a4"/>
<Instance simplefile="/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL" version="217521f6" release="45e8a532"/>
</BoundPackage>
diff --git a/doc/appendix/guides/import-existing-ssh-keys.txt b/doc/appendix/guides/import-existing-ssh-keys.txt
index e606ba908..64a1b62cd 100644
--- a/doc/appendix/guides/import-existing-ssh-keys.txt
+++ b/doc/appendix/guides/import-existing-ssh-keys.txt
@@ -46,7 +46,7 @@ Validate your repository
Validation can be performed using the following command::
- bcfg2-repo-validate -v
+ bcfg2-lint
Run the bcfg2 client
====================
diff --git a/doc/appendix/guides/ubuntu.txt b/doc/appendix/guides/ubuntu.txt
index 24338c6d2..a597a5492 100644
--- a/doc/appendix/guides/ubuntu.txt
+++ b/doc/appendix/guides/ubuntu.txt
@@ -167,14 +167,39 @@ Create Packages layout (as per :ref:`packages-exampleusage`) in
<Arch>amd64</Arch>
<Arch>i386</Arch>
</APTSource>
+ <APTSource>
+ <Group>lucid</Group>
+ <URL>http://archive.ubuntu.com/ubuntu</URL>
+ <Version>lucid-updates</Version>
+ <Component>main</Component>
+ <Component>multiverse</Component>
+ <Component>restricted</Component>
+ <Component>universe</Component>
+ <Arch>amd64</Arch>
+ </APTSource>
+ <APTSource>
+ <Group>lucid</Group>
+ <URL>http://security.ubuntu.com/ubuntu</URL>
+ <Version>lucid-security</Version>
+ <Component>main</Component>
+ <Component>multiverse</Component>
+ <Component>restricted</Component>
+ <Component>universe</Component>
+ <Arch>amd64</Arch>
+ </APTSource>
</Sources>
-Due to the `Magic Groups`_, we need to modify our Metadata. Let's add
-an **ubuntu-lucid** group which inherits the **ubuntu** group already
-present in ``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file
-should look something like this
+Due to the :ref:`server-plugins-generators-packages-magic-groups`,
+we need to modify our Metadata. Let's add an **ubuntu-lucid**
+group which inherits the **ubuntu** group already present in
+``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file should look
+something like this
+
+.. note::
-.. _Magic Groups: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Plugins/Packages#MagicGroups
+ The reason we are creating a release-specific group in this case is
+ that the APTSource above is specific to the lucid release of ubuntu.
+ That is, it should not apply to other releases (hardy, maverick, etc).
.. code-block:: xml
@@ -197,7 +222,7 @@ should look something like this
.. note::
When editing your xml files by hand, it is useful to occasionally run
- `bcfg2-repo-validate` to ensure that your xml validates properly.
+ `bcfg2-lint` to ensure that your xml validates properly.
The last thing we need is for the client to have the proper
arch group membership. For this, we will make use of the
@@ -481,13 +506,8 @@ Now we run the client and see there are no more unmanaged entries! ::
managed. Please see :ref:`unsorted-writing_specification` for more
details.
-Dynamic (web) reports
-=====================
-
-See installation instructions at :ref:`appendix-guides-web-reports-install`
-
Upstart
-=======
+^^^^^^^
Upstart services are defined like this:
@@ -501,3 +521,13 @@ Some Upstart services require additional parameters, like network-interface and
<Service name="network-interface" status="on" type="upstart" parameters="INTERFACE=eth0"/>
<Service name="bridge-network-interface" status="on" type="upstart" parameters="INTERFACE=br0"/>
+
+Dynamic (web) reports
+=====================
+
+See installation instructions at :ref:`appendix-guides-web-reports-install`
+
+Next Steps
+==========
+
+:ref:`getting_started-index-next-steps`
diff --git a/doc/client/metadata.txt b/doc/client/metadata.txt
index 2a19f3a1a..27870ba9a 100644
--- a/doc/client/metadata.txt
+++ b/doc/client/metadata.txt
@@ -22,22 +22,23 @@ interaction:
* Statistics Upload
This construction process spans several server plugins. The
-:ref:`server-plugins-grouping-metadata` is responsible for initial
-instance creation, including the client hostname, profile, and basic
-group memberships. After this initial creation, Connector plugins (such as
-:ref:`server-plugins-probes-index` or :ref:`server-plugins-properties`)
-can add additional group memberships for clients. These memberships
-are merged into the instance; that is, the new group memberships are
-treated as if they were included in groups.xml. If any of these groups
-are defined in groups.xml, then groups included there are included in
-the ClientMetadata instance group list. At the end of this process, the
-ClientMetadata instance has its complete set of group memberships. At this
-point, each connector plugin has the opportunity to return an additional
-object which will be placed in an attribute corresponding to the Connector
-name. For example, the Probes plugin returns a dictionary of probe name
-to probe result mappings for the client. This dictionary is available as
-the "Probes" attribute. With this, ClientMetadata resolution is complete,
-and the ClientMetadata instance can be used by the rest of the system.
+:ref:`server-plugins-grouping-metadata` is responsible for
+initial instance creation, including the client hostname,
+profile, and basic group memberships. After this initial creation,
+Connector plugins (such as :ref:`server-plugins-probes-index` or
+:ref:`server-plugins-connectors-properties`) can add additional group
+memberships for clients. These memberships are merged into the instance;
+that is, the new group memberships are treated as if they were included
+in groups.xml. If any of these groups are defined in groups.xml,
+then groups included there are included in the ClientMetadata instance
+group list. At the end of this process, the ClientMetadata instance has
+its complete set of group memberships. At this point, each connector
+plugin has the opportunity to return an additional object which will be
+placed in an attribute corresponding to the Connector name. For example,
+the Probes plugin returns a dictionary of probe name to probe result
+mappings for the client. This dictionary is available as the "Probes"
+attribute. With this, ClientMetadata resolution is complete, and the
+ClientMetadata instance can be used by the rest of the system.
Contents
========
diff --git a/doc/client/tools.txt b/doc/client/tools.txt
index d9a9b76f4..bedcb9ab9 100644
--- a/doc/client/tools.txt
+++ b/doc/client/tools.txt
@@ -57,11 +57,15 @@ Chkconfig
Tool to manage services (primarily on Redhat based distros).
-.. note:: Start and stop are standard arguments, but the one for reload
- isn't consistent across services. You can specify which argument
- to use with the `restart` property in Service tags. Example:
- ``<Service name="ftp" restart="condrestart" status="on"
- type="chkconfig">``
+.. note::
+
+ Start and stop are standard arguments, but the one for reload
+ isn't consistent across services. You can specify which argument
+ to use with the ``target`` attribute in Service tags. Example:
+
+ .. code-block:: xml
+
+ <Service name="ftp" target="condrestart" status="on" type="chkconfig"/>
DebInit
-------
@@ -89,9 +93,13 @@ launchd
Mac OS X Services. To use this tool, you must maintain a standard launch
daemon .plist file in ``/Library/LaunchDaemons/`` (example ssh.plist)
-and setup a ``<Service name="com.openssh.sshd" type="launchd" status="on"
-/>`` entry in your config to load or unload the service. Note the name
-is the ''Label'' specified inside of the .plist file
+and setup an entry in your config to load or unload the service.
+
+.. code-block:: xml
+
+ <Service name="com.openssh.sshd" type="launchd" status="on"/>
+
+Note the name is the *Label* specified inside of the .plist file
Portage
-------
@@ -133,7 +141,18 @@ Example legacy run service (lrc):
.. code-block:: xml
- <BoundService name='/etc/rc2_d/S47pppd' FMRI='lrc:/etc/rc2_d/S47pppd' status='off' type='smf'/>
+ <Service name='/etc/rc2_d/S47pppd' FMRI='lrc:/etc/rc2_d/S47pppd' status='off' type='smf'/>
+
+Systemd
+-------
+
+Systemd service support.
+
+Example:
+
+.. code-block:: xml
+
+ <Service name='udev' status='on' type='systemd'/>
SYSV
----
diff --git a/doc/client/tools/vcs.txt b/doc/client/tools/vcs.txt
new file mode 100644
index 000000000..fb9c33684
--- /dev/null
+++ b/doc/client/tools/vcs.txt
@@ -0,0 +1,15 @@
+.. -*- mode: rst -*-
+
+.. _client-tools-vcs:
+
+===============
+VCS Client Tool
+===============
+
+.. warning: This tool is currently under development.
+
+.. note: Currently, the only supported VCS is git.
+
+The VCS tool allows you to checkout particular revisions from a VCS
+repository on the client to a specified path. The tool requires the
+appropriate python libraries for the VCS used to be installed.
diff --git a/doc/client/tools/yumng.txt b/doc/client/tools/yumng.txt
index dd712ddec..c2e9161a1 100644
--- a/doc/client/tools/yumng.txt
+++ b/doc/client/tools/yumng.txt
@@ -18,9 +18,15 @@ and SUSE based distributions.
Examples of this are:
-* SLES10 and openSUSE 10.2 both install six GPG keys. From an RPM perspective this means that there are six packages with the name gpg-pubkey.
-* YUM always installs, as opposed to upgrades, kernel packages. This is hard coded in YUM (actually it can be overridden in yum.conf), so systems using YUM will eventually have multiple kernel packages installed.
-* Red Hat family x86_64 based systems frequently have both an x86_64 and an i386 version of the same package installed.
+* SLES10 and openSUSE 10.2 both install six GPG keys. From an RPM
+ perspective this means that there are six packages with the name
+ gpg-pubkey.
+* YUM always installs, as opposed to upgrades, kernel packages. This is
+ hard coded in YUM (actually it can be overridden in yum.conf),
+ so systems using YUM will eventually have multiple kernel packages
+ installed.
+* Red Hat family x86_64 based systems frequently have both an x86_64
+ and an i386 version of the same package installed.
The new Pkgmgr format files with Instances are therefore the only way to
accurately describe an RPM based system. It is recommended that all RPM
@@ -50,7 +56,9 @@ Features
* Support for per instance ignoring of individual files for the RPM verification with the Ignore tag.
* Multiple package Instances with full version information listed in interactive mode.
* Support for installation and removal of gpg-pubkey packages.
-* Support for controlling what action is taken on package verification failure with the install_action, version_fail_action and verify_fail_action attributes.
+* Support for controlling what action is taken on package
+ verification failure with the install_action, version_fail_action and
+ verify_fail_action attributes.
RPMng Driver Overview
diff --git a/doc/development/client-driver.txt b/doc/development/client-driver.txt
index cc065dd32..32bb0aff4 100644
--- a/doc/development/client-driver.txt
+++ b/doc/development/client-driver.txt
@@ -10,7 +10,6 @@ driver for a configuration element type. The included example describes
an existing driver, and the process that was used to create it.
#. Pick a name for the driver. In this case, we picked the name RPM.
-#. Add "RPM" to the ``__all__`` list in ``src/lib/Client/Tools/__init__.py``
#. Create a file in ``src/lib/Client/Tools`` with the same name (RPM.py)
#. Create a class in this file with the same name (class RPM)
diff --git a/doc/development/docstyleguide.txt b/doc/development/docstyleguide.txt
index fda1037f3..59db58362 100644
--- a/doc/development/docstyleguide.txt
+++ b/doc/development/docstyleguide.txt
@@ -25,3 +25,9 @@ Basics
When used alone this refers to a Bcfg2 :term:`repository`. When there
is a chance for confusion, for instance in documents also talking
about :term:`VCS`, be sure to use the longer Bcfg2 :term:`repository`.
+
+Sections
+--------
+
+Unless necessary, all the documentation follows the sections header
+rules available at http://docs.python.org/documenting/rest.html#sections.
diff --git a/doc/development/plugins.txt b/doc/development/plugins.txt
index 709b9fcec..4c723f7e9 100644
--- a/doc/development/plugins.txt
+++ b/doc/development/plugins.txt
@@ -139,7 +139,7 @@ Example Plugin
__version__ = '1'
__author__ = 'me@me.com'
__rmi__ = ['myfunction']
- # myfunction is not available remotely as MyPlugin.myfunction
+ # myfunction is now available remotely as MyPlugin.myfunction
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
@@ -206,8 +206,8 @@ do so. We will call our new plugin `MyMetadata`.
__version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
- def __init__(self, core, datastore, watch_clients=True):
- Bcfg2.Server.Plugins.Metadata.Metadata.__init__(self, core, datastore, watch_clients)
+ def __init__(self, core, datastore, watch_clients=True):
+ Bcfg2.Server.Plugins.Metadata.Metadata.__init__(self, core, datastore, watch_clients)
#. Add MyMetadata to ``src/lib/Server/Plugins/__init__.py``
#. Replace Metadata with MyMetadata in the plugins line of bcfg2.conf
diff --git a/doc/getting_started/index.txt b/doc/getting_started/index.txt
index b256edd2d..65ac296f7 100644
--- a/doc/getting_started/index.txt
+++ b/doc/getting_started/index.txt
@@ -223,11 +223,13 @@ you will find that we now have a correct entry::
Done! Now we just have 242 (or more) entries to take care of!
-:ref:`server-plugins-structures-bundler-index` is a relatively easy
-directory to populate. You can find many samples of Bundles in the
-`Bundle Repository`_, many of which can be used without editing.
+:ref:`server-plugins-structures-bundler-index` is a
+relatively easy directory to populate. You can find many
+samples of Bundles in the :ref:`Bundler Example Repository
+<server-plugins-structures-bundler-index-examples>`, many of which can
+be used without editing.
-.. _Bundle Repository: http://docs.bcfg2.org/server/plugins/structures/bundler/index.html#other-examples
+.. _getting_started-index-next-steps:
Next Steps
==========
@@ -247,6 +249,9 @@ Run ``bcfg2-info``, and type help and the prompt when it comes up.
``bcfg2-admin`` can perform a variety of repository maintenance
tasks. Run ``bcfg2-admin`` help for details.
+Once you have the server setup, you may be interested in
+:ref:`bootstrapping <appendix-guides-bootstrap>` additional clients.
+
Platform-specific Quickstart Notes
==================================
diff --git a/doc/help/faq/general.txt b/doc/help/faq/general.txt
index f8ecc9854..ba5d1fda3 100644
--- a/doc/help/faq/general.txt
+++ b/doc/help/faq/general.txt
@@ -29,7 +29,7 @@ exact list of platforms on which Bcfg2 works.
**What pre-requisites are needed to run Bcfg2?**
-Please visit the :ref:`prerequisites` section in the manual.
+Please visit the :ref:`installation-prerequisites` section in the manual.
**Why won't bcfg2-server start?**
@@ -51,6 +51,6 @@ The bcfg2-server process logs to syslog facility LOG_DAEMON. The server produces
**Is there a way to check if all repository XML files conform to schemas?**
Bcfg2 comes with XML schemas describing all of the XML formats used in
-the server repository. A validation command ``bcfg2-repo-validate`` is
-included with the source distribution and all packages. Run it with
-the ``-v`` flag to see each file and the results if its validation.
+the server repository. A validation command ``bcfg2-lint`` is
+included with the source distribution and all packages. ``bcfg2-lint``
+can also performs lots of other checks for common mistakes.
diff --git a/doc/help/troubleshooting.txt b/doc/help/troubleshooting.txt
index 0892587aa..88a0febc5 100644
--- a/doc/help/troubleshooting.txt
+++ b/doc/help/troubleshooting.txt
@@ -38,9 +38,8 @@ Check if all repository XML files conform to schemas
====================================================
Bcfg2 comes with XML schemas describing all of the XML formats used in
-the server repository. A validation command ``bcfg2-repo-validate`` is
-included with the source distribution and all packages. Run it with the
--v flag to see each file and the results if its validation.
+the server repository. A validation command ``bcfg2-lint`` is
+included with the source distribution and all packages.
If the bcfg2 server is not reflecting recent changes, try restarting the bcfg2-server process
=============================================================================================
@@ -139,11 +138,27 @@ be taken to remedy them.
| | | associated with a | |
| | | non-profile group. | |
+------------------------------+----------+---------------------+--------------+
+| Failed to decode <filename> | Server | The encoding being | [10]_ |
+| Please verify you are using | | used is unable to | |
+| the proper encoding | | decode the | |
+| | | character present | |
+| | | in this file. | |
++------------------------------+----------+---------------------+--------------+
+| Got unknown entries | Server | The Packages plugin | [11]_ |
+| [list of unknown entries] | | has no knowledge of | |
+| | | the listed entries | |
++------------------------------+----------+---------------------+--------------+
+| Failed to import lxml | Server | The server cannot | [12]_ |
+| dependency. Shutting | | import lxml | |
+| down server. | | | |
++------------------------------+----------+---------------------+--------------+
.. [1] This entry is not being bound. Ensure that a version of this
entry applies to this client.
-.. [2] Add a type to the generator definition for this entry
+.. [2] It is possible that the type attribute for this generator entry
+ is undefined. You may need to add the appropriate type attribute
+ in the literal entry specification.
.. [3] Copy the Bcfg2 server's CA certificate to the client and specify it
using the **ca** option in the [communication] section of
``bcfg2.conf``
@@ -158,6 +173,18 @@ be taken to remedy them.
.. [8] Ensure that the file is properly formed XML.
.. [9] Fix hostname resolution for the client or ensure that the profile
group is properly setup.
+.. [10] Ensure the correct encoding is specified in the [components]
+ section of ``bcfg2.conf``.
+.. [11] For packages listed other than **gpg-pubkey**, this error means
+ that the Packages plugin is unable to find the package in any of
+ the sources listed in ``Packages/config.xml``. The issue often
+ arises when the client is not in one of the groups necessary for
+ the Source listed. In the case of gpg-pubkey, you can safely
+ ignore the message as the Packages plugin has no knowledge of
+ these packages (however, note that this package is most often
+ specified as a BoundPackage entry).
+.. [12] Ensure that you have installed all the necessary
+ :ref:`installation-prerequisites`.
FAQs
====
@@ -172,10 +199,12 @@ why.
Why am I getting a traceback?
-----------------------------
-If you get a traceback, please let us know by :ref:`reporting it
-<report-a-bug>` on Trac, via the mailing list, or on IRC. Your best bet
+If you get a traceback, please let us know by reporting it
+on `Trac ticket tracker`_, via the mailing list, or on IRC. Your best bet
to get a quick response will be to jump on IRC during the daytime (CST).
+.. _Trac ticket tracker: http://bcfg2.org
+
What is the most common cause of "The following entries are not handled by any tool"?
-------------------------------------------------------------------------------------
diff --git a/doc/installation/prerequisites.txt b/doc/installation/prerequisites.txt
index c86e86774..0cb721bb9 100644
--- a/doc/installation/prerequisites.txt
+++ b/doc/installation/prerequisites.txt
@@ -1,6 +1,6 @@
.. -*- mode: rst -*-
-.. _prerequisites:
+.. _installation-prerequisites:
Prerequisites
=============
@@ -21,7 +21,7 @@ Bcfg2 Client
+----------------------------+------------------------+--------------------------------+
| libxslt (if lxml is used) | Any | libxml2 |
+----------------------------+------------------------+--------------------------------+
-| python | 2.3-2.4, 2.5-2.7 [#f1] | |
+| python | 2.4 and greater [#f1] | |
+----------------------------+------------------------+--------------------------------+
| lxml or elementtree [#f2]_ | Any | lxml: libxml2, libxslt, python |
+----------------------------+------------------------+--------------------------------+
@@ -39,20 +39,23 @@ Bcfg2 Client
Bcfg2 Server
------------
-+----------------------------+----------+--------------------------------+
-| Software | Version | Requires |
-+============================+==========+================================+
-| libxml2 | 2.6.24+ | |
-+----------------------------+----------+--------------------------------+
-| libxslt | Any | libxml2 |
-+----------------------------+----------+--------------------------------+
-| python | 2.2-2.7 | |
-+----------------------------+----------+--------------------------------+
-| lxml | 0.9+ | lxml: libxml2, libxslt, python |
-+----------------------------+----------+--------------------------------+
-| gamin or fam | Any | |
-+----------------------------+----------+--------------------------------+
-| python-gamin or python-fam | Any | gamin or fam, python |
-+----------------------------+----------+--------------------------------+
-| M2crypto | Any | python, openssl |
-+----------------------------+----------+--------------------------------+
++-------------------------------+----------+--------------------------------+
+| Software | Version | Requires |
++===============================+==========+================================+
+| libxml2 | 2.6.24+ | |
++-------------------------------+----------+--------------------------------+
+| libxslt | Any | libxml2 |
++-------------------------------+----------+--------------------------------+
+| python | 2.2-2.7 | |
++-------------------------------+----------+--------------------------------+
+| lxml | 0.9+ | lxml: libxml2, libxslt, python |
++-------------------------------+----------+--------------------------------+
+| gamin or fam | Any | |
++-------------------------------+----------+--------------------------------+
+| python-gamin or python-fam | Any | gamin or fam, python |
++-------------------------------+----------+--------------------------------+
+| M2crypto or python-ssl (note | Any | python, openssl |
+| that the ssl module is | | |
+| included in python versions | | |
+| 2.6 and later | | |
++-------------------------------+----------+--------------------------------+
diff --git a/doc/server/configurationentries.txt b/doc/server/configurationentries.txt
index 6e6ab79f7..5602da189 100644
--- a/doc/server/configurationentries.txt
+++ b/doc/server/configurationentries.txt
@@ -31,7 +31,7 @@ Non-POSIX entries
+-------------+---------------------+-----------------------------+
| PostInstall | PostInstall command | name |
+-------------+---------------------+-----------------------------+
-| Service | System Services | name, type, status, reload |
+| Service | System Services | name, type, status, target |
+-------------+---------------------+-----------------------------+
POSIX entries
@@ -50,9 +50,9 @@ plugin that handles the entry in the case of `Cfg`_, `TGenshi`_, or
a device, directory, hardlink, symlink, etc), then you will specify both
the *type* and any other necessary attributes in `Rules`_.
-Running ``bcfg2-repo-validate`` will check your configuration
-specification for the presence of any mandatory attributes that are
-necessary for the Path type specified.
+Running ``bcfg2-lint`` will check your configuration specification for
+the presence of any mandatory attributes that are necessary for the
+Path type specified.
.. note:: A tool for converting old POSIX entries is available in the
Bcfg2 source directory at tools/posixunified.py
@@ -95,6 +95,10 @@ necessary for the Path type specified.
| | entries | POSIX entities | group, perms |
| | | | |
+-------------+----------------------+-----------------+--------------------------+
+| vcs | New | Create version | vcstype (git), |
+| | | control | sourceurl, revision |
+| | | checkout | |
++-------------+----------------------+-----------------+--------------------------+
Keep in mind that permissions for files served up by Cfg/TGenshi/TCheetah
are still handled via the traditional :ref:`server-info` mechanisms.
diff --git a/doc/server/plugins/connectors/properties.txt b/doc/server/plugins/connectors/properties.txt
index ae8bf0caa..1cbc4cf65 100644
--- a/doc/server/plugins/connectors/properties.txt
+++ b/doc/server/plugins/connectors/properties.txt
@@ -1,6 +1,6 @@
.. -*- mode: rst -*-
-.. _server-plugins-properties:
+.. _server-plugins-connectors-properties:
==========
Properties
@@ -24,17 +24,37 @@ Properties adds a new dictionary to client metadata instances that maps
property file names to PropertyFile instances. PropertyFile instances
contain parsed XML data as the "data" attribute.
+The XML data in a property file is arbitrary, but a matching ``.xsd``
+file can be created to assign a schema to a property file, which will
+be checked when running ``bcfg2-lint``. For instance, given::
+
+ Properties/dns-config.xml
+ Properties/dns-config.xsd
+
+``dns-config.xml`` will be validated against ``dns-config.xsd``.
+
Usage
=====
Specific property files can be referred to in
-templates as metadata.Properties[<filename>]. The
+templates as ``metadata.Properties[<filename>]``. The
data attribute is an LXML element object. (Documented
`here <http://codespeak.net/lxml/tutorial.html#the-element-class>`_)
-Currently, no access methods are defined for this data, but as we
-formulate common use cases, we will add them to the !PropertyFile class
-as methods. This will simplify templates.
+Currently, only one access method is defined for this data, ``Match``.
+``Match`` parses the Group and Client tags in the file and returns a
+list of elements that apply to the client described by a set of
+metadata. (See :ref:`server-plugins-structures-bundler-index` for
+more details on how Group and Client tags are parsed.) For instance::
+
+ {% python
+ ntp_servers = [el.text
+ for el in metadata.Properties['ntp.xml'].Match(metadata):
+ if el.tag == "Server"]
+ %}
+
+As we formulate more common use cases, we will add them to the
+!PropertyFile class as methods. This will simplify templates.
Accessing Properties contents from TGenshi
==========================================
diff --git a/doc/server/plugins/generators/cfg.txt b/doc/server/plugins/generators/cfg.txt
index 8b40eec67..612b14bec 100644
--- a/doc/server/plugins/generators/cfg.txt
+++ b/doc/server/plugins/generators/cfg.txt
@@ -24,12 +24,13 @@ the files on your clients, starting at the root level. For example::
bin/ boot/ etc/ opt/ root/ usr/ var/
Specific config files go in like-named directories in this
-heirarchy. For example the password file, ``/etc/passwd``, goes in
-``Cfg/etc/passwd/passwd``, while the ssh pam module config file,
-``/etc/pam.d/sshd``, goes in ``Cfg/etc/pam.d/sshd/sshd``. The reason
-for the like-name directory is to allow multiple versions of each file
-to exist, as described below. Note that these files are exact copies of
-what will appear on the client machine - no templates, XML wrappers, etc.
+heirarchy. For example the password file, ``/etc/passwd``, goes
+in ``Cfg/etc/passwd/passwd``, while the ssh pam module config file,
+``/etc/pam.d/sshd``, goes in ``Cfg/etc/pam.d/sshd/sshd``. The reason for
+the like-name directory is to allow multiple versions of each file to
+exist, as described below. Note that these files are exact copies of what
+will appear on the client machine (except when using genshi templating --
+see below).
Group-Specific Files
====================
@@ -157,6 +158,17 @@ will be processed using the new template style (like .newtxt in the TGenshi
plugin). Templates can be host and group specific as well. Deltas will not
be processed for any genshi base file.
+.. note::
+
+ If you are using genshi templating in combination with host-specific
+ or group-specific files, you will need to ensure that the ``.genshi``
+ extensions is at the **end** of the filename. Using the examples
+ from above for *host.example.com* and group *server* you would have
+ the following::
+
+ Cfg/etc/fstab/fstab.H_host.example.com.genshi
+ Cfg/etc/fstab/fstab.G50_server.genshi
+
File permissions
================
diff --git a/doc/server/plugins/generators/nagiosgen.txt b/doc/server/plugins/generators/nagiosgen.txt
index 196f1e76b..b839c10ca 100644
--- a/doc/server/plugins/generators/nagiosgen.txt
+++ b/doc/server/plugins/generators/nagiosgen.txt
@@ -169,3 +169,37 @@ Note that some of these files are built on demand, each time a client
in group "nagios-server" checks in with the Bcfg2 server. Local nagios
instances can be configured to use the NagiosGen directory in the Bcfg2
repository directly.
+
+Fine-Grained Configuration
+==========================
+
+NagiosGen can be configured in excruciating detail by editing
+``NagiosGen/config.xml``, which will let you set individual Nagios
+options for hosts or groups. E.g.:
+
+.. code-block:: xml
+
+ <NagiosGen>
+ <Group name="datacenter-2">
+ <Option name="parents">dc-2-switch</Option>
+ <Group>
+ <Group name="non-production">
+ <Option name="notification_period">workhours</Option>
+ <Option name="notification_options">d</Option>
+ </Group>
+ <Client name="foo.example.com">
+ <Option name="max_check_attempts">10</Option>
+ </Client>
+ </NagiosGen>
+
+Obviously the sort of fine-grained control you get from this overlaps
+to some degree with Nagios' own templating, so use it wisely and in
+moderation.
+
+``NagiosGen/config.xml`` replaces the files
+``Properties/NagiosGen.xml`` and ``NagiosGen/parents.xml`` in older
+versions of Bcfg2; your old configs can be migrated using the
+``nagiosgen-convert.py`` tool. The plugin does contain a
+backwards-compatibility layer for those older config files, but
+``NagiosGen/config.xml`` must exist (even if empty) for the plugin to
+function.
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index d1a68b16e..90cd19de4 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -15,6 +15,8 @@ information, Packages delegates control of package version information to
the underlying package manager, installing the latest version available
through those channels.
+.. _server-plugins-generators-packages-magic-groups:
+
"Magic Groups"
==============
@@ -152,14 +154,15 @@ like this:
</Sources>
.. note::
+
The default behavior of the Packages plugin is to not make
any assumptions about which packages you want to have added
- automatically. For that reason, neither **Recommended**
- nor **Suggested** packages are added as dependencies by
- default. You will notice that the default behavior for apt is
- to add Recommended packages as dependencies. You can configure
- the Packages plugin to add recommended packages by adding
- the following line immediately following the URL:
+ automatically. For that reason, neither **Recommended** nor
+ **Suggested** packages are added as dependencies by default. You
+ will notice that the default behavior for apt is to add Recommended
+ packages as dependencies. You can configure the Packages plugin to
+ add recommended packages by adding the following line immediately
+ following the **URL**:
.. versionadded:: 1.1.0
@@ -184,6 +187,8 @@ Yum sources can be similarly specified:
</YUMSource>
</Sources>
+For sources with a **URL** tag, the **Version** tag is also necessary.
+
.. note:: There is also a RawURL syntax for specifying APT or YUM sources that
don't follow the conventional layout:
@@ -268,7 +273,7 @@ Validation
==========
A schema for Packages/config.xml is included; config.xml can be validated
-using ``bcfg2-repo-validate``.
+using ``bcfg2-lint``.
.. note:: The schema requires that elements be specified in the above order.
diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt
index cff78a8ee..a2953ad08 100644
--- a/doc/server/plugins/generators/rules.txt
+++ b/doc/server/plugins/generators/rules.txt
@@ -116,34 +116,28 @@ See :ref:`client-tools-actions`
Service Tag
-----------
-+------------+--------------------------+---------------------------------------+
-| Name | Description | Values |
-+============+==========================+=======================================+
-| mode | Per Service Mode (New in | (manual|default|supervised) |
-| | 1.0) | |
-+------------+--------------------------+---------------------------------------+
-| name | Service Name | String |
-+------------+--------------------------+---------------------------------------+
-| status | Should the service be | (on|off) |
-| | on or off (default: | |
-| | off). | |
-+------------+--------------------------+---------------------------------------+
-| target | Service command for | String |
-| | restart (default: | |
-| | restart) | |
-+------------+--------------------------+---------------------------------------+
-| type | Driver to use on the | (chkconfig|deb|rc-update|smf|upstart) |
-| | client to manage this | |
-| | service. | |
-+------------+--------------------------+---------------------------------------+
-| sequence | Order for service | integer |
-| | startup (debian services | |
-| | only) | |
-+------------+--------------------------+---------------------------------------+
-| parameters | Pass parameters to | String |
-| | service (Upstart | |
-| | services only) | |
-+------------+--------------------------+---------------------------------------+
++------------+-------------------------------+-----------------------------------------------+
+| Name | Description | Values |
++============+===============================+===============================================+
+| mode | Per Service Mode (New in 1.0) | (manual | default | supervised) |
++------------+-------------------------------+-----------------------------------------------+
+| name | Service Name | String |
++------------+-------------------------------+-----------------------------------------------+
+| status | Should the service be on or | (on | off | ignore) |
+| | off (default: off). | |
++------------+-------------------------------+-----------------------------------------------+
+| target | Service command for restart | String |
+| | (default: restart) | |
++------------+-------------------------------+-----------------------------------------------+
+| type | Driver to use on the client | (chkconfig | deb | rc-update | smf | upstart) |
+| | to manage this service. | |
++------------+-------------------------------+-----------------------------------------------+
+| sequence | Order for service startup | integer |
+| | (debian services only) | |
++------------+-------------------------------+-----------------------------------------------+
+| parameters | Pass parameters to service | String |
+| | (Upstart services only) | |
++------------+-------------------------------+-----------------------------------------------+
Service mode descriptions
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -153,6 +147,12 @@ Service mode descriptions
* manual
* do not start/stop/restart this service
+ * service installation is not performed
+
+* interactive_only
+
+ * only attempt to start/stop/restart this service if the client is run interactively
+ * service installation is performed
* default
@@ -163,6 +163,21 @@ Service mode descriptions
* default and ensure service is running (or stopped) when verification is performed
* deprecates supervised='true'
+Service status descriptions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* on
+
+ * start the service at boot time
+
+* off
+
+ * don't start the service at boot time
+
+* ignore
+
+ * don't check the status, leave it as-is (valid for deb and upstart services only)
+
Client Tag
----------
diff --git a/doc/server/plugins/generators/sslca.txt b/doc/server/plugins/generators/sslca.txt
index ebc625e11..c91905d78 100644
--- a/doc/server/plugins/generators/sslca.txt
+++ b/doc/server/plugins/generators/sslca.txt
@@ -34,39 +34,39 @@ must contain full (not relative) paths.
server -- This enabled the SSLCA plugin on the Bcfg2 server.
#. Add a section to your ``/etc/bcfg2.conf`` called sslca_foo, replacing foo
-with the name you wish to give your CA so you can reference it in certificate
-definitions.
+ with the name you wish to give your CA so you can reference it in certificate
+ definitions.
#. Under that section, add an entry for ``config`` that gives the location of
-the openssl configuration file for your CA.
+ the openssl configuration file for your CA.
#. If necessary, add an entry for ``passphrase`` containing the passphrase for
-the CA's private key. We store this in ``/etc/bcfg2.conf`` as the permissions
-on that file should have it only readable by the bcfg2 user. If no passphrase
-is entry exists, it is assumed that the private key is stored unencrypted.
+ the CA's private key. We store this in ``/etc/bcfg2.conf`` as the permissions
+ on that file should have it only readable by the bcfg2 user. If no passphrase
+ is entry exists, it is assumed that the private key is stored unencrypted.
#. Add an entry ``chaincert`` that points to the location of your ssl chaining
-certificate. This is used when preexisting certifcate hostfiles are found, so
-that they can be validated and only regenerated if they no longer meet the
-specification.
+ certificate. This is used when preexisting certifcate hostfiles are found, so
+ that they can be validated and only regenerated if they no longer meet the
+ specification.
#. Once all this is done, you should have a section in your ``/etc/bcfg2.conf``
-that looks similar to the following:
+ that looks similar to the following::
- [sslca_default]
- config = /etc/pki/CA/openssl.cnf
- passphrase = youReallyThinkIdShareThis?
- chaincert = /etc/pki/CA/chaincert.crt
+ [sslca_default]
+ config = /etc/pki/CA/openssl.cnf
+ passphrase = youReallyThinkIdShareThis?
+ chaincert = /etc/pki/CA/chaincert.crt
#. You are now ready to create key and certificate definitions. For this
-example we'll assume you've added Path entries for the key,
-``/etc/pki/tls/private/localhost.key``, and the certificate,
-``/etc/pki/tls/certs/localhost.crt`` to a bundle or base.
+ example we'll assume you've added Path entries for the key,
+ ``/etc/pki/tls/private/localhost.key``, and the certificate,
+ ``/etc/pki/tls/certs/localhost.crt`` to a bundle or base.
#. Defining a key or certificate is similar to defining a TGenshi template.
-Under your Bcfg2's SSLCA directory, create the directory structure to match the
-path to your key. In this case this would be something like
-``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/localhost.key``.
+ Under your Bcfg2's SSLCA directory, create the directory structure to match the
+ path to your key. In this case this would be something like
+ ``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/localhost.key``.
#. Within that directory, create a ``key.xml`` file containing the following:
@@ -77,11 +77,11 @@ path to your key. In this case this would be something like
</KeyInfo>
#. This will cause the generation of an 2048 bit RSA key when a client requests
-that Path. Alternatively you can specify ``dsa`` as the keytype, or a different
-number of bits.
+ that Path. Alternatively you can specify ``dsa`` as the keytype, or a different
+ number of bits.
#. Similarly, create the matching directory structure for the certificate path,
-and a ``cert.xml`` containinng the following:
+ and a ``cert.xml`` containinng the following:
.. code-block:: xml
@@ -90,9 +90,9 @@ and a ``cert.xml`` containinng the following:
</CertInfo>
#. When a client requests the cert path, a certificate will be generated using
-the key hostfile at the specified key location, using the CA matching the ca
-attribute. ie. ca="default" will match [sslca_default] in your
-``/etc/bcfg2.conf``
+ the key hostfile at the specified key location, using the CA matching the ca
+ attribute. ie. ca="default" will match [sslca_default] in your
+ ``/etc/bcfg2.conf``
TODO
====
diff --git a/doc/server/plugins/generators/tcheetah.txt b/doc/server/plugins/generators/tcheetah.txt
index f2ca6c87c..ef8bb5513 100644
--- a/doc/server/plugins/generators/tcheetah.txt
+++ b/doc/server/plugins/generators/tcheetah.txt
@@ -50,6 +50,12 @@ self.metadata is an instance of the class ClientMetadata and documented
self.metadata.Properties.data
=============================
+.. note::
+
+ If you want to use Properties, you will need to enable the
+ :ref:`server-plugins-connectors-properties` plugin in
+ ``/etc/bcfg2.conf``.
+
Properties.data is a python `ElementTree <http://codespeak.net/lxml/>`_
object, loaded from the data in ``/var/lib/bcfg2/Properties/<properties
file>.xml``. That file should have a ``Properties`` node at its root.
@@ -78,6 +84,12 @@ on the host 'www.example.com'::
${self.metadata.Properties['example.xml'].data.xpath($path)[0].text}
${self.metadata.Properties['example.xml'].data.xpath(path)[0].text}
+Other Variables
+===============
+
+* **Template.searchList(self)[1]['path']** is the Path name specified in a Bundle
+* **Template.searchList(self)[1]['source_path']** is the path to the TCheetah template on the Bcfg2 server
+
Simple Example
==============
@@ -92,6 +104,8 @@ directory. Below is a simple example a file ``/foo``.
> buildfile /foo <clientname>
Hostname is $self.metadata.hostname
+ Filename is $Template.searchList(self)[1]['path']
+ Template is $Template.searchList(self)[1]['source_path']
Groups:
#for $group in $self.metadata.groups:
* $group
@@ -124,6 +138,8 @@ current client probe state.
<Path type="file" name="/foo" owner="root" perms="0624" group="root">
Hostname is topaz.mcs.anl.gov
+ Filename is /foo
+ Template is /var/lib/bcfg2/TCheetah/foo/template
Groups:
* desktop
* mcs-base
diff --git a/doc/server/plugins/generators/tgenshi/index.txt b/doc/server/plugins/generators/tgenshi/index.txt
index c1970ee0d..0fc541b52 100644
--- a/doc/server/plugins/generators/tgenshi/index.txt
+++ b/doc/server/plugins/generators/tgenshi/index.txt
@@ -48,8 +48,11 @@ supported.
Inside of templates
===================
-* **metadata** is the client's :ref:`metadata <server-plugins-grouping-metadata-clientmetadata>`
-* **properties.properties** is an xml document of unstructured data
+* **metadata** is the client's :ref:`metadata
+ <server-plugins-grouping-metadata-clientmetadata>`
+* **metadata.Properties** is an xml document of unstructured data (only
+ available when used in conjunction with the
+ :ref:`server-plugins-connectors-properties` plugin)
* **name** is the path name specified in bcfg
* **path** is the path to the TGenshi template
diff --git a/doc/server/plugins/grouping/metadata.txt b/doc/server/plugins/grouping/metadata.txt
index 1c915e2cf..43cc6a2d7 100644
--- a/doc/server/plugins/grouping/metadata.txt
+++ b/doc/server/plugins/grouping/metadata.txt
@@ -217,9 +217,9 @@ useful results:
</Groups>
Each of the included groups files has the same format. These files are
-properly validated by ``bcfg2-repo-validate``. This mechanism is
-useful for composing group definitions from multiple sources, or
-setting different permissions in an svn repository.
+properly validated by ``bcfg2-lint``. This mechanism is useful for
+composing group definitions from multiple sources, or setting
+different permissions in an svn repository.
Probes
======
diff --git a/doc/server/plugins/structures/bundler/index.txt b/doc/server/plugins/structures/bundler/index.txt
index 0d0054a2c..9fd897385 100644
--- a/doc/server/plugins/structures/bundler/index.txt
+++ b/doc/server/plugins/structures/bundler/index.txt
@@ -16,14 +16,15 @@ entries. For example, a bundle could say that the configuration file
describe the particular version of ``/etc/passwd`` that a given client
will receive.
-Groups can be used inside of bundles to differentiate which entries
-particular clients will recieve; this is useful for the case where
-entries are named differently across systems; for example, one linux
-distro may have a package called openssh while another uses the name
-ssh. Configuration entries nested inside of Group elements only apply
-to clients who are a member of those groups; multiple nested groups must
-all apply. Also, groups may be negated; entries included in such groups
-will only apply to clients who are not a member of said group.
+Group and Client tags can be used inside of bundles to differentiate
+which entries particular clients will recieve; this is useful for the
+case where entries are named differently across systems; for example,
+one linux distro may have a package called openssh while another uses
+the name ssh. Configuration entries nested inside of Group elements
+only apply to clients who are a member of those groups; multiple
+nested groups must all apply. Also, groups may be negated; entries
+included in such groups will only apply to clients who are not a
+member of said group. The same applies to Client elements.
The following is an annotated copy of a bundle:
@@ -54,20 +55,25 @@ The following is an annotated copy of a bundle:
<Package name='ssh'/>
<Service name='ssh'/>
</Group>
+ <Client name='trust.example.com'>
+ <Path name='/etc/ssh/shosts.equiv'/>
+ </Client>
</Bundle>
-In this bundle, most of the entries are common to all systems. Clients in
-group **deb** get one extra package and service, while clients in group
-**rpm** get two extra packages and an extra service. In addition, clients
-in group **fedora** *and* group **rpm** get one extra package entries,
-unless they are not in the **fc14** group, in which case, they get an
-extra package. Notice that this file doesn't describe which versions
-of these entries that clients should get, only that they should get
-them. (Admittedly, this example is slightly contrived, but demonstrates
-how group entries can be used in bundles)
+In this bundle, most of the entries are common to all systems. Clients
+in group **deb** get one extra package and service, while clients in
+group **rpm** get two extra packages and an extra service. In
+addition, clients in group **fedora** *and* group **rpm** get one
+extra package entries, unless they are not in the **fc14** group, in
+which case, they get an extra package. The client
+**trust.example.com** gets one extra file that is not distributed to
+any other clients. Notice that this file doesn't describe which
+versions of these entries that clients should get, only that they
+should get them. (Admittedly, this example is slightly contrived, but
+demonstrates how group entries can be used in bundles)
+----------------------------+-------------------------------+
-| Group | Entry |
+| Group/Hostname | Entry |
+============================+===============================+
| all | /etc/ssh/ssh_host_dsa_key |
+----------------------------+-------------------------------+
@@ -101,6 +107,8 @@ how group entries can be used in bundles)
+----------------------------+-------------------------------+
| deb | Service ssh |
+----------------------------+-------------------------------+
+| trust.example.com | /etc/ssh/shosts.equiv |
++----------------------------+-------------------------------+
Genshi templates
================
@@ -220,6 +228,8 @@ The latter form is preferred if the if block contains multiple
files. While this example is simple, the test in the if block can in
fact be any python statement.
+.. _server-plugins-structures-bundler-index-examples:
+
Other examples
==============
diff --git a/doc/unsorted/help.txt b/doc/unsorted/help.txt
deleted file mode 100644
index 155dbf77c..000000000
--- a/doc/unsorted/help.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-.. -*- mode: rst -*-
-
-.. _unsorted-help:
-
-================
-Ways to get help
-================
-
-Interactive Help
-================
-
-* [wiki:IRCChannel IRC Channel, with indexed archives]
-* [wiki:MailingList Mailing list, with indexed archives]
-
-Note that the IRC channel tends to be much busier than the mailing list; use whichever seems most appropriate for your query, but don't let the lack of mailing list activity make you think the project isn't active.
-
-Frequently Asked Questions
-==========================
-
-* [wiki:FAQ The FAQ]
-
-Examples
-========
-
-* There are examples sprinkled throughout this wiki; we should link to them from here.
-* The [http://www.fsf.org Free Software Foundation] is (very slowly) working towards having configurations for the majority of the machines it administers available via [http://config.fsf.org config.fsf.org]. This is a tie-in with the [http://autonomo.us/2008/07/franklin-street-statement/ Franklin Street Statement on Freedom and Network Services] (FSF offices are on Franklin Street). Documentation on how to have a public access Bcfg2 configuration repository will be at PublicRepository.
-
-Manuals
-=======
-
-* The current canonical source of documentation are pages on this wiki ([wiki:UsingBcfg2]). Please mail the MailingList for editor access to this wiki.
-* There is a printed manual in the SAGE short topics series, "!#19: Configuration Management with Bcfg2", that you can [https://db.usenix.org/cgi-bin/sage/booklets/order.cgi order] for $20 (or get for free if you are a [http://www.sage.org/index.html SAGE] member and haven't gotten a booklet yet during your current membership year). The book includes documentation up to and including most features in Bcfg2 0.9.6. Note that all proceeds from the sale of this book go to SAGE.
-
-FLOSS Manual Project
-====================
-
-A project is getting started to make a user-contributed manual using the [http://en.flossmanuals.net/ FLOSS Manuals] web site and tools. The intention is for this manual to be based on but not a verbatim copy of the information on the wiki, formatted in a way that is easier for new users to read, and written mostly by users of Bcfg2, rather than the authors of Bcfg2. This manual will also be free (as in freedom and price).
-
-One important point is that new contributors can get edit access to the manual in about a minute, and the manual is edited via WYSIWYG tools, so there should be pretty much no barrier for new manual authors to get started.
-
-There will be an announcement to the mailing list about this soon.
-
-If you are seriously interested in dedicating time to this manual, it would make sense to read the [http://en.flossmanuals.net/FLOSSManuals FLOSS Manuals Manual] (free online) and the [https://db.usenix.org/cgi-bin/sage/booklets/order.cgi Configuration Management with Bcfg2] manual ($20). If you are willing to commit time to manual writing, would like physical copies of these manuals, and purchasing them would be a financial hardship for you, email [http://pobox.com/~dclark Danny Clark] at dclark@pobox.com (ping djbclark on [wiki:IRCChannel #bcfg2 irc] if you don't get a reply) with your postal address (don't be shy, I already bought a bunch of these, and they aren't doing much good sitting on my shelf :-).
-
-You can get to the Bcfg2 FLOSS Manual at http://docs.bcfg2.org (which just redirects to http://en.flossmanuals.net/bin/view/BCFG2).
diff --git a/examples/bcfg2-lint.conf b/examples/bcfg2-lint.conf
new file mode 100644
index 000000000..abf969496
--- /dev/null
+++ b/examples/bcfg2-lint.conf
@@ -0,0 +1,25 @@
+[lint]
+plugins=Duplicates,InfoXML,Bundles,Comments,RequiredAttrs,Validate
+
+[errors]
+no-infoxml=error
+paranoid-false=error
+properties-schema-not-found=silent
+inconsistent-bundle-name=error
+keywords-not-found=error
+comments-not-found=error
+
+[InfoXML]
+required_attrs = owner,group,perms,paranoid
+
+[Comments]
+global_keywords = Id
+sgenshi_comments = Properties,Probes,Description
+properties_comments = Template,Format
+tgenshi_comments = Maintainer,Properties,Probes,Description
+cfg_comments =
+cfg_keywords =
+probe_comments = Maintainer,Purpose,Groups,Other Output
+
+[Validate]
+schema=/usr/share/bcfg2/schema
diff --git a/man/bcfg2-admin.8 b/man/bcfg2-admin.8
index 0b3829b7e..829d00f03 100644
--- a/man/bcfg2-admin.8
+++ b/man/bcfg2-admin.8
@@ -55,7 +55,7 @@ Build structure entries based on client statistics extra entries.
Install configuration information into repo based on client bad
entries.
.RE
-.B report [init|load_stats|purge|scrub|update]
+.B reports [init|load_stats|purge|scrub|update]
.RS
Interact with the dynamic reporting system.
.RE
@@ -154,7 +154,7 @@ Specify the type of the entry to pull.
.RS
Specify the name of the entry to pull.
.RE
-.SH REPORT OPTIONS
+.SH REPORTS OPTIONS
.PP
.B init
.RS
diff --git a/man/bcfg2-lint.8 b/man/bcfg2-lint.8
new file mode 100644
index 000000000..b1fa9244b
--- /dev/null
+++ b/man/bcfg2-lint.8
@@ -0,0 +1,169 @@
+.TH "bcfg2-lint" 8
+.SH NAME
+bcfg2-lint \- Check Bcfg2 specification for validity, common mistakes,
+and style
+
+.SH SYNOPSIS
+.B bcfg2-lint
+.I [OPTIONS]
+.I [<plugin> [<plugin>...]]
+
+.SH DESCRIPTION
+.PP
+.B bcfg2-lint
+This script checks the Bcfg2 specification for schema validity, common
+mistakes, and other criteria. It can be quite helpful in finding
+typos or malformed data.
+
+.B bcfg2-lint
+exits with a return value of 2 if errors were found, and 3
+if warnings (but no errors) were found. Any other non-0 exit value
+denotes some failure in the script itself.
+
+.B bcfg2-lint
+is a rewrite of the older
+.B bcfg2-repo-validate
+tool.
+
+.SH OPTIONS
+
+.TP
+.BR "-v"
+Be verbose.
+
+.TP
+.BR "-C"
+Specify path to bcfg2.conf (default /etc/bcfg2.conf)
+
+.TP
+.BR "--lint-config"
+Specify path to bcfg2-lint.conf (default /etc/bcfg2-lint.conf)
+
+.TP
+.BR "-Q"
+Specify path to Bcfg2 repository (default /var/lib/bcfg2)
+
+.TP
+.BR "--stdin"
+Rather than operating on all files in the Bcfg2 specification, only
+validate a list of files supplied on stdin. This mode is particularly
+useful in pre-commit hooks.
+
+This makes a few assumptions:
+
+Metadata files will only be checked if a valid chain of XIncludes can
+be followed all the way from clients.xml or groups.xml. Since there
+are multiple formats of metadata stored in Metadata/ (i.e., clients
+and groups), there is no way to determine which sort of data a file
+contains unless there is a valid chain of XIncludes. It may be useful
+to always specify all metadata files should be checked, even if not
+all of them have changed.
+
+Property files will only be validated if both the property file itself
+and its matching schema are included on stdin.
+
+.TP
+.BR "--require-schema"
+Require property files to have matching schema files
+
+.RE
+
+.SH "PLUGINS"
+
+See
+.BR bcfg-lint.conf(5)
+for more information on the configuration of the plugins listed below.
+
+.TP
+.BR Bundles
+Check the specification for several issues with Bundler: bundles
+referenced in metadata but not found in
+.I Bundler/
+; bundles whose
+.I name
+attribute does not match the filename; and Genshi template bundles
+that use the
+.I <Group>
+tag (which is not processed in templated bundles).
+
+.TP
+.BR Comments
+Check the specification for VCS keywords and any comments that are
+required. By default, this only checks that the
+.I $Id$
+keyword is included and expanded in all files. You may specify VCS
+keywords to check and comments to be required in the config file.
+(For instance, you might require that every file have a "Maintainer"
+comment.)
+
+In XML files, only comments are checked for the keywords and comments
+required.
+
+.TP
+.BR Duplicates
+Check for several types of duplicates in the Metadata: duplicate
+groups; duplicate clients; and multiple default groups.
+
+.TP
+.BR InfoXML
+Check that certain attributes are specified in
+.I info.xml
+files. By default, requires that
+.I owner
+,
+.I group
+, and
+.I perms
+are specified. Can also require that an
+.I info.xml
+exists for all Cfg files, and that paranoid mode be enabled for all
+files.
+
+.TP
+.BR Pkgmgr
+Check for duplicate packages specified in Pkgmgr.
+
+.TP
+.BR RequiredAttrs
+Check that all
+.I <Path>
+and
+.I <BoundPath>
+tags have the attributes that are required by their type. (E.g., a
+path of type
+.I "symlink"
+must have
+.I name
+and
+.I to
+specified to be valid. This sort of validation is beyond the scope of
+an XML schema.
+
+.TP
+.BR Validate
+Validate the Bcfg2 specification against the XML schemas.
+
+Property files are freeform XML, but if a
+.I .xsd
+file with a matching filename is provided, then schema validation will
+be performed on property files individually as well. For instance, if
+you have a property file named
+.I ntp.xml
+then by placing a schema for that file in
+.I ntp.xsd
+schema validation will be performed on
+.I ntp.xml
+.
+
+
+.SH "SEE ALSO"
+.BR bcfg2(1),
+.BR bcfg2-server(8),
+.BR bcfg2-lint.conf(5)
+
+.SH "BUGS"
+
+bcfg2-lint may not handle some older plugins as well as it handles
+newer ones. For instance, there may be some places where it expects
+all of your configuration files to be handled by Cfg rather than by a
+mix of Cfg and TGenshi or TCheetah.
diff --git a/man/bcfg2-lint.conf.5 b/man/bcfg2-lint.conf.5
new file mode 100644
index 000000000..0ae7a27ac
--- /dev/null
+++ b/man/bcfg2-lint.conf.5
@@ -0,0 +1,166 @@
+.TH bcfg2-lint.conf 5
+
+.SH NAME
+bcfg2-lint.conf - configuration parameters for bcfg2-lint
+
+.SH DESCRIPTION
+.TP
+bcfg2-lint.conf includes configuration parameters for
+.I bcfg2-lint
+
+.SH FILE FORMAT
+The file is INI-style and consists of sections and options. A section
+begins with the name of the sections in square brackets and continues
+until the next section begins.
+
+Options are specified in the form 'name = value'.
+
+The file is line-based each newline-terminated line represents either
+a comment, a section name or an option.
+
+Any line beginning with a hash (#) is ignored, as are lines containing
+only whitespace.
+
+The file consists of one
+.I [lint]
+section, up to one
+.I [errors]
+section, and then any number of plugin-specific sections, documented below. (Note that this makes it quite feasible to combine your
+.B bcfg2-lint.conf
+into your
+.B bcfg2.conf(5)
+file, if you so desire.)
+
+.SH GLOBAL OPTIONS
+These options apply to
+.I bcfg2-lint
+generally, and must be in the
+.I [lint]
+section.
+
+.TP
+.BR plugins
+A comma-delimited list of plugins to run. By default, all plugins are
+run. This can be overridden by listing plugins on the command line.
+See
+.B bcfg2-lint(1)
+for a list of the available plugins.
+
+.SH ERROR HANDLING
+Error handling is configured in the
+.I [errors]
+section. Each option should be the name of an error and one of
+.I "error"
+,
+.I "warning"
+, or
+.I "silent"
+, which tells
+.B bcfg2-lint(1)
+how to handle the warning. Error names and their defaults can be
+displayed by running
+.B bcfg2-lint(1)
+with the
+.B --list-errors
+option.
+
+.SH PLUGIN OPTIONS
+
+These options apply only to a single plugin. Each option should be in
+a section named for its plugin; for instance, options for the InfoXML
+plugin would be in a section called
+.I [InfoXML]
+.
+
+If a plugin is not listed below, then it has no configuration.
+
+In many cases, the behavior of a plugin can be configured by modifying
+how errors from it are handled. See
+.B ERROR HANDLING
+, above.
+
+.TP
+.BR Comments
+
+The
+.I Comments
+plugin configuration specifies which VCS keywords and comments are
+required for which file types. The valid types of file are
+.I "global"
+(all file types),
+.I "bundler"
+(non-templated bundle files),
+.I "sgenshi"
+(templated bundle files),
+.I "properties"
+(property files),
+.I "cfg"
+(non-templated Cfg files),
+.I "tgenshi"
+(templated Cfg files),
+.I "infoxml"
+(info.xml files), and
+.I "probe"
+(probe files).
+
+The specific types (i.e., types other than "global") all supplement
+global; they do not override it. The exception is if you specify an
+empty option, e.g.:
+
+.nf
+cfg_keywords =
+.fi
+
+By default, the
+.I $Id$
+keyword is checked for and nothing else.
+
+Multiple keywords or comments should be comma-delimited.
+
+\(bu
+.B <type>_keywords
+
+Ensure that files of the specified type have the given VCS keyword.
+Do
+.I not
+include the dollar signs. I.e.:
+
+.nf
+infoxml_keywords = Revision
+.fi
+
+.I not:
+
+.nf
+infoxml_keywords = $Revision$
+.fi
+
+\(bu
+.B <type>_comments
+
+Ensure that files of the specified type have a comment containing the
+given string. In XML files, only comments are checked. In plain text
+files, all lines are checked since comment characters may vary.
+
+.TP
+.BR InfoXML
+
+\(bu
+.B required_attrs
+A comma-delimited list of attributes to require on
+.I <Info>
+tags. Default is "owner,group,perms".
+
+.TP
+.BR Validate
+
+\(bu
+.B schema
+The full path to the XML Schema files. Default is
+"/usr/share/bcfg2/schema". This can be overridden with the
+.I --schema
+command-line option
+
+.SH SEE ALSO
+.BR bcfg2-lint(1)
+
diff --git a/man/bcfg2-ping-sweep.8 b/man/bcfg2-ping-sweep.8
new file mode 100644
index 000000000..54eaa8e76
--- /dev/null
+++ b/man/bcfg2-ping-sweep.8
@@ -0,0 +1,20 @@
+.TH "bcfg2-ping-sweep" 8
+.SH NAME
+bcfg2-ping-sweep \- Update pingable and pingtime attributes in
+clients.xml
+.SH SYNOPSIS
+.B bcfg2-ping-sweep
+.SH "DESCRIPTION"
+.PP
+\fBbcfg2-ping-sweep\fR traverses the list of clients in
+Metadata/clients.xml and updates their pingable/pingtime attributes. The
+pingtime value is set to the last time the client was pinged (not the
+RTT value).
+.SH OPTIONS
+.PP
+.B None
+.SH "SEE ALSO"
+.BR bcfg(1),
+.BR bcfg2-server(8)
+.SH "BUGS"
+None currently known
diff --git a/man/bcfg2-repo-validate.8 b/man/bcfg2-repo-validate.8
deleted file mode 100644
index d00885313..000000000
--- a/man/bcfg2-repo-validate.8
+++ /dev/null
@@ -1,22 +0,0 @@
-.TH "bcfg2-repo-validate" 8
-.SH NAME
-bcfg2-repo-validate \- Check Bcfg2 repository data against data schemas
-.SH SYNOPSIS
-.B bcfg2-repo-validate
-.I [-v]
-.SH DESCRIPTION
-.PP
-.B bcfg2-repo-validate
-This script checks data against schemas, and it quite helpful in
-finding typos or malformed data.
-.SH OPTIONS
-.PP
-.B "\-C"
-.RS
-Specify path to bcfg2.conf (default /etc/bcfg2.conf)
-.RE
-.SH "SEE ALSO"
-.BR bcfg2(1),
-.BR bcfg2-server(8)
-.SH "BUGS"
-None currently known
diff --git a/man/bcfg2-reports.8 b/man/bcfg2-reports.8
index b8b4cccee..bc4c9344b 100644
--- a/man/bcfg2-reports.8
+++ b/man/bcfg2-reports.8
@@ -1,82 +1,81 @@
-.TH "bcfg2-reports" 8
-.SH NAME
-bcfg2-reports \- Query reporting system for client status
-.SH SYNOPSIS
-.B bcfg2-reports
-.I [-v]
-.SH DESCRIPTION
-.PP
-.B bcfg2-reports
-bcfg2-reports allows you to retrieve data from the database about
-clients, and the states of their current interactions. It also allows
-you to change the expired/unexpired states.
-The utility runs as a standalone application. It does, however, use
-the models from /src/lib/Server/Reports/reports/models.py.
-.SH OPTIONS
-.PP
-.B "\-a"
-.RS
-Shows all hosts, including expired hosts.
-.RE
-.B "\-b NAME"
-.RS
-Single-host mode \- shows bad entries from the current interaction of
-NAME. NAME is the name of the entry.
-.RE
-.B "-c\"
-.RS
-Shows only clean hosts.
-.RE
-.B "\-d"
-.RS
-Shows only dirty hosts.
-.RE
-.B "\-e NAME"
-.RS
-Single host mode \- shows extra entries from the current interaction
-of NAME. NAME is the name of the entry.
-.RE
-.B "\-h"
-.RS
-Shows help and usage info about bcfg2-reports.
-.RE
-.B "\-s NAME"
-.RS
-Single host mode \- shows bad and extra entries from the current
-interaction of NAME. NAME is the name of the entry.
-.RE
-.B "\-x NAME"
-.RS
-Toggles expired/unexpired state of NAME. NAME is the name of the entry.
-.RE
-.B "\-\-badentry=KIND,NAME"
-.RS
-Shows only hosts whose current interaction has bad entries in of KIND
-kind and NAME name; if a single argument ARG1 is given, then KIND,NAME
-pairs will be read from a file of name ARG1. KIND is the type of entry
-(Package, Path, Service, etc). NAME is the name of the entry.
-.RE
-.B "\-\-extraentry=KIND,NAME"
-.RS
-Shows only hosts whose current interaction has extra entries in of KIND
-kind and NAME name; if a single argument ARG1 is given, then KIND,NAME
-pairs will be read from a file of name ARG1. KIND is the type of entry
-(Package, Path, Service, etc). NAME is the name of the entry.
-.RE
-.B "\-\-fields=ARG1,ARG2,..."
-.RS
-Only displays the fields ARG1,ARG2,... (name, time, state)
-.RE
-.B "\-\-sort=ARG1,ARG2,..."
-.RS
-Sorts output on ARG1,ARG2,... (name, time, state)
-.RE
-.B "\-\-stale"
-.RS
-Shows hosts which haven't run in the last 24 hours
-.RE
-.SH "SEE ALSO"
-.BR bcfg2(1),
-.BR bcfg2-server(8)
-.SH "BUGS"
-None currently known
+.TH "bcfg2-reports" 8
+.SH NAME
+bcfg2-reports \- Query reporting system for client status
+.SH SYNOPSIS
+.B bcfg2-reports
+.I [-v]
+.SH DESCRIPTION
+.PP
+\fBbcfg2-reports\fR allows you to retrieve data from the database about
+clients, and the states of their current interactions. It also allows
+you to change the expired/unexpired states.
+The utility runs as a standalone application. It does, however, use
+the models from /src/lib/Server/Reports/reports/models.py.
+.SH OPTIONS
+.PP
+.B "\-a"
+.RS
+Shows all hosts, including expired hosts.
+.RE
+.B "\-b NAME"
+.RS
+Single-host mode \- shows bad entries from the current interaction of
+NAME. NAME is the name of the entry.
+.RE
+.B "-c\"
+.RS
+Shows only clean hosts.
+.RE
+.B "\-d"
+.RS
+Shows only dirty hosts.
+.RE
+.B "\-e NAME"
+.RS
+Single host mode \- shows extra entries from the current interaction
+of NAME. NAME is the name of the entry.
+.RE
+.B "\-h"
+.RS
+Shows help and usage info about bcfg2-reports.
+.RE
+.B "\-s NAME"
+.RS
+Single host mode \- shows bad and extra entries from the current
+interaction of NAME. NAME is the name of the entry.
+.RE
+.B "\-x NAME"
+.RS
+Toggles expired/unexpired state of NAME. NAME is the name of the entry.
+.RE
+.B "\-\-badentry=KIND,NAME"
+.RS
+Shows only hosts whose current interaction has bad entries in of KIND
+kind and NAME name; if a single argument ARG1 is given, then KIND,NAME
+pairs will be read from a file of name ARG1. KIND is the type of entry
+(Package, Path, Service, etc). NAME is the name of the entry.
+.RE
+.B "\-\-extraentry=KIND,NAME"
+.RS
+Shows only hosts whose current interaction has extra entries in of KIND
+kind and NAME name; if a single argument ARG1 is given, then KIND,NAME
+pairs will be read from a file of name ARG1. KIND is the type of entry
+(Package, Path, Service, etc). NAME is the name of the entry.
+.RE
+.B "\-\-fields=ARG1,ARG2,..."
+.RS
+Only displays the fields ARG1,ARG2,... (name, time, state)
+.RE
+.B "\-\-sort=ARG1,ARG2,..."
+.RS
+Sorts output on ARG1,ARG2,... (name, time, state)
+.RE
+.B "\-\-stale"
+.RS
+Shows hosts which haven't run in the last 24 hours
+.RE
+.SH "SEE ALSO"
+.BR bcfg2(1),
+.BR bcfg2-server(8)
+.SH "BUGS"
+None currently known
diff --git a/man/bcfg2-server.8 b/man/bcfg2-server.8
index a6bffc1fa..2d132ce6d 100644
--- a/man/bcfg2-server.8
+++ b/man/bcfg2-server.8
@@ -53,6 +53,6 @@ Set path to SSL key.
.RE
.SH "SEE ALSO"
.BR bcfg2(1),
-.BR bcfg2-repo-validate(8)
+.BR bcfg2-lint(8)
.SH "BUGS"
None currently known
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index a94b49253..1e6b71ea5 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -13,13 +13,13 @@
%define lxmldep %(rpm -q %{alt_lxml} 2>&1 > /dev/null && echo %{alt_lxml} || echo %{dfl_lxml})
Name: bcfg2
-Version: 1.2.0pre1
+Version: 1.2.0pre2
Release: %{release}
Summary: Configuration management system
Group: Applications/System
License: BSD
-URL: http://trac.mcs.anl.gov/projects/bcfg2
+URL: http://bcfg2.org
Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -28,11 +28,13 @@ BuildArch: noarch
BuildRequires: python-devel
BuildRequires: %{lxmldep}
-%if 0%{?rhel} <= 5
+# %{rhel} wasn't set before rhel 6. so this checks for old RHEL
+# %systems (and potentially very old Fedora systems, too)
+%if "%{_vendor}" == "redhat" && 0%{?rhel} < 6
BuildRequires: python-sphinx10
# the python-sphinx10 package doesn't set sys.path correctly, so we
# have to do it for them
-%define pythonpath %(rpm -ql python-sphinx10 | grep '\.egg$')
+%define pythonpath /usr/lib/python%{py_ver}/site-packages/Sphinx-1.0.4-py%{py_ver}.egg
%else
BuildRequires: python-sphinx >= 0.6
%endif
@@ -226,15 +228,17 @@ mv build/dtd %{buildroot}%{_defaultdocdir}/bcfg2-doc-%{version}/
%{_sbindir}/bcfg2-build-reports
%{_sbindir}/bcfg2-info
%{_sbindir}/bcfg2-ping-sweep
+%{_sbindir}/bcfg2-lint
%{_sbindir}/bcfg2-repo-validate
%{_sbindir}/bcfg2-reports
%{_sbindir}/bcfg2-server
+%{_mandir}/man5/bcfg2-lint.conf.5*
%{_mandir}/man8/*.8*
%dir %{_prefix}/lib/bcfg2
%files doc
-%defattr(0644,root,root,-)
+%defattr(-,root,root,-)
%doc %{_defaultdocdir}/bcfg2-doc-%{version}
%files -n bcfg2-web
diff --git a/osx/Introduction.txt b/osx/Introduction.txt
new file mode 100644
index 000000000..79b935f23
--- /dev/null
+++ b/osx/Introduction.txt
@@ -0,0 +1,16 @@
+Bcfg2
+
+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.
+
+Bcfg2 is fairly portable. It has been successfully run on:
+
+ AIX, FreeBSD, OpenBSD, Mac OS X, OpenSolaris, Solaris
+ Many GNU/Linux distributions, including ArchLinux Blag, CentOS, Debian, Fedora, Gentoo, gNewSense, Mandriva, openSUSE, Red Hat/RHEL, SuSE/SLES, Trisquel, and Ubuntu.
+
+Bcfg2 should run on any POSIX compatible operating system, however direct support for an operating system's package and service formats are limited by the currently available client tools (new client tools are pretty easy to add). There is also an incomplete but more exact list of platforms on which Bcfg2 works.
diff --git a/osx/Makefile b/osx/Makefile
index bd16a4227..075628668 100644
--- a/osx/Makefile
+++ b/osx/Makefile
@@ -14,7 +14,7 @@ FILTERS = --filter Hostbase \
--filter bcfg2-build-reports \
--filter bcfg2-info \
--filter bcfg2-ping-sweep \
---filter bcfg2-repo-validate \
+--filter bcfg2-lint \
--filter bcfg2-reports \
--filter bcfg2-server
@@ -41,14 +41,14 @@ default: clean client
install:
echo "Installing Bcfg2 to ${PKGROOT}"
- mkdir ${PKGROOT}
+ mkdir -p ${PKGROOT}
cd ../ && /usr/bin/python setup.py install \
--root="osx/${PKGROOT}" \
--install-lib="${SITELIBDIR}" \
--install-data="${DATADIR}"
prepare: install
- mkdir ${PKGTMP}
+ mkdir -p ${PKGTMP}
cp ${PROTO_PLIST} ${PKGTMP}
sed -i '' "s/{SHORTVERSION}/${BCFGVER}/g" "${PKGTMP}/${PROTO_PLIST}"
sed -i '' "s/{MAJORVERSION}/${MAJOR}/g" "${PKGTMP}/${PROTO_PLIST}"
diff --git a/osx/preflight b/osx/preflight
index 169551b35..64555480f 100644
--- a/osx/preflight
+++ b/osx/preflight
@@ -8,4 +8,6 @@
/bin/rm -Rvf "${3}"{SITELIBDIR}/Bcfg2*
/bin/rm -Rvf "${3}"/usr/local/bin/bcfg2*
/bin/rm -Rvf "${3}{DATADIR}/share/bcfg2"
+/bin/rm -Rvf "${3}{DATADIR}/share/man/man1/bcfg2*"
+/bin/rm -Rvf "${3}{DATADIR}/share/man/man5/bcfg2*"
/bin/rm -Rvf "${3}{DATADIR}/share/man/man8/bcfg2*"
diff --git a/redhat/RELEASE b/redhat/RELEASE
index 22a4ba1bd..a0694f914 100644
--- a/redhat/RELEASE
+++ b/redhat/RELEASE
@@ -1 +1 @@
-0.0pre1
+0.0pre2
diff --git a/redhat/bcfg2.spec.in b/redhat/bcfg2.spec.in
index dd6e70f86..432dee5b8 100644
--- a/redhat/bcfg2.spec.in
+++ b/redhat/bcfg2.spec.in
@@ -15,8 +15,8 @@ Summary: Configuration management system
Group: Applications/System
License: BSD
-URL: http://trac.mcs.anl.gov/projects/bcfg2
-Source0: %{name}-%{version}-%{release}.tar.gz
+URL: http://bcfg2.org
+Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
@@ -26,11 +26,13 @@ BuildRequires: python-setuptools-devel
BuildRequires: python-setuptools
%endif
-%if 0%{?rhel} <= 5
+# %{rhel} wasn't set before rhel 6. so this checks for old RHEL
+# %systems (and potentially very old Fedora systems, too)
+%if "%{_vendor}" == "redhat" && 0%{?rhel} < 6
BuildRequires: python-sphinx10
# the python-sphinx10 package doesn't set sys.path correctly, so we
# have to do it for them
-%define pythonpath %(rpm -ql python-sphinx10 | grep '\.egg$')
+%define pythonpath /usr/lib/python%{py_ver}/site-packages/Sphinx-1.0.4-py%{py_ver}.egg
%else
BuildRequires: python-sphinx >= 0.6
%endif
@@ -234,16 +236,18 @@ fi
%{_sbindir}/bcfg2-build-reports
%{_sbindir}/bcfg2-info
%{_sbindir}/bcfg2-ping-sweep
+%{_sbindir}/bcfg2-lint
%{_sbindir}/bcfg2-repo-validate
%{_sbindir}/bcfg2-reports
%{_sbindir}/bcfg2-server
+%{_mandir}/man5/bcfg2-lint.conf.5*
%{_mandir}/man8/*.8*
%dir %{_var}/lib/bcfg2
%files doc
-%defattr(0644,root,root,-)
+%defattr(-,root,root,-)
%doc %{_defaultdocdir}/bcfg2-doc-%{version}
%changelog
diff --git a/reports/site_media/CalendarPopup.js b/reports/site_media/CalendarPopup.js
index b63ff2f11..8119032df 100644
--- a/reports/site_media/CalendarPopup.js
+++ b/reports/site_media/CalendarPopup.js
@@ -1,693 +1,693 @@
-// ===================================================================
-// Author: Matt Kruse <matt@mattkruse.com>
-// WWW: http://www.mattkruse.com/
-//
-// NOTICE: You may use this code for any purpose, commercial or
-// private, without any further permission from the author. You may
-// remove this notice from your final code if you wish, however it is
-// appreciated by the author if at least my web site address is kept.
-//
-// You may *NOT* re-distribute this code in any way except through its
-// use. That means, you can include it in your product, or your web
-// site, or any other form where the code is actually being used. You
-// may not put the plain javascript up on your site for download or
-// include it in your javascript libraries for download.
-// If you wish to share this code with others, please just point them
-// to the URL instead.
-// Please DO NOT link directly to my .js files from your site. Copy
-// the files to your server and use them there. Thank you.
-// ===================================================================
-
-// HISTORY
-// ------------------------------------------------------------------
-// Feb 7, 2005: Fixed a CSS styles to use px unit
-// March 29, 2004: Added check in select() method for the form field
-// being disabled. If it is, just return and don't do anything.
-// March 24, 2004: Fixed bug - when month name and abbreviations were
-// changed, date format still used original values.
-// January 26, 2004: Added support for drop-down month and year
-// navigation (Thanks to Chris Reid for the idea)
-// September 22, 2003: Fixed a minor problem in YEAR calendar with
-// CSS prefix.
-// August 19, 2003: Renamed the function to get styles, and made it
-// work correctly without an object reference
-// August 18, 2003: Changed showYearNavigation and
-// showYearNavigationInput to optionally take an argument of
-// true or false
-// July 31, 2003: Added text input option for year navigation.
-// Added a per-calendar CSS prefix option to optionally use
-// different styles for different calendars.
-// July 29, 2003: Fixed bug causing the Today link to be clickable
-// even though today falls in a disabled date range.
-// Changed formatting to use pure CSS, allowing greater control
-// over look-and-feel options.
-// June 11, 2003: Fixed bug causing the Today link to be unselectable
-// under certain cases when some days of week are disabled
-// March 14, 2003: Added ability to disable individual dates or date
-// ranges, display as light gray and strike-through
-// March 14, 2003: Removed dependency on graypixel.gif and instead
-/// use table border coloring
-// March 12, 2003: Modified showCalendar() function to allow optional
-// start-date parameter
-// March 11, 2003: Modified select() function to allow optional
-// start-date parameter
-/*
-DESCRIPTION: This object implements a popup calendar to allow the user to
-select a date, month, quarter, or year.
-
-COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
-positioning errors - usually with Window positioning - occur on the
-Macintosh platform.
-The calendar can be modified to work for any location in the world by
-changing which weekday is displayed as the first column, changing the month
-names, and changing the column headers for each day.
-
-USAGE:
-// Create a new CalendarPopup object of type WINDOW
-var cal = new CalendarPopup();
-
-// Create a new CalendarPopup object of type DIV using the DIV named 'mydiv'
-var cal = new CalendarPopup('mydiv');
-
-// Easy method to link the popup calendar with an input box.
-cal.select(inputObject, anchorname, dateFormat);
-// Same method, but passing a default date other than the field's current value
-cal.select(inputObject, anchorname, dateFormat, '01/02/2000');
-// This is an example call to the popup calendar from a link to populate an
-// input box. Note that to use this, date.js must also be included!!
-<A HREF="#" onClick="cal.select(document.forms[0].date,'anchorname','MM/dd/yyyy'); return false;">Select</A>
-
-// Set the type of date select to be used. By default it is 'date'.
-cal.setDisplayType(type);
-
-// When a date, month, quarter, or year is clicked, a function is called and
-// passed the details. You must write this function, and tell the calendar
-// popup what the function name is.
-// Function to be called for 'date' select receives y, m, d
-cal.setReturnFunction(functionname);
-// Function to be called for 'month' select receives y, m
-cal.setReturnMonthFunction(functionname);
-// Function to be called for 'quarter' select receives y, q
-cal.setReturnQuarterFunction(functionname);
-// Function to be called for 'year' select receives y
-cal.setReturnYearFunction(functionname);
-
-// Show the calendar relative to a given anchor
-cal.showCalendar(anchorname);
-
-// Hide the calendar. The calendar is set to autoHide automatically
-cal.hideCalendar();
-
-// Set the month names to be used. Default are English month names
-cal.setMonthNames("January","February","March",...);
-
-// Set the month abbreviations to be used. Default are English month abbreviations
-cal.setMonthAbbreviations("Jan","Feb","Mar",...);
-
-// Show navigation for changing by the year, not just one month at a time
-cal.showYearNavigation();
-
-// Show month and year dropdowns, for quicker selection of month of dates
-cal.showNavigationDropdowns();
-
-// Set the text to be used above each day column. The days start with
-// sunday regardless of the value of WeekStartDay
-cal.setDayHeaders("S","M","T",...);
-
-// Set the day for the first column in the calendar grid. By default this
-// is Sunday (0) but it may be changed to fit the conventions of other
-// countries.
-cal.setWeekStartDay(1); // week is Monday - Sunday
-
-// Set the weekdays which should be disabled in the 'date' select popup. You can
-// then allow someone to only select week end dates, or Tuedays, for example
-cal.setDisabledWeekDays(0,1); // To disable selecting the 1st or 2nd days of the week
-
-// Selectively disable individual days or date ranges. Disabled days will not
-// be clickable, and show as strike-through text on current browsers.
-// Date format is any format recognized by parseDate() in date.js
-// Pass a single date to disable:
-cal.addDisabledDates("2003-01-01");
-// Pass null as the first parameter to mean "anything up to and including" the
-// passed date:
-cal.addDisabledDates(null, "01/02/03");
-// Pass null as the second parameter to mean "including the passed date and
-// anything after it:
-cal.addDisabledDates("Jan 01, 2003", null);
-// Pass two dates to disable all dates inbetween and including the two
-cal.addDisabledDates("January 01, 2003", "Dec 31, 2003");
-
-// When the 'year' select is displayed, set the number of years back from the
-// current year to start listing years. Default is 2.
-// This is also used for year drop-down, to decide how many years +/- to display
-cal.setYearSelectStartOffset(2);
-
-// Text for the word "Today" appearing on the calendar
-cal.setTodayText("Today");
-
-// The calendar uses CSS classes for formatting. If you want your calendar to
-// have unique styles, you can set the prefix that will be added to all the
-// classes in the output.
-// For example, normal output may have this:
-// <SPAN CLASS="cpTodayTextDisabled">Today<SPAN>
-// But if you set the prefix like this:
-cal.setCssPrefix("Test");
-// The output will then look like:
-// <SPAN CLASS="TestcpTodayTextDisabled">Today<SPAN>
-// And you can define that style somewhere in your page.
-
-// When using Year navigation, you can make the year be an input box, so
-// the user can manually change it and jump to any year
-cal.showYearNavigationInput();
-
-// Set the calendar offset to be different than the default. By default it
-// will appear just below and to the right of the anchorname. So if you have
-// a text box where the date will go and and anchor immediately after the
-// text box, the calendar will display immediately under the text box.
-cal.offsetX = 20;
-cal.offsetY = 20;
-
-NOTES:
-1) Requires the functions in AnchorPosition.js and PopupWindow.js
-
-2) Your anchor tag MUST contain both NAME and ID attributes which are the
- same. For example:
- <A NAME="test" ID="test"> </A>
-
-3) There must be at least a space between <A> </A> for IE5.5 to see the
- anchor tag correctly. Do not do <A></A> with no space.
-
-4) When a CalendarPopup object is created, a handler for 'onmouseup' is
- attached to any event handler you may have already defined. Do NOT define
- an event handler for 'onmouseup' after you define a CalendarPopup object
- or the autoHide() will not work correctly.
-
-5) The calendar popup display uses style sheets to make it look nice.
-
-*/
-
-// CONSTRUCTOR for the CalendarPopup Object
-function CalendarPopup() {
- var c;
- if (arguments.length>0) {
- c = new PopupWindow(arguments[0]);
- }
- else {
- c = new PopupWindow();
- c.setSize(150,175);
- }
- c.offsetX = -152;
- c.offsetY = 25;
- c.autoHide();
- // Calendar-specific properties
- c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
- c.monthAbbreviations = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
- c.dayHeaders = new Array("S","M","T","W","T","F","S");
- c.returnFunction = "CP_tmpReturnFunction";
- c.returnMonthFunction = "CP_tmpReturnMonthFunction";
- c.returnQuarterFunction = "CP_tmpReturnQuarterFunction";
- c.returnYearFunction = "CP_tmpReturnYearFunction";
- c.weekStartDay = 0;
- c.isShowYearNavigation = false;
- c.displayType = "date";
- c.disabledWeekDays = new Object();
- c.disabledDatesExpression = "";
- c.yearSelectStartOffset = 2;
- c.currentDate = null;
- c.todayText="Today";
- c.cssPrefix="";
- c.isShowNavigationDropdowns=false;
- c.isShowYearNavigationInput=false;
- window.CP_calendarObject = null;
- window.CP_targetInput = null;
- window.CP_dateFormat = "MM/dd/yyyy";
- // Method mappings
- c.copyMonthNamesToWindow = CP_copyMonthNamesToWindow;
- c.setReturnFunction = CP_setReturnFunction;
- c.setReturnMonthFunction = CP_setReturnMonthFunction;
- c.setReturnQuarterFunction = CP_setReturnQuarterFunction;
- c.setReturnYearFunction = CP_setReturnYearFunction;
- c.setMonthNames = CP_setMonthNames;
- c.setMonthAbbreviations = CP_setMonthAbbreviations;
- c.setDayHeaders = CP_setDayHeaders;
- c.setWeekStartDay = CP_setWeekStartDay;
- c.setDisplayType = CP_setDisplayType;
- c.setDisabledWeekDays = CP_setDisabledWeekDays;
- c.addDisabledDates = CP_addDisabledDates;
- c.setYearSelectStartOffset = CP_setYearSelectStartOffset;
- c.setTodayText = CP_setTodayText;
- c.showYearNavigation = CP_showYearNavigation;
- c.showCalendar = CP_showCalendar;
- c.hideCalendar = CP_hideCalendar;
- c.getStyles = getCalendarStyles;
- c.refreshCalendar = CP_refreshCalendar;
- c.getCalendar = CP_getCalendar;
- c.select = CP_select;
- c.setCssPrefix = CP_setCssPrefix;
- c.showNavigationDropdowns = CP_showNavigationDropdowns;
- c.showYearNavigationInput = CP_showYearNavigationInput;
- c.copyMonthNamesToWindow();
- // Return the object
- return c;
- }
-function CP_copyMonthNamesToWindow() {
- // Copy these values over to the date.js
- if (typeof(window.MONTH_NAMES)!="undefined" && window.MONTH_NAMES!=null) {
- window.MONTH_NAMES = new Array();
- for (var i=0; i<this.monthNames.length; i++) {
- window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthNames[i];
- }
- for (var i=0; i<this.monthAbbreviations.length; i++) {
- window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthAbbreviations[i];
- }
- }
-}
-// Temporary default functions to be called when items clicked, so no error is thrown
-function CP_tmpReturnFunction(y,m,d) {
- if (window.CP_targetInput!=null) {
- var dt = new Date(y,m-1,d,0,0,0);
- if (window.CP_calendarObject!=null) { window.CP_calendarObject.copyMonthNamesToWindow(); }
- window.CP_targetInput.value = formatDate(dt,window.CP_dateFormat);
- }
- else {
- alert('Use setReturnFunction() to define which function will get the clicked results!');
- }
- }
-function CP_tmpReturnMonthFunction(y,m) {
- alert('Use setReturnMonthFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , month='+m);
- }
-function CP_tmpReturnQuarterFunction(y,q) {
- alert('Use setReturnQuarterFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , quarter='+q);
- }
-function CP_tmpReturnYearFunction(y) {
- alert('Use setReturnYearFunction() to define which function will get the clicked results!\nYou clicked: year='+y);
- }
-
-// Set the name of the functions to call to get the clicked item
-function CP_setReturnFunction(name) { this.returnFunction = name; }
-function CP_setReturnMonthFunction(name) { this.returnMonthFunction = name; }
-function CP_setReturnQuarterFunction(name) { this.returnQuarterFunction = name; }
-function CP_setReturnYearFunction(name) { this.returnYearFunction = name; }
-
-// Over-ride the built-in month names
-function CP_setMonthNames() {
- for (var i=0; i<arguments.length; i++) { this.monthNames[i] = arguments[i]; }
- this.copyMonthNamesToWindow();
- }
-
-// Over-ride the built-in month abbreviations
-function CP_setMonthAbbreviations() {
- for (var i=0; i<arguments.length; i++) { this.monthAbbreviations[i] = arguments[i]; }
- this.copyMonthNamesToWindow();
- }
-
-// Over-ride the built-in column headers for each day
-function CP_setDayHeaders() {
- for (var i=0; i<arguments.length; i++) { this.dayHeaders[i] = arguments[i]; }
- }
-
-// Set the day of the week (0-7) that the calendar display starts on
-// This is for countries other than the US whose calendar displays start on Monday(1), for example
-function CP_setWeekStartDay(day) { this.weekStartDay = day; }
-
-// Show next/last year navigation links
-function CP_showYearNavigation() { this.isShowYearNavigation = (arguments.length>0)?arguments[0]:true; }
-
-// Which type of calendar to display
-function CP_setDisplayType(type) {
- if (type!="date"&&type!="week-end"&&type!="month"&&type!="quarter"&&type!="year") { alert("Invalid display type! Must be one of: date,week-end,month,quarter,year"); return false; }
- this.displayType=type;
- }
-
-// How many years back to start by default for year display
-function CP_setYearSelectStartOffset(num) { this.yearSelectStartOffset=num; }
-
-// Set which weekdays should not be clickable
-function CP_setDisabledWeekDays() {
- this.disabledWeekDays = new Object();
- for (var i=0; i<arguments.length; i++) { this.disabledWeekDays[arguments[i]] = true; }
- }
-
-// Disable individual dates or ranges
-// Builds an internal logical test which is run via eval() for efficiency
-function CP_addDisabledDates(start, end) {
- if (arguments.length==1) { end=start; }
- if (start==null && end==null) { return; }
- if (this.disabledDatesExpression!="") { this.disabledDatesExpression+= "||"; }
- if (start!=null) { start = parseDate(start); start=""+start.getFullYear()+LZ(start.getMonth()+1)+LZ(start.getDate());}
- if (end!=null) { end=parseDate(end); end=""+end.getFullYear()+LZ(end.getMonth()+1)+LZ(end.getDate());}
- if (start==null) { this.disabledDatesExpression+="(ds<="+end+")"; }
- else if (end ==null) { this.disabledDatesExpression+="(ds>="+start+")"; }
- else { this.disabledDatesExpression+="(ds>="+start+"&&ds<="+end+")"; }
- }
-
-// Set the text to use for the "Today" link
-function CP_setTodayText(text) {
- this.todayText = text;
- }
-
-// Set the prefix to be added to all CSS classes when writing output
-function CP_setCssPrefix(val) {
- this.cssPrefix = val;
- }
-
-// Show the navigation as an dropdowns that can be manually changed
-function CP_showNavigationDropdowns() { this.isShowNavigationDropdowns = (arguments.length>0)?arguments[0]:true; }
-
-// Show the year navigation as an input box that can be manually changed
-function CP_showYearNavigationInput() { this.isShowYearNavigationInput = (arguments.length>0)?arguments[0]:true; }
-
-// Hide a calendar object
-function CP_hideCalendar() {
- if (arguments.length > 0) { window.popupWindowObjects[arguments[0]].hidePopup(); }
- else { this.hidePopup(); }
- bcfg2_check_date();
- }
-
-// Refresh the contents of the calendar display
-function CP_refreshCalendar(index) {
- var calObject = window.popupWindowObjects[index];
- if (arguments.length>1) {
- calObject.populate(calObject.getCalendar(arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]));
- }
- else {
- calObject.populate(calObject.getCalendar());
- }
- calObject.refresh();
- }
-
-// Populate the calendar and display it
-function CP_showCalendar(anchorname) {
- if (arguments.length>1) {
- if (arguments[1]==null||arguments[1]=="") {
- this.currentDate=new Date();
- }
- else {
- this.currentDate=new Date(parseDate(arguments[1]));
- }
- }
- this.populate(this.getCalendar());
- this.showPopup(anchorname);
- }
-
-// Simple method to interface popup calendar with a text-entry box
-function CP_select(inputobj, linkname, format) {
- var selectedDate=(arguments.length>3)?arguments[3]:null;
- if (!window.getDateFromFormat) {
- alert("calendar.select: To use this method you must also include 'date.js' for date formatting");
- return;
- }
- if (this.displayType!="date"&&this.displayType!="week-end") {
- alert("calendar.select: This function can only be used with displayType 'date' or 'week-end'");
- return;
- }
- if (inputobj.type!="text" && inputobj.type!="hidden" && inputobj.type!="textarea") {
- alert("calendar.select: Input object passed is not a valid form input object");
- window.CP_targetInput=null;
- return;
- }
- if (inputobj.disabled) { return; } // Can't use calendar input on disabled form input!
- window.CP_targetInput = inputobj;
- window.CP_calendarObject = this;
- this.currentDate=null;
- var time=0;
- if (selectedDate!=null) {
- time = getDateFromFormat(selectedDate,format)
- }
- else if (inputobj.value!="") {
- time = getDateFromFormat(inputobj.value,format);
- }
- if (selectedDate!=null || inputobj.value!="") {
- if (time==0) { this.currentDate=null; }
- else { this.currentDate=new Date(time); }
- }
- window.CP_dateFormat = format;
- this.showCalendar(linkname);
- }
-
-// Get style block needed to display the calendar correctly
-function getCalendarStyles() {
- var result = "";
- var p = "";
- if (this!=null && typeof(this.cssPrefix)!="undefined" && this.cssPrefix!=null && this.cssPrefix!="") { p=this.cssPrefix; }
- result += "<STYLE>\n";
- result += "."+p+"cpYearNavigation,."+p+"cpMonthNavigation { background-color:#C0C0C0; text-align:center; vertical-align:center; text-decoration:none; color:#000000; font-weight:bold; }\n";
- result += "."+p+"cpDayColumnHeader, ."+p+"cpYearNavigation,."+p+"cpMonthNavigation,."+p+"cpCurrentMonthDate,."+p+"cpCurrentMonthDateDisabled,."+p+"cpOtherMonthDate,."+p+"cpOtherMonthDateDisabled,."+p+"cpCurrentDate,."+p+"cpCurrentDateDisabled,."+p+"cpTodayText,."+p+"cpTodayTextDisabled,."+p+"cpText { font-family:arial; font-size:8pt; }\n";
- result += "TD."+p+"cpDayColumnHeader { text-align:right; border:solid thin #C0C0C0;border-width:0px 0px 1px 0px; }\n";
- result += "."+p+"cpCurrentMonthDate, ."+p+"cpOtherMonthDate, ."+p+"cpCurrentDate { text-align:right; text-decoration:none; }\n";
- result += "."+p+"cpCurrentMonthDateDisabled, ."+p+"cpOtherMonthDateDisabled, ."+p+"cpCurrentDateDisabled { color:#D0D0D0; text-align:right; text-decoration:line-through; }\n";
- result += "."+p+"cpCurrentMonthDate, .cpCurrentDate { color:#000000; }\n";
- result += "."+p+"cpOtherMonthDate { color:#808080; }\n";
- result += "TD."+p+"cpCurrentDate { color:white; background-color: #C0C0C0; border-width:1px; border:solid thin #800000; }\n";
- result += "TD."+p+"cpCurrentDateDisabled { border-width:1px; border:solid thin #FFAAAA; }\n";
- result += "TD."+p+"cpTodayText, TD."+p+"cpTodayTextDisabled { border:solid thin #C0C0C0; border-width:1px 0px 0px 0px;}\n";
- result += "A."+p+"cpTodayText, SPAN."+p+"cpTodayTextDisabled { height:20px; }\n";
- result += "A."+p+"cpTodayText { color:black; }\n";
- result += "."+p+"cpTodayTextDisabled { color:#D0D0D0; }\n";
- result += "."+p+"cpBorder { border:solid thin #808080; }\n";
- result += "</STYLE>\n";
- return result;
- }
-
-// Return a string containing all the calendar code to be displayed
-function CP_getCalendar() {
- var now = new Date();
- // Reference to window
- if (this.type == "WINDOW") { var windowref = "window.opener."; }
- else { var windowref = ""; }
- var result = "";
- // If POPUP, write entire HTML document
- if (this.type == "WINDOW") {
- result += "<HTML><HEAD><TITLE>Calendar</TITLE>"+this.getStyles()+"</HEAD><BODY MARGINWIDTH=0 MARGINHEIGHT=0 TOPMARGIN=0 RIGHTMARGIN=0 LEFTMARGIN=0>\n";
- result += '<CENTER><TABLE WIDTH=100% BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>\n';
- }
- else {
- result += '<TABLE CLASS="'+this.cssPrefix+'cpBorder" WIDTH=144 BORDER=1 BORDERWIDTH=1 CELLSPACING=0 CELLPADDING=1>\n';
- result += '<TR><TD ALIGN=CENTER>\n';
- result += '<CENTER>\n';
- }
- // Code for DATE display (default)
- // -------------------------------
- if (this.displayType=="date" || this.displayType=="week-end") {
- if (this.currentDate==null) { this.currentDate = now; }
- if (arguments.length > 0) { var month = arguments[0]; }
- else { var month = this.currentDate.getMonth()+1; }
- if (arguments.length > 1 && arguments[1]>0 && arguments[1]-0==arguments[1]) { var year = arguments[1]; }
- else { var year = this.currentDate.getFullYear(); }
- var daysinmonth= new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
- if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) {
- daysinmonth[2] = 29;
- }
- var current_month = new Date(year,month-1,1);
- var display_year = year;
- var display_month = month;
- var display_date = 1;
- var weekday= current_month.getDay();
- var offset = 0;
-
- offset = (weekday >= this.weekStartDay) ? weekday-this.weekStartDay : 7-this.weekStartDay+weekday ;
- if (offset > 0) {
- display_month--;
- if (display_month < 1) { display_month = 12; display_year--; }
- display_date = daysinmonth[display_month]-offset+1;
- }
- var next_month = month+1;
- var next_month_year = year;
- if (next_month > 12) { next_month=1; next_month_year++; }
- var last_month = month-1;
- var last_month_year = year;
- if (last_month < 1) { last_month=12; last_month_year--; }
- var date_class;
- if (this.type!="WINDOW") {
- result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
- }
- result += '<TR>\n';
- var refresh = windowref+'CP_refreshCalendar';
- var refreshLink = 'javascript:' + refresh;
- if (this.isShowNavigationDropdowns) {
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="78" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpMonthNavigation" name="cpMonth" onChange="'+refresh+'('+this.index+',this.options[this.selectedIndex].value-0,'+(year-0)+');">';
- for( var monthCounter=1; monthCounter<=12; monthCounter++ ) {
- var selected = (monthCounter==month) ? 'SELECTED' : '';
- result += '<option value="'+monthCounter+'" '+selected+'>'+this.monthNames[monthCounter-1]+'</option>';
- }
- result += '</select></TD>';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
-
- result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="56" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpYearNavigation" name="cpYear" onChange="'+refresh+'('+this.index+','+month+',this.options[this.selectedIndex].value-0);">';
- for( var yearCounter=year-this.yearSelectStartOffset; yearCounter<=year+this.yearSelectStartOffset; yearCounter++ ) {
- var selected = (yearCounter==year) ? 'SELECTED' : '';
- result += '<option value="'+yearCounter+'" '+selected+'>'+yearCounter+'</option>';
- }
- result += '</select></TD>';
- }
- else {
- if (this.isShowYearNavigation) {
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;</A></TD>';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="58"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+'</SPAN></TD>';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;</A></TD>';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
-
- result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year-1)+');">&lt;</A></TD>';
- if (this.isShowYearNavigationInput) {
- result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><INPUT NAME="cpYear" CLASS="'+this.cssPrefix+'cpYearNavigation" SIZE="4" MAXLENGTH="4" VALUE="'+year+'" onBlur="'+refresh+'('+this.index+','+month+',this.value-0);"></TD>';
- }
- else {
- result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><SPAN CLASS="'+this.cssPrefix+'cpYearNavigation">'+year+'</SPAN></TD>';
- }
- result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year+1)+');">&gt;</A></TD>';
- }
- else {
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;&lt;</A></TD>\n';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="100"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+' '+year+'</SPAN></TD>\n';
- result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;&gt;</A></TD>\n';
- }
- }
- result += '</TR></TABLE>\n';
- result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=0 CELLPADDING=1 ALIGN=CENTER>\n';
- result += '<TR>\n';
- for (var j=0; j<7; j++) {
-
- result += '<TD CLASS="'+this.cssPrefix+'cpDayColumnHeader" WIDTH="14%"><SPAN CLASS="'+this.cssPrefix+'cpDayColumnHeader">'+this.dayHeaders[(this.weekStartDay+j)%7]+'</TD>\n';
- }
- result += '</TR>\n';
- for (var row=1; row<=6; row++) {
- result += '<TR>\n';
- for (var col=1; col<=7; col++) {
- var disabled=false;
- if (this.disabledDatesExpression!="") {
- var ds=""+display_year+LZ(display_month)+LZ(display_date);
- eval("disabled=("+this.disabledDatesExpression+")");
- }
- var dateClass = "";
- if ((display_month == this.currentDate.getMonth()+1) && (display_date==this.currentDate.getDate()) && (display_year==this.currentDate.getFullYear())) {
- dateClass = "cpCurrentDate";
- }
- else if (display_month == month) {
- dateClass = "cpCurrentMonthDate";
- }
- else {
- dateClass = "cpOtherMonthDate";
- }
- if (disabled || this.disabledWeekDays[col-1]) {
- result += ' <TD CLASS="'+this.cssPrefix+dateClass+'"><SPAN CLASS="'+this.cssPrefix+dateClass+'Disabled">'+display_date+'</SPAN></TD>\n';
- }
- else {
- var selected_date = display_date;
- var selected_month = display_month;
- var selected_year = display_year;
- if (this.displayType=="week-end") {
- var d = new Date(selected_year,selected_month-1,selected_date,0,0,0,0);
- d.setDate(d.getDate() + (7-col));
- selected_year = d.getYear();
- if (selected_year < 1000) { selected_year += 1900; }
- selected_month = d.getMonth()+1;
- selected_date = d.getDate();
- }
- result += ' <TD CLASS="'+this.cssPrefix+dateClass+'"><A HREF="javascript:'+windowref+this.returnFunction+'('+selected_year+','+selected_month+','+selected_date+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+this.cssPrefix+dateClass+'">'+display_date+'</A></TD>\n';
- }
- display_date++;
- if (display_date > daysinmonth[display_month]) {
- display_date=1;
- display_month++;
- }
- if (display_month > 12) {
- display_month=1;
- display_year++;
- }
- }
- result += '</TR>';
- }
- var current_weekday = now.getDay() - this.weekStartDay;
- if (current_weekday < 0) {
- current_weekday += 7;
- }
- result += '<TR>\n';
- result += ' <TD COLSPAN=7 ALIGN=CENTER CLASS="'+this.cssPrefix+'cpTodayText">\n';
- if (this.disabledDatesExpression!="") {
- var ds=""+now.getFullYear()+LZ(now.getMonth()+1)+LZ(now.getDate());
- eval("disabled=("+this.disabledDatesExpression+")");
- }
- if (disabled || this.disabledWeekDays[current_weekday+1]) {
- result += ' <SPAN CLASS="'+this.cssPrefix+'cpTodayTextDisabled">'+this.todayText+'</SPAN>\n';
- }
- else {
- result += ' <A CLASS="'+this.cssPrefix+'cpTodayText" HREF="javascript:'+windowref+this.returnFunction+'(\''+now.getFullYear()+'\',\''+(now.getMonth()+1)+'\',\''+now.getDate()+'\');'+windowref+'CP_hideCalendar(\''+this.index+'\');">'+this.todayText+'</A>\n';
- }
- result += ' <BR>\n';
- result += ' </TD></TR></TABLE></CENTER></TD></TR></TABLE>\n';
- }
-
- // Code common for MONTH, QUARTER, YEAR
- // ------------------------------------
- if (this.displayType=="month" || this.displayType=="quarter" || this.displayType=="year") {
- if (arguments.length > 0) { var year = arguments[0]; }
- else {
- if (this.displayType=="year") { var year = now.getFullYear()-this.yearSelectStartOffset; }
- else { var year = now.getFullYear(); }
- }
- if (this.displayType!="year" && this.isShowYearNavigation) {
- result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
- result += '<TR>\n';
- result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-1)+');">&lt;&lt;</A></TD>\n';
- result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="100">'+year+'</TD>\n';
- result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+1)+');">&gt;&gt;</A></TD>\n';
- result += '</TR></TABLE>\n';
- }
- }
-
- // Code for MONTH display
- // ----------------------
- if (this.displayType=="month") {
- // If POPUP, write entire HTML document
- result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
- for (var i=0; i<4; i++) {
- result += '<TR>';
- for (var j=0; j<3; j++) {
- var monthindex = ((i*3)+j);
- result += '<TD WIDTH=33% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnMonthFunction+'('+year+','+(monthindex+1)+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+this.monthAbbreviations[monthindex]+'</A></TD>';
- }
- result += '</TR>';
- }
- result += '</TABLE></CENTER></TD></TR></TABLE>\n';
- }
-
- // Code for QUARTER display
- // ------------------------
- if (this.displayType=="quarter") {
- result += '<BR><TABLE WIDTH=120 BORDER=1 CELLSPACING=0 CELLPADDING=0 ALIGN=CENTER>\n';
- for (var i=0; i<2; i++) {
- result += '<TR>';
- for (var j=0; j<2; j++) {
- var quarter = ((i*2)+j+1);
- result += '<TD WIDTH=50% ALIGN=CENTER><BR><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnQuarterFunction+'('+year+','+quarter+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">Q'+quarter+'</A><BR><BR></TD>';
- }
- result += '</TR>';
- }
- result += '</TABLE></CENTER></TD></TR></TABLE>\n';
- }
-
- // Code for YEAR display
- // ---------------------
- if (this.displayType=="year") {
- var yearColumnSize = 4;
- result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
- result += '<TR>\n';
- result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-(yearColumnSize*2))+');">&lt;&lt;</A></TD>\n';
- result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+(yearColumnSize*2))+');">&gt;&gt;</A></TD>\n';
- result += '</TR></TABLE>\n';
- result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
- for (var i=0; i<yearColumnSize; i++) {
- for (var j=0; j<2; j++) {
- var currentyear = year+(j*yearColumnSize)+i;
- result += '<TD WIDTH=50% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnYearFunction+'('+currentyear+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+currentyear+'</A></TD>';
- }
- result += '</TR>';
- }
- result += '</TABLE></CENTER></TD></TR></TABLE>\n';
- }
- // Common
- if (this.type == "WINDOW") {
- result += "</BODY></HTML>\n";
- }
- return result;
- }
+// ===================================================================
+// Author: Matt Kruse <matt@mattkruse.com>
+// WWW: http://www.mattkruse.com/
+//
+// NOTICE: You may use this code for any purpose, commercial or
+// private, without any further permission from the author. You may
+// remove this notice from your final code if you wish, however it is
+// appreciated by the author if at least my web site address is kept.
+//
+// You may *NOT* re-distribute this code in any way except through its
+// use. That means, you can include it in your product, or your web
+// site, or any other form where the code is actually being used. You
+// may not put the plain javascript up on your site for download or
+// include it in your javascript libraries for download.
+// If you wish to share this code with others, please just point them
+// to the URL instead.
+// Please DO NOT link directly to my .js files from your site. Copy
+// the files to your server and use them there. Thank you.
+// ===================================================================
+
+// HISTORY
+// ------------------------------------------------------------------
+// Feb 7, 2005: Fixed a CSS styles to use px unit
+// March 29, 2004: Added check in select() method for the form field
+// being disabled. If it is, just return and don't do anything.
+// March 24, 2004: Fixed bug - when month name and abbreviations were
+// changed, date format still used original values.
+// January 26, 2004: Added support for drop-down month and year
+// navigation (Thanks to Chris Reid for the idea)
+// September 22, 2003: Fixed a minor problem in YEAR calendar with
+// CSS prefix.
+// August 19, 2003: Renamed the function to get styles, and made it
+// work correctly without an object reference
+// August 18, 2003: Changed showYearNavigation and
+// showYearNavigationInput to optionally take an argument of
+// true or false
+// July 31, 2003: Added text input option for year navigation.
+// Added a per-calendar CSS prefix option to optionally use
+// different styles for different calendars.
+// July 29, 2003: Fixed bug causing the Today link to be clickable
+// even though today falls in a disabled date range.
+// Changed formatting to use pure CSS, allowing greater control
+// over look-and-feel options.
+// June 11, 2003: Fixed bug causing the Today link to be unselectable
+// under certain cases when some days of week are disabled
+// March 14, 2003: Added ability to disable individual dates or date
+// ranges, display as light gray and strike-through
+// March 14, 2003: Removed dependency on graypixel.gif and instead
+/// use table border coloring
+// March 12, 2003: Modified showCalendar() function to allow optional
+// start-date parameter
+// March 11, 2003: Modified select() function to allow optional
+// start-date parameter
+/*
+DESCRIPTION: This object implements a popup calendar to allow the user to
+select a date, month, quarter, or year.
+
+COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the
+Macintosh platform.
+The calendar can be modified to work for any location in the world by
+changing which weekday is displayed as the first column, changing the month
+names, and changing the column headers for each day.
+
+USAGE:
+// Create a new CalendarPopup object of type WINDOW
+var cal = new CalendarPopup();
+
+// Create a new CalendarPopup object of type DIV using the DIV named 'mydiv'
+var cal = new CalendarPopup('mydiv');
+
+// Easy method to link the popup calendar with an input box.
+cal.select(inputObject, anchorname, dateFormat);
+// Same method, but passing a default date other than the field's current value
+cal.select(inputObject, anchorname, dateFormat, '01/02/2000');
+// This is an example call to the popup calendar from a link to populate an
+// input box. Note that to use this, date.js must also be included!!
+<A HREF="#" onClick="cal.select(document.forms[0].date,'anchorname','MM/dd/yyyy'); return false;">Select</A>
+
+// Set the type of date select to be used. By default it is 'date'.
+cal.setDisplayType(type);
+
+// When a date, month, quarter, or year is clicked, a function is called and
+// passed the details. You must write this function, and tell the calendar
+// popup what the function name is.
+// Function to be called for 'date' select receives y, m, d
+cal.setReturnFunction(functionname);
+// Function to be called for 'month' select receives y, m
+cal.setReturnMonthFunction(functionname);
+// Function to be called for 'quarter' select receives y, q
+cal.setReturnQuarterFunction(functionname);
+// Function to be called for 'year' select receives y
+cal.setReturnYearFunction(functionname);
+
+// Show the calendar relative to a given anchor
+cal.showCalendar(anchorname);
+
+// Hide the calendar. The calendar is set to autoHide automatically
+cal.hideCalendar();
+
+// Set the month names to be used. Default are English month names
+cal.setMonthNames("January","February","March",...);
+
+// Set the month abbreviations to be used. Default are English month abbreviations
+cal.setMonthAbbreviations("Jan","Feb","Mar",...);
+
+// Show navigation for changing by the year, not just one month at a time
+cal.showYearNavigation();
+
+// Show month and year dropdowns, for quicker selection of month of dates
+cal.showNavigationDropdowns();
+
+// Set the text to be used above each day column. The days start with
+// sunday regardless of the value of WeekStartDay
+cal.setDayHeaders("S","M","T",...);
+
+// Set the day for the first column in the calendar grid. By default this
+// is Sunday (0) but it may be changed to fit the conventions of other
+// countries.
+cal.setWeekStartDay(1); // week is Monday - Sunday
+
+// Set the weekdays which should be disabled in the 'date' select popup. You can
+// then allow someone to only select week end dates, or Tuedays, for example
+cal.setDisabledWeekDays(0,1); // To disable selecting the 1st or 2nd days of the week
+
+// Selectively disable individual days or date ranges. Disabled days will not
+// be clickable, and show as strike-through text on current browsers.
+// Date format is any format recognized by parseDate() in date.js
+// Pass a single date to disable:
+cal.addDisabledDates("2003-01-01");
+// Pass null as the first parameter to mean "anything up to and including" the
+// passed date:
+cal.addDisabledDates(null, "01/02/03");
+// Pass null as the second parameter to mean "including the passed date and
+// anything after it:
+cal.addDisabledDates("Jan 01, 2003", null);
+// Pass two dates to disable all dates inbetween and including the two
+cal.addDisabledDates("January 01, 2003", "Dec 31, 2003");
+
+// When the 'year' select is displayed, set the number of years back from the
+// current year to start listing years. Default is 2.
+// This is also used for year drop-down, to decide how many years +/- to display
+cal.setYearSelectStartOffset(2);
+
+// Text for the word "Today" appearing on the calendar
+cal.setTodayText("Today");
+
+// The calendar uses CSS classes for formatting. If you want your calendar to
+// have unique styles, you can set the prefix that will be added to all the
+// classes in the output.
+// For example, normal output may have this:
+// <SPAN CLASS="cpTodayTextDisabled">Today<SPAN>
+// But if you set the prefix like this:
+cal.setCssPrefix("Test");
+// The output will then look like:
+// <SPAN CLASS="TestcpTodayTextDisabled">Today<SPAN>
+// And you can define that style somewhere in your page.
+
+// When using Year navigation, you can make the year be an input box, so
+// the user can manually change it and jump to any year
+cal.showYearNavigationInput();
+
+// Set the calendar offset to be different than the default. By default it
+// will appear just below and to the right of the anchorname. So if you have
+// a text box where the date will go and and anchor immediately after the
+// text box, the calendar will display immediately under the text box.
+cal.offsetX = 20;
+cal.offsetY = 20;
+
+NOTES:
+1) Requires the functions in AnchorPosition.js and PopupWindow.js
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the
+ same. For example:
+ <A NAME="test" ID="test"> </A>
+
+3) There must be at least a space between <A> </A> for IE5.5 to see the
+ anchor tag correctly. Do not do <A></A> with no space.
+
+4) When a CalendarPopup object is created, a handler for 'onmouseup' is
+ attached to any event handler you may have already defined. Do NOT define
+ an event handler for 'onmouseup' after you define a CalendarPopup object
+ or the autoHide() will not work correctly.
+
+5) The calendar popup display uses style sheets to make it look nice.
+
+*/
+
+// CONSTRUCTOR for the CalendarPopup Object
+function CalendarPopup() {
+ var c;
+ if (arguments.length>0) {
+ c = new PopupWindow(arguments[0]);
+ }
+ else {
+ c = new PopupWindow();
+ c.setSize(150,175);
+ }
+ c.offsetX = -152;
+ c.offsetY = 25;
+ c.autoHide();
+ // Calendar-specific properties
+ c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
+ c.monthAbbreviations = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
+ c.dayHeaders = new Array("S","M","T","W","T","F","S");
+ c.returnFunction = "CP_tmpReturnFunction";
+ c.returnMonthFunction = "CP_tmpReturnMonthFunction";
+ c.returnQuarterFunction = "CP_tmpReturnQuarterFunction";
+ c.returnYearFunction = "CP_tmpReturnYearFunction";
+ c.weekStartDay = 0;
+ c.isShowYearNavigation = false;
+ c.displayType = "date";
+ c.disabledWeekDays = new Object();
+ c.disabledDatesExpression = "";
+ c.yearSelectStartOffset = 2;
+ c.currentDate = null;
+ c.todayText="Today";
+ c.cssPrefix="";
+ c.isShowNavigationDropdowns=false;
+ c.isShowYearNavigationInput=false;
+ window.CP_calendarObject = null;
+ window.CP_targetInput = null;
+ window.CP_dateFormat = "MM/dd/yyyy";
+ // Method mappings
+ c.copyMonthNamesToWindow = CP_copyMonthNamesToWindow;
+ c.setReturnFunction = CP_setReturnFunction;
+ c.setReturnMonthFunction = CP_setReturnMonthFunction;
+ c.setReturnQuarterFunction = CP_setReturnQuarterFunction;
+ c.setReturnYearFunction = CP_setReturnYearFunction;
+ c.setMonthNames = CP_setMonthNames;
+ c.setMonthAbbreviations = CP_setMonthAbbreviations;
+ c.setDayHeaders = CP_setDayHeaders;
+ c.setWeekStartDay = CP_setWeekStartDay;
+ c.setDisplayType = CP_setDisplayType;
+ c.setDisabledWeekDays = CP_setDisabledWeekDays;
+ c.addDisabledDates = CP_addDisabledDates;
+ c.setYearSelectStartOffset = CP_setYearSelectStartOffset;
+ c.setTodayText = CP_setTodayText;
+ c.showYearNavigation = CP_showYearNavigation;
+ c.showCalendar = CP_showCalendar;
+ c.hideCalendar = CP_hideCalendar;
+ c.getStyles = getCalendarStyles;
+ c.refreshCalendar = CP_refreshCalendar;
+ c.getCalendar = CP_getCalendar;
+ c.select = CP_select;
+ c.setCssPrefix = CP_setCssPrefix;
+ c.showNavigationDropdowns = CP_showNavigationDropdowns;
+ c.showYearNavigationInput = CP_showYearNavigationInput;
+ c.copyMonthNamesToWindow();
+ // Return the object
+ return c;
+ }
+function CP_copyMonthNamesToWindow() {
+ // Copy these values over to the date.js
+ if (typeof(window.MONTH_NAMES)!="undefined" && window.MONTH_NAMES!=null) {
+ window.MONTH_NAMES = new Array();
+ for (var i=0; i<this.monthNames.length; i++) {
+ window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthNames[i];
+ }
+ for (var i=0; i<this.monthAbbreviations.length; i++) {
+ window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthAbbreviations[i];
+ }
+ }
+}
+// Temporary default functions to be called when items clicked, so no error is thrown
+function CP_tmpReturnFunction(y,m,d) {
+ if (window.CP_targetInput!=null) {
+ var dt = new Date(y,m-1,d,0,0,0);
+ if (window.CP_calendarObject!=null) { window.CP_calendarObject.copyMonthNamesToWindow(); }
+ window.CP_targetInput.value = formatDate(dt,window.CP_dateFormat);
+ }
+ else {
+ alert('Use setReturnFunction() to define which function will get the clicked results!');
+ }
+ }
+function CP_tmpReturnMonthFunction(y,m) {
+ alert('Use setReturnMonthFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , month='+m);
+ }
+function CP_tmpReturnQuarterFunction(y,q) {
+ alert('Use setReturnQuarterFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , quarter='+q);
+ }
+function CP_tmpReturnYearFunction(y) {
+ alert('Use setReturnYearFunction() to define which function will get the clicked results!\nYou clicked: year='+y);
+ }
+
+// Set the name of the functions to call to get the clicked item
+function CP_setReturnFunction(name) { this.returnFunction = name; }
+function CP_setReturnMonthFunction(name) { this.returnMonthFunction = name; }
+function CP_setReturnQuarterFunction(name) { this.returnQuarterFunction = name; }
+function CP_setReturnYearFunction(name) { this.returnYearFunction = name; }
+
+// Over-ride the built-in month names
+function CP_setMonthNames() {
+ for (var i=0; i<arguments.length; i++) { this.monthNames[i] = arguments[i]; }
+ this.copyMonthNamesToWindow();
+ }
+
+// Over-ride the built-in month abbreviations
+function CP_setMonthAbbreviations() {
+ for (var i=0; i<arguments.length; i++) { this.monthAbbreviations[i] = arguments[i]; }
+ this.copyMonthNamesToWindow();
+ }
+
+// Over-ride the built-in column headers for each day
+function CP_setDayHeaders() {
+ for (var i=0; i<arguments.length; i++) { this.dayHeaders[i] = arguments[i]; }
+ }
+
+// Set the day of the week (0-7) that the calendar display starts on
+// This is for countries other than the US whose calendar displays start on Monday(1), for example
+function CP_setWeekStartDay(day) { this.weekStartDay = day; }
+
+// Show next/last year navigation links
+function CP_showYearNavigation() { this.isShowYearNavigation = (arguments.length>0)?arguments[0]:true; }
+
+// Which type of calendar to display
+function CP_setDisplayType(type) {
+ if (type!="date"&&type!="week-end"&&type!="month"&&type!="quarter"&&type!="year") { alert("Invalid display type! Must be one of: date,week-end,month,quarter,year"); return false; }
+ this.displayType=type;
+ }
+
+// How many years back to start by default for year display
+function CP_setYearSelectStartOffset(num) { this.yearSelectStartOffset=num; }
+
+// Set which weekdays should not be clickable
+function CP_setDisabledWeekDays() {
+ this.disabledWeekDays = new Object();
+ for (var i=0; i<arguments.length; i++) { this.disabledWeekDays[arguments[i]] = true; }
+ }
+
+// Disable individual dates or ranges
+// Builds an internal logical test which is run via eval() for efficiency
+function CP_addDisabledDates(start, end) {
+ if (arguments.length==1) { end=start; }
+ if (start==null && end==null) { return; }
+ if (this.disabledDatesExpression!="") { this.disabledDatesExpression+= "||"; }
+ if (start!=null) { start = parseDate(start); start=""+start.getFullYear()+LZ(start.getMonth()+1)+LZ(start.getDate());}
+ if (end!=null) { end=parseDate(end); end=""+end.getFullYear()+LZ(end.getMonth()+1)+LZ(end.getDate());}
+ if (start==null) { this.disabledDatesExpression+="(ds<="+end+")"; }
+ else if (end ==null) { this.disabledDatesExpression+="(ds>="+start+")"; }
+ else { this.disabledDatesExpression+="(ds>="+start+"&&ds<="+end+")"; }
+ }
+
+// Set the text to use for the "Today" link
+function CP_setTodayText(text) {
+ this.todayText = text;
+ }
+
+// Set the prefix to be added to all CSS classes when writing output
+function CP_setCssPrefix(val) {
+ this.cssPrefix = val;
+ }
+
+// Show the navigation as an dropdowns that can be manually changed
+function CP_showNavigationDropdowns() { this.isShowNavigationDropdowns = (arguments.length>0)?arguments[0]:true; }
+
+// Show the year navigation as an input box that can be manually changed
+function CP_showYearNavigationInput() { this.isShowYearNavigationInput = (arguments.length>0)?arguments[0]:true; }
+
+// Hide a calendar object
+function CP_hideCalendar() {
+ if (arguments.length > 0) { window.popupWindowObjects[arguments[0]].hidePopup(); }
+ else { this.hidePopup(); }
+ bcfg2_check_date();
+ }
+
+// Refresh the contents of the calendar display
+function CP_refreshCalendar(index) {
+ var calObject = window.popupWindowObjects[index];
+ if (arguments.length>1) {
+ calObject.populate(calObject.getCalendar(arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]));
+ }
+ else {
+ calObject.populate(calObject.getCalendar());
+ }
+ calObject.refresh();
+ }
+
+// Populate the calendar and display it
+function CP_showCalendar(anchorname) {
+ if (arguments.length>1) {
+ if (arguments[1]==null||arguments[1]=="") {
+ this.currentDate=new Date();
+ }
+ else {
+ this.currentDate=new Date(parseDate(arguments[1]));
+ }
+ }
+ this.populate(this.getCalendar());
+ this.showPopup(anchorname);
+ }
+
+// Simple method to interface popup calendar with a text-entry box
+function CP_select(inputobj, linkname, format) {
+ var selectedDate=(arguments.length>3)?arguments[3]:null;
+ if (!window.getDateFromFormat) {
+ alert("calendar.select: To use this method you must also include 'date.js' for date formatting");
+ return;
+ }
+ if (this.displayType!="date"&&this.displayType!="week-end") {
+ alert("calendar.select: This function can only be used with displayType 'date' or 'week-end'");
+ return;
+ }
+ if (inputobj.type!="text" && inputobj.type!="hidden" && inputobj.type!="textarea") {
+ alert("calendar.select: Input object passed is not a valid form input object");
+ window.CP_targetInput=null;
+ return;
+ }
+ if (inputobj.disabled) { return; } // Can't use calendar input on disabled form input!
+ window.CP_targetInput = inputobj;
+ window.CP_calendarObject = this;
+ this.currentDate=null;
+ var time=0;
+ if (selectedDate!=null) {
+ time = getDateFromFormat(selectedDate,format)
+ }
+ else if (inputobj.value!="") {
+ time = getDateFromFormat(inputobj.value,format);
+ }
+ if (selectedDate!=null || inputobj.value!="") {
+ if (time==0) { this.currentDate=null; }
+ else { this.currentDate=new Date(time); }
+ }
+ window.CP_dateFormat = format;
+ this.showCalendar(linkname);
+ }
+
+// Get style block needed to display the calendar correctly
+function getCalendarStyles() {
+ var result = "";
+ var p = "";
+ if (this!=null && typeof(this.cssPrefix)!="undefined" && this.cssPrefix!=null && this.cssPrefix!="") { p=this.cssPrefix; }
+ result += "<STYLE>\n";
+ result += "."+p+"cpYearNavigation,."+p+"cpMonthNavigation { background-color:#C0C0C0; text-align:center; vertical-align:center; text-decoration:none; color:#000000; font-weight:bold; }\n";
+ result += "."+p+"cpDayColumnHeader, ."+p+"cpYearNavigation,."+p+"cpMonthNavigation,."+p+"cpCurrentMonthDate,."+p+"cpCurrentMonthDateDisabled,."+p+"cpOtherMonthDate,."+p+"cpOtherMonthDateDisabled,."+p+"cpCurrentDate,."+p+"cpCurrentDateDisabled,."+p+"cpTodayText,."+p+"cpTodayTextDisabled,."+p+"cpText { font-family:arial; font-size:8pt; }\n";
+ result += "TD."+p+"cpDayColumnHeader { text-align:right; border:solid thin #C0C0C0;border-width:0px 0px 1px 0px; }\n";
+ result += "."+p+"cpCurrentMonthDate, ."+p+"cpOtherMonthDate, ."+p+"cpCurrentDate { text-align:right; text-decoration:none; }\n";
+ result += "."+p+"cpCurrentMonthDateDisabled, ."+p+"cpOtherMonthDateDisabled, ."+p+"cpCurrentDateDisabled { color:#D0D0D0; text-align:right; text-decoration:line-through; }\n";
+ result += "."+p+"cpCurrentMonthDate, .cpCurrentDate { color:#000000; }\n";
+ result += "."+p+"cpOtherMonthDate { color:#808080; }\n";
+ result += "TD."+p+"cpCurrentDate { color:white; background-color: #C0C0C0; border-width:1px; border:solid thin #800000; }\n";
+ result += "TD."+p+"cpCurrentDateDisabled { border-width:1px; border:solid thin #FFAAAA; }\n";
+ result += "TD."+p+"cpTodayText, TD."+p+"cpTodayTextDisabled { border:solid thin #C0C0C0; border-width:1px 0px 0px 0px;}\n";
+ result += "A."+p+"cpTodayText, SPAN."+p+"cpTodayTextDisabled { height:20px; }\n";
+ result += "A."+p+"cpTodayText { color:black; }\n";
+ result += "."+p+"cpTodayTextDisabled { color:#D0D0D0; }\n";
+ result += "."+p+"cpBorder { border:solid thin #808080; }\n";
+ result += "</STYLE>\n";
+ return result;
+ }
+
+// Return a string containing all the calendar code to be displayed
+function CP_getCalendar() {
+ var now = new Date();
+ // Reference to window
+ if (this.type == "WINDOW") { var windowref = "window.opener."; }
+ else { var windowref = ""; }
+ var result = "";
+ // If POPUP, write entire HTML document
+ if (this.type == "WINDOW") {
+ result += "<HTML><HEAD><TITLE>Calendar</TITLE>"+this.getStyles()+"</HEAD><BODY MARGINWIDTH=0 MARGINHEIGHT=0 TOPMARGIN=0 RIGHTMARGIN=0 LEFTMARGIN=0>\n";
+ result += '<CENTER><TABLE WIDTH=100% BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>\n';
+ }
+ else {
+ result += '<TABLE CLASS="'+this.cssPrefix+'cpBorder" WIDTH=144 BORDER=1 BORDERWIDTH=1 CELLSPACING=0 CELLPADDING=1>\n';
+ result += '<TR><TD ALIGN=CENTER>\n';
+ result += '<CENTER>\n';
+ }
+ // Code for DATE display (default)
+ // -------------------------------
+ if (this.displayType=="date" || this.displayType=="week-end") {
+ if (this.currentDate==null) { this.currentDate = now; }
+ if (arguments.length > 0) { var month = arguments[0]; }
+ else { var month = this.currentDate.getMonth()+1; }
+ if (arguments.length > 1 && arguments[1]>0 && arguments[1]-0==arguments[1]) { var year = arguments[1]; }
+ else { var year = this.currentDate.getFullYear(); }
+ var daysinmonth= new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
+ if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) {
+ daysinmonth[2] = 29;
+ }
+ var current_month = new Date(year,month-1,1);
+ var display_year = year;
+ var display_month = month;
+ var display_date = 1;
+ var weekday= current_month.getDay();
+ var offset = 0;
+
+ offset = (weekday >= this.weekStartDay) ? weekday-this.weekStartDay : 7-this.weekStartDay+weekday ;
+ if (offset > 0) {
+ display_month--;
+ if (display_month < 1) { display_month = 12; display_year--; }
+ display_date = daysinmonth[display_month]-offset+1;
+ }
+ var next_month = month+1;
+ var next_month_year = year;
+ if (next_month > 12) { next_month=1; next_month_year++; }
+ var last_month = month-1;
+ var last_month_year = year;
+ if (last_month < 1) { last_month=12; last_month_year--; }
+ var date_class;
+ if (this.type!="WINDOW") {
+ result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+ }
+ result += '<TR>\n';
+ var refresh = windowref+'CP_refreshCalendar';
+ var refreshLink = 'javascript:' + refresh;
+ if (this.isShowNavigationDropdowns) {
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="78" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpMonthNavigation" name="cpMonth" onChange="'+refresh+'('+this.index+',this.options[this.selectedIndex].value-0,'+(year-0)+');">';
+ for( var monthCounter=1; monthCounter<=12; monthCounter++ ) {
+ var selected = (monthCounter==month) ? 'SELECTED' : '';
+ result += '<option value="'+monthCounter+'" '+selected+'>'+this.monthNames[monthCounter-1]+'</option>';
+ }
+ result += '</select></TD>';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
+
+ result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="56" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpYearNavigation" name="cpYear" onChange="'+refresh+'('+this.index+','+month+',this.options[this.selectedIndex].value-0);">';
+ for( var yearCounter=year-this.yearSelectStartOffset; yearCounter<=year+this.yearSelectStartOffset; yearCounter++ ) {
+ var selected = (yearCounter==year) ? 'SELECTED' : '';
+ result += '<option value="'+yearCounter+'" '+selected+'>'+yearCounter+'</option>';
+ }
+ result += '</select></TD>';
+ }
+ else {
+ if (this.isShowYearNavigation) {
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;</A></TD>';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="58"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+'</SPAN></TD>';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;</A></TD>';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
+
+ result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year-1)+');">&lt;</A></TD>';
+ if (this.isShowYearNavigationInput) {
+ result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><INPUT NAME="cpYear" CLASS="'+this.cssPrefix+'cpYearNavigation" SIZE="4" MAXLENGTH="4" VALUE="'+year+'" onBlur="'+refresh+'('+this.index+','+month+',this.value-0);"></TD>';
+ }
+ else {
+ result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><SPAN CLASS="'+this.cssPrefix+'cpYearNavigation">'+year+'</SPAN></TD>';
+ }
+ result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year+1)+');">&gt;</A></TD>';
+ }
+ else {
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;&lt;</A></TD>\n';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="100"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+' '+year+'</SPAN></TD>\n';
+ result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;&gt;</A></TD>\n';
+ }
+ }
+ result += '</TR></TABLE>\n';
+ result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=0 CELLPADDING=1 ALIGN=CENTER>\n';
+ result += '<TR>\n';
+ for (var j=0; j<7; j++) {
+
+ result += '<TD CLASS="'+this.cssPrefix+'cpDayColumnHeader" WIDTH="14%"><SPAN CLASS="'+this.cssPrefix+'cpDayColumnHeader">'+this.dayHeaders[(this.weekStartDay+j)%7]+'</TD>\n';
+ }
+ result += '</TR>\n';
+ for (var row=1; row<=6; row++) {
+ result += '<TR>\n';
+ for (var col=1; col<=7; col++) {
+ var disabled=false;
+ if (this.disabledDatesExpression!="") {
+ var ds=""+display_year+LZ(display_month)+LZ(display_date);
+ eval("disabled=("+this.disabledDatesExpression+")");
+ }
+ var dateClass = "";
+ if ((display_month == this.currentDate.getMonth()+1) && (display_date==this.currentDate.getDate()) && (display_year==this.currentDate.getFullYear())) {
+ dateClass = "cpCurrentDate";
+ }
+ else if (display_month == month) {
+ dateClass = "cpCurrentMonthDate";
+ }
+ else {
+ dateClass = "cpOtherMonthDate";
+ }
+ if (disabled || this.disabledWeekDays[col-1]) {
+ result += ' <TD CLASS="'+this.cssPrefix+dateClass+'"><SPAN CLASS="'+this.cssPrefix+dateClass+'Disabled">'+display_date+'</SPAN></TD>\n';
+ }
+ else {
+ var selected_date = display_date;
+ var selected_month = display_month;
+ var selected_year = display_year;
+ if (this.displayType=="week-end") {
+ var d = new Date(selected_year,selected_month-1,selected_date,0,0,0,0);
+ d.setDate(d.getDate() + (7-col));
+ selected_year = d.getYear();
+ if (selected_year < 1000) { selected_year += 1900; }
+ selected_month = d.getMonth()+1;
+ selected_date = d.getDate();
+ }
+ result += ' <TD CLASS="'+this.cssPrefix+dateClass+'"><A HREF="javascript:'+windowref+this.returnFunction+'('+selected_year+','+selected_month+','+selected_date+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+this.cssPrefix+dateClass+'">'+display_date+'</A></TD>\n';
+ }
+ display_date++;
+ if (display_date > daysinmonth[display_month]) {
+ display_date=1;
+ display_month++;
+ }
+ if (display_month > 12) {
+ display_month=1;
+ display_year++;
+ }
+ }
+ result += '</TR>';
+ }
+ var current_weekday = now.getDay() - this.weekStartDay;
+ if (current_weekday < 0) {
+ current_weekday += 7;
+ }
+ result += '<TR>\n';
+ result += ' <TD COLSPAN=7 ALIGN=CENTER CLASS="'+this.cssPrefix+'cpTodayText">\n';
+ if (this.disabledDatesExpression!="") {
+ var ds=""+now.getFullYear()+LZ(now.getMonth()+1)+LZ(now.getDate());
+ eval("disabled=("+this.disabledDatesExpression+")");
+ }
+ if (disabled || this.disabledWeekDays[current_weekday+1]) {
+ result += ' <SPAN CLASS="'+this.cssPrefix+'cpTodayTextDisabled">'+this.todayText+'</SPAN>\n';
+ }
+ else {
+ result += ' <A CLASS="'+this.cssPrefix+'cpTodayText" HREF="javascript:'+windowref+this.returnFunction+'(\''+now.getFullYear()+'\',\''+(now.getMonth()+1)+'\',\''+now.getDate()+'\');'+windowref+'CP_hideCalendar(\''+this.index+'\');">'+this.todayText+'</A>\n';
+ }
+ result += ' <BR>\n';
+ result += ' </TD></TR></TABLE></CENTER></TD></TR></TABLE>\n';
+ }
+
+ // Code common for MONTH, QUARTER, YEAR
+ // ------------------------------------
+ if (this.displayType=="month" || this.displayType=="quarter" || this.displayType=="year") {
+ if (arguments.length > 0) { var year = arguments[0]; }
+ else {
+ if (this.displayType=="year") { var year = now.getFullYear()-this.yearSelectStartOffset; }
+ else { var year = now.getFullYear(); }
+ }
+ if (this.displayType!="year" && this.isShowYearNavigation) {
+ result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+ result += '<TR>\n';
+ result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-1)+');">&lt;&lt;</A></TD>\n';
+ result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="100">'+year+'</TD>\n';
+ result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+1)+');">&gt;&gt;</A></TD>\n';
+ result += '</TR></TABLE>\n';
+ }
+ }
+
+ // Code for MONTH display
+ // ----------------------
+ if (this.displayType=="month") {
+ // If POPUP, write entire HTML document
+ result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
+ for (var i=0; i<4; i++) {
+ result += '<TR>';
+ for (var j=0; j<3; j++) {
+ var monthindex = ((i*3)+j);
+ result += '<TD WIDTH=33% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnMonthFunction+'('+year+','+(monthindex+1)+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+this.monthAbbreviations[monthindex]+'</A></TD>';
+ }
+ result += '</TR>';
+ }
+ result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+ }
+
+ // Code for QUARTER display
+ // ------------------------
+ if (this.displayType=="quarter") {
+ result += '<BR><TABLE WIDTH=120 BORDER=1 CELLSPACING=0 CELLPADDING=0 ALIGN=CENTER>\n';
+ for (var i=0; i<2; i++) {
+ result += '<TR>';
+ for (var j=0; j<2; j++) {
+ var quarter = ((i*2)+j+1);
+ result += '<TD WIDTH=50% ALIGN=CENTER><BR><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnQuarterFunction+'('+year+','+quarter+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">Q'+quarter+'</A><BR><BR></TD>';
+ }
+ result += '</TR>';
+ }
+ result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+ }
+
+ // Code for YEAR display
+ // ---------------------
+ if (this.displayType=="year") {
+ var yearColumnSize = 4;
+ result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+ result += '<TR>\n';
+ result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-(yearColumnSize*2))+');">&lt;&lt;</A></TD>\n';
+ result += ' <TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+(yearColumnSize*2))+');">&gt;&gt;</A></TD>\n';
+ result += '</TR></TABLE>\n';
+ result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
+ for (var i=0; i<yearColumnSize; i++) {
+ for (var j=0; j<2; j++) {
+ var currentyear = year+(j*yearColumnSize)+i;
+ result += '<TD WIDTH=50% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnYearFunction+'('+currentyear+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+currentyear+'</A></TD>';
+ }
+ result += '</TR>';
+ }
+ result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+ }
+ // Common
+ if (this.type == "WINDOW") {
+ result += "</BODY></HTML>\n";
+ }
+ return result;
+ }
diff --git a/schemas/atom.xsd b/schemas/atom.xsd
index e25262b45..e1931439e 100644
--- a/schemas/atom.xsd
+++ b/schemas/atom.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -7,10 +8,13 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
+
<xsd:complexType name='StructureEntry'>
<xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='altsrc'/>
<xsd:attribute type='xsd:string' name='verify' use='optional'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:schema>
diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd
index bf72915d8..b226e1078 100644
--- a/schemas/bundle.xsd
+++ b/schemas/bundle.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -7,6 +8,11 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="xml.xsd"/>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
+
<xsd:include schemaLocation="atom.xsd"/>
<xsd:include schemaLocation="pathentry.xsd"/>
<xsd:include schemaLocation="rules.xsd"/>
@@ -87,6 +93,21 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
+ <xsd:element name='Client' type='GroupType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Elements within Client tags only apply to the named client
+ (or vice-versa; see #element_negate below)
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
<xsd:attribute type='xsd:string' name='name' use='required'>
<xsd:annotation>
@@ -102,23 +123,24 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
<xsd:element name='Bundle'>
- <xsd:annotation>
- <xsd:documentation>
- A bundle describes a group of inter-dependent configuration
- entries, such as the combination of packages, configuration
- files, and service activations that comprise typical Unix
- daemons. Bundles are used to add groups of configuration
- entries to the inventory of client configurations, as
- opposed to describing particular versions of those
- entries. For example, a bundle could say that the
- configuration file ``/etc/passwd`` should be included in a
- configuration, but will not describe the particular version
- of ``/etc/passwd`` that a given client will receive.
- </xsd:documentation>
- </xsd:annotation>
+ <xsd:annotation>
+ <xsd:documentation>
+ A bundle describes a group of inter-dependent configuration
+ entries, such as the combination of packages, configuration
+ files, and service activations that comprise typical Unix
+ daemons. Bundles are used to add groups of configuration
+ entries to the inventory of client configurations, as opposed
+ to describing particular versions of those entries. For
+ example, a bundle could say that the configuration file
+ ``/etc/passwd`` should be included in a configuration, but
+ will not describe the particular version of ``/etc/passwd``
+ that a given client will receive.
+ </xsd:documentation>
+ </xsd:annotation>
<xsd:complexType>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Package' type='StructureEntry'>
@@ -193,12 +215,28 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
+ <xsd:element name='Client' type='GroupType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Elements within Client tags only apply to the named client
+ (or vice-versa; see #element_negate below)
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
<xsd:attribute type='xsd:string' name='description' />
<xsd:attribute type='xsd:string' name='name'/>
<xsd:attribute type='xsd:string' name='version'/>
<xsd:attribute type='xsd:string' name='origin'/>
<xsd:attribute type='xsd:string' name='revision'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
diff --git a/schemas/decisions.xsd b/schemas/decisions.xsd
index 57ee71672..a354ec8cb 100644
--- a/schemas/decisions.xsd
+++ b/schemas/decisions.xsd
@@ -8,7 +8,7 @@
</xsd:documentation>
</xsd:annotation>
-<xsd:element name='Decisions'>
+ <xsd:element name='Decisions'>
<xsd:complexType>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Decision'>
diff --git a/schemas/genshi.xsd b/schemas/genshi.xsd
new file mode 100644
index 000000000..853b69c0d
--- /dev/null
+++ b/schemas/genshi.xsd
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/"
+ xml:lang="en"
+ targetNamespace="http://genshi.edgewall.org/">
+ <xs:annotation>
+ <xs:documentation>
+ Genshi schema
+ Chris St. Pierre
+ $Id$
+ </xs:documentation>
+ </xs:annotation>
+
+ <!-- genshi tags -->
+ <xs:element name="for" type="py:forType"/>
+ <xs:complexType name="forType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="each" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="if" type="py:ifType"/>
+ <xs:complexType name="ifType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="test" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="match" type="py:matchType"/>
+ <xs:complexType name="matchType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="path" type="xs:string" use="required"/>
+ <xs:attribute name="once" type="xs:boolean" default="false"/>
+ <xs:attribute name="buffer" type="xs:boolean" default="true"/>
+ <xs:attribute name="recursive" type="xs:boolean" default="true"/>
+ </xs:complexType>
+
+ <xs:element name="def" type="py:defType"/>
+ <xs:complexType name="defType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="function" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="with" type="py:withType"/>
+ <xs:complexType name="withType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="vars" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="replace" type="py:replaceType"/>
+ <xs:complexType name="replaceType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:element name="choose" type="py:chooseType"/>
+ <xs:complexType name="chooseType" mixed="true">
+ <xs:sequence>
+ <xs:element name="when" type="py:ifType" maxOccurs="unbounded"/>
+ <xs:element name="otherwise" type="py:otherwiseType"
+ minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="test" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="otherwiseType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:any processContents="lax"/>
+ </xs:choice>
+ </xs:complexType>
+
+ <!-- genshi attributes -->
+ <xs:attributeGroup name="genshiAttrs">
+ <xs:attribute name="ifAttr" type="xs:string"/>
+ <xs:attribute name="chooseAttr" type="xs:string"/>
+ <xs:attribute name="whenAttr" type="xs:string"/>
+ <xs:attribute name="otherwiseAttr" type="xs:string"/>
+ <xs:attribute name="forAttr" type="xs:string"/>
+ <xs:attribute name="defAttr" type="xs:string"/>
+ <xs:attribute name="matchAttr" type="xs:string"/>
+ <xs:attribute name="withAttr" type="xs:string"/>
+ <xs:attribute name="attrsAttr" type="xs:string"/>
+ <xs:attribute name="contentAttr" type="xs:string"/>
+ <xs:attribute name="replaceAttr" type="xs:string"/>
+ <xs:attribute name="stripAttr" type="xs:string"/>
+ </xs:attributeGroup>
+</xs:schema>
diff --git a/schemas/info.xsd b/schemas/info.xsd
index 4028f5c15..96ccbe56c 100644
--- a/schemas/info.xsd
+++ b/schemas/info.xsd
@@ -11,25 +11,29 @@
<xsd:complexType name='InfoType'>
<xsd:attribute name='encoding' type='xsd:string'/>
<xsd:attribute name='group' type='xsd:string'/>
- <xsd:attribute name='important' type='xsd:string'/>
+ <xsd:attribute name='important' type='xsd:boolean'/>
<xsd:attribute name='owner' type='xsd:string'/>
<xsd:attribute name='perms' type='xsd:string'/>
- <xsd:attribute name='paranoid' type='xsd:string'/>
+ <xsd:attribute name='paranoid' type='xsd:boolean'/>
</xsd:complexType>
<xsd:complexType name='GroupType'>
<xsd:choice minOccurs='1' maxOccurs='1'>
<xsd:element name='Info' type='InfoType'/>
- <xsd:element name='Group' type='GroupType' minOccurs='0' maxOccurs='unbounded'/>
+ <xsd:element name='Group' type='GroupType' minOccurs='0'
+ maxOccurs='unbounded'/>
+ <xsd:element name='Client' type='GroupType' minOccurs='0'
+ maxOccurs='unbounded'/>
</xsd:choice>
<xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='negate' />
+ <xsd:attribute type='xsd:boolean' name='negate' />
</xsd:complexType>
<xsd:element name='FileInfo'>
<xsd:complexType>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Group' type='GroupType'/>
+ <xsd:element name='Client' type='GroupType'/>
<xsd:element name='Info' type='InfoType'/>
</xsd:choice>
</xsd:complexType>
diff --git a/schemas/metadata.xsd b/schemas/metadata.xsd
index 1c2474eac..58f9e8029 100644
--- a/schemas/metadata.xsd
+++ b/schemas/metadata.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:xi="http://www.w3.org/2001/XInclude" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -10,12 +11,8 @@
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="xml.xsd"/>
-
- <xsd:simpleType name='booleanType'>
- <xsd:restriction base="xsd:string">
- <xsd:pattern value="true|false"/>
- </xsd:restriction>
- </xsd:simpleType>
+ <xsd:import namespace="http://www.w3.org/2001/XInclude"
+ schemaLocation="xinclude.xsd"/>
<xsd:complexType name='groupType'>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
@@ -30,9 +27,9 @@
</xsd:complexType>
</xsd:element>
</xsd:choice>
- <xsd:attribute type='booleanType' name='profile' use='optional'/>
- <xsd:attribute type='booleanType' name='public' use='optional'/>
- <xsd:attribute type='booleanType' name='default' use='optional'/>
+ <xsd:attribute type='xsd:boolean' name='profile' use='optional'/>
+ <xsd:attribute type='xsd:boolean' name='public' use='optional'/>
+ <xsd:attribute type='xsd:boolean' name='default' use='optional'/>
<xsd:attribute type='xsd:string' name='name' use='required'/>
<xsd:attribute type='xsd:string' name='auth' use='optional'/>
<xsd:attribute type='xsd:string' name='category' use='optional'/>
@@ -43,6 +40,7 @@
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Group' type='groupType'/>
<xsd:element name='Groups' type='groupsType'/>
+ <xsd:element ref="xi:include"/>
</xsd:choice>
<xsd:attribute name='version' type='xsd:string'/>
<xsd:attribute name='origin' type='xsd:string'/>
diff --git a/schemas/nagiosgen.xsd b/schemas/nagiosgen.xsd
new file mode 100644
index 000000000..080994cd1
--- /dev/null
+++ b/schemas/nagiosgen.xsd
@@ -0,0 +1,31 @@
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+ <xsd:annotation>
+ <xsd:documentation>
+ NagiosGen config schema for bcfg2
+ Chris St. Pierre
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:complexType name="GroupType">
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element name="Option" type="OptionType"/>
+ <xsd:element name="Group" type="GroupType"/>
+ <xsd:element name="Client" type="GroupType"/>
+ </xsd:choice>
+ <xsd:attribute type="xsd:string" name="name" use="required"/>
+ <xsd:attribute type="xsd:string" name="negate"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="OptionType" mixed="true">
+ <xsd:attribute type="xsd:string" name="name" use="required"/>
+ </xsd:complexType>
+
+ <xsd:element name="NagiosGen">
+ <xsd:complexType>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element name="Group" type="GroupType"/>
+ <xsd:element name="Client" type="GroupType"/>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema>
diff --git a/schemas/pathentry.xsd b/schemas/pathentry.xsd
index e3bdeddc6..0c27f9112 100644
--- a/schemas/pathentry.xsd
+++ b/schemas/pathentry.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -8,9 +9,13 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
+
<xsd:complexType name='PathEntry'>
<xsd:attribute type='xsd:string' name='name' use='required'/>
<xsd:attribute type='xsd:string' name='altsrc' use='optional'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
<xsd:complexType name='BoundPathEntry'>
@@ -21,6 +26,6 @@
<xsd:attribute type='xsd:string' name='prune' use='optional'/>
<xsd:attribute type='xsd:string' name='to' use='optional'/>
<xsd:attribute type='xsd:string' name='type' use='optional'/>
- <xsd:attribute type='xsd:string' name='altsrc' use='optional'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:schema>
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index 4756f0ecd..ad63cd9d2 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -9,23 +10,13 @@
</xsd:annotation>
<xsd:include schemaLocation="types.xsd"/>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
<xsd:complexType name='PackageType'>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
- <xsd:element name='Ignore'>
- <xsd:complexType>
- <xsd:attribute type='xsd:string' name='name'/>
- </xsd:complexType>
- </xsd:element>
<xsd:element name='Instance'>
<xsd:complexType>
- <xsd:choice minOccurs='0' maxOccurs='unbounded'>
- <xsd:element name='Ignore'>
- <xsd:complexType>
- <xsd:attribute type='xsd:string' name='name'/>
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
<xsd:attribute name='arch' type='xsd:string'/>
<xsd:attribute name='epoch' type='xsd:string'/>
<xsd:attribute name='version' type='xsd:string'/>
@@ -36,10 +27,18 @@
<xsd:attribute name='installed_action' type='xsd:string'/>
<xsd:attribute name='version_fail_action' type='xsd:string'/>
<xsd:attribute name='verify_fail_action' type='xsd:string'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:element>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
- <xsd:attribute type='xsd:string' name='name'/>
+ <xsd:attribute type='xsd:string' name='name' use="required"/>
<xsd:attribute type='xsd:string' name='version'/>
<xsd:attribute type='xsd:string' name='file'/>
<xsd:attribute type='xsd:string' name='verify'/>
@@ -51,5 +50,6 @@
<xsd:attribute type='xsd:string' name='bname'/>
<xsd:attribute name='pkg_checks' type='xsd:string'/>
<xsd:attribute name='verify_flags' type='xsd:string'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:schema>
diff --git a/schemas/rules.xsd b/schemas/rules.xsd
index 80036834a..bc8a4af80 100644
--- a/schemas/rules.xsd
+++ b/schemas/rules.xsd
@@ -1,5 +1,6 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
-
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
+
<xsd:annotation>
<xsd:documentation>
string enumeration definitions for bcfg2
@@ -10,49 +11,9 @@
<xsd:include schemaLocation="servicetype.xsd"/>
<xsd:include schemaLocation="types.xsd"/>
-
-<xsd:complexType name='PackageType'>
- <xsd:choice minOccurs='0' maxOccurs='unbounded'>
- <xsd:element name='Ignore'>
- <xsd:complexType>
- <xsd:attribute type='xsd:string' name='name'/>
- </xsd:complexType>
- </xsd:element>
- <xsd:element name='Instance'>
- <xsd:complexType>
- <xsd:choice minOccurs='0' maxOccurs='unbounded'>
- <xsd:element name='Ignore'>
- <xsd:complexType>
- <xsd:attribute type='xsd:string' name='name'/>
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- <xsd:attribute name='arch' type='xsd:string'/>
- <xsd:attribute name='epoch' type='xsd:string'/>
- <xsd:attribute name='version' type='xsd:string'/>
- <xsd:attribute name='release' type='xsd:string'/>
- <xsd:attribute name='simplefile' type='xsd:string'/>
- <xsd:attribute name='pkg_verify' type='xsd:string'/>
- <xsd:attribute name='verify_flags' type='xsd:string'/>
- <xsd:attribute name='installed_action' type='xsd:string'/>
- <xsd:attribute name='version_fail_action' type='xsd:string'/>
- <xsd:attribute name='verify_fail_action' type='xsd:string'/>
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- <xsd:attribute type='xsd:string' name='name' use='required'/>
- <xsd:attribute type='xsd:string' name='version' use='required'/>
- <xsd:attribute type='xsd:string' name='file'/>
- <xsd:attribute type='xsd:string' name='verify'/>
- <xsd:attribute type='xsd:string' name='simplefile'/>
- <xsd:attribute type='xsd:string' name='reloc'/>
- <xsd:attribute type='xsd:string' name='multiarch'/>
- <xsd:attribute type='xsd:string' name='srcs'/>
- <xsd:attribute type='xsd:string' name='type' use='required'/>
- <xsd:attribute type='xsd:string' name='bname'/>
- <xsd:attribute name='pkg_checks' type='xsd:string'/>
- <xsd:attribute name='verify_flags' type='xsd:string'/>
- </xsd:complexType>
+ <xsd:include schemaLocation="pkgtype.xsd"/>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
<xsd:complexType name='ActionType'>
<xsd:attribute type='ActionTimingEnum' name='timing' use='required'/>
@@ -60,6 +21,7 @@
<xsd:attribute type='ActionStatusEnum' name='status' use='required'/>
<xsd:attribute type='xsd:string' name='name' use='required'/>
<xsd:attribute type='xsd:string' name='command' use='required'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
<xsd:complexType name='PathType'>
@@ -74,19 +36,28 @@
<xsd:attribute type='xsd:string' name='group'/>
<xsd:attribute type='xsd:string' name='prune'/>
<xsd:attribute type='xsd:string' name='to'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
<xsd:complexType name='RContainerType'>
- <xsd:choice minOccurs='0' maxOccurs='unbounded'>
+ <xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='Service' type='ServiceType'/>
<xsd:element name='Package' type='PackageType'/>
<xsd:element name='Path' type='PathType'/>
<xsd:element name='Action' type='ActionType'/>
<xsd:element name='Group' type='RContainerType'/>
<xsd:element name='Client' type='RContainerType'/>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
<xsd:attribute name='name' type='xsd:string'/>
- <xsd:attribute name='negate' type='xsd:string'/>
+ <xsd:attribute name='negate' type='xsd:boolean'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
@@ -99,8 +70,16 @@
<xsd:element name='Action' type='ActionType'/>
<xsd:element name='Group' type='RContainerType'/>
<xsd:element name='Client' type='RContainerType'/>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
<xsd:attribute name='priority' type='xsd:integer' use='required'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
diff --git a/schemas/servicetype.xsd b/schemas/servicetype.xsd
index a9cb40667..07971a427 100644
--- a/schemas/servicetype.xsd
+++ b/schemas/servicetype.xsd
@@ -1,4 +1,5 @@
-<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en">
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
<xsd:annotation>
<xsd:documentation>
@@ -8,6 +9,9 @@
</xsd:documentation>
</xsd:annotation>
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
+
<xsd:complexType name='ServiceType'>
<xsd:choice minOccurs='0' maxOccurs='unbounded'>
<xsd:element name='User'>
@@ -16,6 +20,13 @@
<xsd:attribute name='mask' type='xsd:string' use='required'/>
</xsd:complexType>
</xsd:element>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
</xsd:choice>
<xsd:attribute name='name' type='xsd:string' use='required'/>
<xsd:attribute name='status' type='xsd:string' use='required'/>
@@ -29,6 +40,7 @@
<xsd:attribute name='sequence' type='xsd:string'/>
<xsd:attribute name='target' type='xsd:string'/>
<xsd:attribute name='parameters' type='xsd:string'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
</xsd:schema>
diff --git a/schemas/xinclude.xsd b/schemas/xinclude.xsd
new file mode 100644
index 000000000..de58df4e0
--- /dev/null
+++ b/schemas/xinclude.xsd
@@ -0,0 +1,41 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ targetNamespace="http://www.w3.org/2001/XInclude"
+ finalDefault="extension">
+ <xs:annotation>
+ <xs:documentation>
+ The official XInclude XML Schema document is not normative or
+ deterministic. This schema implements only the features of
+ XInclude that are used in Bcfg2 in a manner that is deterministic
+ (i.e., passes XML validation).
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:element name="include" type="xi:includeType" />
+ <xs:complexType name="includeType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded" >
+ <xs:element ref="xi:fallback" />
+ </xs:choice>
+ <xs:attribute name="href" use="optional" type="xs:anyURI"/>
+ <xs:attribute name="parse" use="optional" default="xml"
+ type="xi:parseType" />
+ <xs:attribute name="xpointer" use="optional" type="xs:string"/>
+ <xs:attribute name="encoding" use="optional" type="xs:string"/>
+ <xs:attribute name="accept" use="optional" type="xs:string"/>
+ <xs:attribute name="accept-language" use="optional" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:simpleType name="parseType">
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="xml"/>
+ <xs:enumeration value="text"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="fallback" type="xi:fallbackType" />
+ <xs:complexType name="fallbackType" mixed="true">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="xi:include"/>
+ </xs:choice>
+ </xs:complexType>
+</xs:schema>
diff --git a/schemas/xs3p.xsl b/schemas/xs3p.xsl
new file mode 100644
index 000000000..b127948be
--- /dev/null
+++ b/schemas/xs3p.xsl
@@ -0,0 +1,8503 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 2002
+
+ The software contained on this media is the property of the
+ DSTC Pty Ltd. Use of this software is strictly in accordance
+ with the license agreement in the accompanying LICENSE file.
+ If your distribution of this software does not contain a
+ LICENSE file then you have no rights to use this software
+ in any manner and should contact DSTC at the address below
+ to determine an appropriate licensing arrangement.
+
+ DSTC Pty Ltd
+ Level 7, General Purpose South
+ The University of Queensland
+ QLD 4072 Australia
+ Tel: +61 7 3365 4310
+ Fax: +61 7 3365 4311
+ Email: titanium_enquiries@dstc.edu.au
+
+ This software is being provided "AS IS" without warranty of
+ any kind. In no event shall DSTC Pty Ltd be liable for
+ damage of any kind arising out of or in connection with
+ the use or performance of this software.
+-->
+<!--
+ File:
+ xs3p.xsl
+ Description:
+ Stylesheet that generates XHTML documentation, given an XML
+ Schema document
+ Assumptions:
+ -Resulting documentation will only be displayed properly with
+ the latest browsers that support XHTML and CSS. Older
+ browsers are not supported.
+ -Assumed that XSD document conforms to the XSD recommendation.
+ No validity checking is done.
+ Constraints:
+ -Local schema components cannot contain two dashes in
+ 'documentation' elements within their 'annotation' element.
+ This is because the contents of those 'documentation'
+ elements are displayed in a separate window using Javascript.
+ This Javascript code is enclosed in comments, which do not
+ allow two dashes inside themselves.
+ Notes:
+ -Javascript code is placed within comments, even though in
+ strict XHTML, JavaScript code should be placed within CDATA
+ sections. This is because current browsers generate a syntax
+ error if the page contains CDATA sections. Placing Javascript
+ code within comments means that the code cannot contain two
+ dashes.
+ (See 'PrintJSCode' named template.)
+ Stylesheet Sections:
+ -Global Parameters
+ Specify parameters that can be set externally to customise
+ stylesheet
+ -Constants
+ Constants used by the stylesheet
+ -Main Document
+ Templates to generate the overall document and the top-level
+ sections within it
+ -Hierarchy table
+ Templates for displaying type hierarchy for simple and
+ complex types, and substitution group hierarchy for elements
+ -Properties table
+ Templates for displaying the properties of top-level schema
+ components
+ -XML Instance Representation table
+ Templates for displaying how an XML instance conforming to
+ the schema component would look like
+ -Schema Component Representation table
+ Templates for displaying the XML representation of the schema
+ component
+ -XML Pretty Printer
+ Templates for displaying arbitrary XML instances
+ -Handling Schema Component References
+ Templates for generating internal and external links
+ -General Utility Templates
+ General templates used by other templates in this stylesheet
+ To Do List:
+ -It is not clever when printing out element and attribute
+ wildcards in the XML Instance Representation tables. It prints
+ out all wildcards, rather than working out the actual wildcard
+ is from multiple wildcard instances.
+ -Same as above for simple type constraints, e.g. it doesn't
+ summarise multiple pattern constraints.
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:ppp="http://titanium.dstc.edu.au/xml/xs3p"
+ version="1.0"
+ exclude-result-prefixes="xsd ppp html">
+
+ <xsl:output
+ method="xml"
+ encoding="ISO-8859-1"
+ standalone="yes"
+ version="1.0"
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ indent="yes"/>
+
+ <xsl:key name="type" match="/xsd:schema/xsd:complexType | /xsd:schema/xsd:simpleType | /xsd:schema/xsd:redefine/xsd:complexType | /xsd:schema/xsd:redefine/xsd:simpleType" use="@name" />
+ <xsl:key name="complexType" match="/xsd:schema/xsd:complexType | /xsd:schema/xsd:redefine/xsd:complexType" use="@name" />
+ <xsl:key name="simpleType" match="/xsd:schema/xsd:simpleType | /xsd:schema/xsd:redefine/xsd:simpleType" use="@name" />
+ <xsl:key name="attributeGroup" match="/xsd:schema/xsd:attributeGroup | /xsd:schema/xsd:redefine/xsd:attributeGroup" use="@name" />
+ <xsl:key name="group" match="/xsd:schema/xsd:group | /xsd:schema/xsd:redefine/xsd:group" use="@name" />
+ <xsl:key name="attribute" match="/xsd:schema/xsd:attribute" use="@name" />
+ <xsl:key name="element" match="/xsd:schema/xsd:element" use="@name" />
+
+ <!-- ******** Global Parameters ******** -->
+
+ <!-- Title of XHTML document. -->
+ <xsl:param name="title"></xsl:param>
+
+ <!-- If 'true', sorts the top-level schema components by type,
+ then name. Otherwise, displays the components by the order that
+ they appear in the schema. -->
+ <xsl:param name="sortByComponent">true</xsl:param>
+
+ <!-- If 'true', XHTML document uses JavaScript for added
+ functionality, such as pop-up windows and information-
+ hiding.
+ Otherwise, XHTML document does not use JavaScript. -->
+ <xsl:param name="useJavaScript">true</xsl:param>
+
+ <!-- If 'true', prints all super-types in the
+ type hierarchy box.
+ Otherwise, prints the parent type only in the
+ type hierarchy box. -->
+ <xsl:param name="printAllSuperTypes">true</xsl:param>
+
+ <!-- If 'true', prints all sub-types in the
+ type hierarchy box.
+ Otherwise, prints the direct sub-types only in the
+ type hierarchy box. -->
+ <xsl:param name="printAllSubTypes">true</xsl:param>
+
+ <!-- If 'true', prints out the Glossary section. -->
+ <xsl:param name="printGlossary">true</xsl:param>
+
+ <!-- If 'true', prints out the Legend section. -->
+ <xsl:param name="printLegend">true</xsl:param>
+
+ <!-- If 'true', prints prefix matching namespace of schema
+ components in XML Instance Representation tables. -->
+ <xsl:param name="printNSPrefixes">true</xsl:param>
+
+ <!-- If 'true', searches 'included' schemas for schema components
+ when generating links and XML Instance Representation tables. -->
+ <xsl:param name="searchIncludedSchemas">false</xsl:param>
+
+ <!-- If 'true', searches 'imported' schemas for schema components
+ when generating links and XML Instance Representation tables. -->
+ <xsl:param name="searchImportedSchemas">false</xsl:param>
+
+ <!-- File containing the mapping from file locations of external
+ (e.g. included, imported, refined) schemas to file locations
+ of their XHTML documentation. -->
+ <xsl:param name="linksFile"></xsl:param>
+
+ <!-- Set the base URL for resolving links. -->
+ <xsl:param name="baseURL"></xsl:param>
+
+ <!-- Uses an external CSS stylesheet rather than using
+ internally-declared CSS properties. -->
+ <xsl:param name="externalCSSURL"></xsl:param>
+
+
+ <!-- ******** Constants ******** -->
+
+ <!-- XML Schema Namespace -->
+ <xsl:variable name="XSD_NS">http://www.w3.org/2001/XMLSchema</xsl:variable>
+
+ <!-- XML Namespace -->
+ <xsl:variable name="XML_NS">http://www.w3.org/XML/1998/namespace</xsl:variable>
+
+ <!-- Number of 'em' to indent from parent element's start tag to
+ child element's start tag -->
+ <xsl:variable name="ELEM_INDENT">1.5</xsl:variable>
+
+ <!-- Number of 'em' to indent from parent element's start tag to
+ attribute's tag -->
+ <xsl:variable name="ATTR_INDENT">0.5</xsl:variable>
+
+ <!-- Title to use if none provided -->
+ <xsl:variable name="DEFAULT_TITLE">XML Schema Documentation</xsl:variable>
+
+ <!-- Prefixes used for anchor names -->
+ <!-- Type definitions -->
+ <xsl:variable name="TYPE_PREFIX">type_</xsl:variable>
+ <!-- Attribute declarations -->
+ <xsl:variable name="ATTR_PREFIX">attribute_</xsl:variable>
+ <!-- Attribute group definitions -->
+ <xsl:variable name="ATTR_GRP_PREFIX">attributeGroup_</xsl:variable>
+ <!-- Complex type definitions -->
+ <xsl:variable name="CTYPE_PREFIX" select="$TYPE_PREFIX"/>
+ <!-- Element declarations -->
+ <xsl:variable name="ELEM_PREFIX">element_</xsl:variable>
+ <!-- Key definitions -->
+ <xsl:variable name="KEY_PREFIX">key_</xsl:variable>
+ <!-- Group definitions -->
+ <xsl:variable name="GRP_PREFIX">group_</xsl:variable>
+ <!-- Notations -->
+ <xsl:variable name="NOTA_PREFIX">notation_</xsl:variable>
+ <!-- Namespace declarations -->
+ <xsl:variable name="NS_PREFIX">ns_</xsl:variable>
+ <!-- Simple type definitions -->
+ <xsl:variable name="STYPE_PREFIX" select="$TYPE_PREFIX"/>
+ <!-- Glossary terms -->
+ <xsl:variable name="TERM_PREFIX">term_</xsl:variable>
+
+ <!-- The original schema needs to be stored because when
+ calculating links for references, the links have to be
+ relative to the original schema. See 'PrintCompRef'
+ template. -->
+ <xsl:variable name="ORIGINAL_SCHEMA" select="/xsd:schema"/>
+
+ <!-- ******** Main Document ******** -->
+
+ <!--
+ Main template that starts the process
+ -->
+ <xsl:template match="/xsd:schema">
+ <!-- Check that links file is provided if searching external
+ schemas for components. -->
+ <xsl:if test="$linksFile='' and (normalize-space(translate($searchIncludedSchemas, 'TRUE', 'true'))='true' or normalize-space(translate($searchImportedSchemas, 'TRUE', 'true'))='true')">
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">true</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+'linksFile' variable must be provided if either
+'searchIncludedSchemas' or 'searchImportedSchemas' is true.
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- Get title of document -->
+ <xsl:variable name="actualTitle">
+ <xsl:choose>
+ <xsl:when test="$title != ''">
+ <xsl:value-of select="$title"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$DEFAULT_TITLE"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <html>
+ <head>
+ <!-- Set title bar -->
+ <title><xsl:value-of select="$actualTitle"/></title>
+
+ <!-- Set content type -->
+ <meta http-equiv="Content-Type" content="text/xml; charset=iso-8859-1"/>
+
+ <!-- Set base URL to use in working out relative paths -->
+ <xsl:if test="$baseURL != ''">
+ <xsl:element name="base">
+ <xsl:attribute name="href"><xsl:value-of select="$baseURL"/></xsl:attribute>
+ </xsl:element>
+ </xsl:if>
+
+ <!-- Set CSS styles -->
+ <style type="text/css">
+ <xsl:choose>
+ <!-- Use external CSS stylesheet -->
+ <xsl:when test="$externalCSSURL != ''">
+ <xsl:text>
+@import url(</xsl:text><xsl:value-of select="$externalCSSURL"/><xsl:text>);
+</xsl:text>
+ </xsl:when>
+ <!-- Use internal CSS styles -->
+ <xsl:otherwise>
+ <xsl:call-template name="DocumentCSSStyles"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </style>
+
+ <!-- Add Javascript code to make the collapseable boxes work -->
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <xsl:call-template name="PrintJSCode">
+ <xsl:with-param name="code">
+ <xsl:call-template name="DocumentJSCode"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </head>
+ <body>
+ <!-- Title -->
+ <h1><a name="top"><xsl:value-of select="$actualTitle"/></a></h1>
+
+ <!-- Buttons for displaying printer-friendly version, and
+ expanding and collapsing all boxes -->
+ <xsl:call-template name="GlobalControlButtons"/>
+
+ <!-- Section: Table of Contents -->
+ <h2>Table of Contents</h2>
+ <xsl:apply-templates select="." mode="toc"/>
+ <xsl:call-template name="SectionFooter"/>
+
+ <!-- Section: Schema Document Properties -->
+ <h2><a name="SchemaProperties">Schema Document Properties</a></h2>
+ <!-- Sub-section: Properties table -->
+ <xsl:apply-templates select="." mode="properties"/>
+ <!-- Sub-section: Namespace Legend -->
+ <h3>Declared Namespaces</h3>
+ <xsl:apply-templates select="." mode="namespaces"/>
+ <!-- Sub-section: Schema Component Representation table -->
+ <xsl:call-template name="SchemaComponentTable">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="SectionFooter"/>
+
+ <!-- Section: Redefined Schema Components -->
+ <xsl:if test="xsd:redefine">
+ <h2><a name="Redefinitions">Redefined Schema Components</a></h2>
+ <xsl:apply-templates select="xsd:redefine/xsd:simpleType | xsd:redefine/xsd:complexType | xsd:redefine/xsd:attributeGroup | xsd:redefine/xsd:group" mode="topSection"/>
+ </xsl:if>
+
+ <!-- Sections: Top-level Schema Components -->
+ <xsl:choose>
+ <!-- Sort schema components -->
+ <xsl:when test="normalize-space(translate($sortByComponent,'TRUE','true'))='true'">
+ <!-- Declarations -->
+ <xsl:if test="xsd:attribute or xsd:element">
+ <h2><a name="SchemaDeclarations">Global Declarations</a></h2>
+ <xsl:apply-templates select="xsd:attribute | xsd:element" mode="topSection">
+ <xsl:sort select="local-name(.)" order="ascending"/>
+ <xsl:sort select="@name" order="ascending"/>
+ </xsl:apply-templates>
+ </xsl:if>
+ <!-- Definitions -->
+ <xsl:if test="xsd:attributeGroup or xsd:complexType or xsd:group or xsd:notation or xsd:simpleType">
+ <h2><a name="SchemaDefinitions">Global Definitions</a></h2>
+ <xsl:apply-templates select="xsd:attributeGroup | xsd:complexType | xsd:group | xsd:notation | xsd:simpleType" mode="topSection">
+ <xsl:sort select="local-name(.)" order="ascending"/>
+ <xsl:sort select="@name" order="ascending"/>
+ </xsl:apply-templates>
+ </xsl:if>
+ </xsl:when>
+ <!-- Display schema components as they occur -->
+ <xsl:otherwise>
+ <h2><a name="SchemaComponents">Global Schema Components</a></h2>
+ <xsl:apply-templates select="xsd:attribute | xsd:attributeGroup | xsd:complexType | xsd:element | xsd:group | xsd:notation | xsd:simpleType" mode="topSection"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Section: Legend -->
+ <xsl:if test="normalize-space(translate($printLegend,'TRUE','true'))='true'">
+ <div id="legend">
+ <h2><a name="Legend">Legend</a></h2>
+ <xsl:call-template name="Legend"/>
+ <xsl:call-template name="SectionFooter"/>
+ </div>
+ </xsl:if>
+
+ <!-- Section: Glossary -->
+ <xsl:if test="normalize-space(translate($printGlossary,'TRUE','true'))='true'">
+ <div id="glossary">
+ <h2><a name="Glossary">Glossary</a></h2>
+ <xsl:call-template name="Glossary"/>
+ <xsl:call-template name="SectionFooter"/>
+ </div>
+ </xsl:if>
+
+ <!-- Document Footer -->
+ <p class="footer">
+ <xsl:text>Generated by </xsl:text>
+ <a href="http://xml.fiforms.org/xs3p/">xs3p</a> (<a href="http://titanium.dstc.edu.au/xml/xs3p">old link</a>)
+ <xsl:text>.</xsl:text>
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <xsl:text> Last modified: </xsl:text>
+ <xsl:call-template name="PrintJSCode">
+ <xsl:with-param name="code">document.write(document.lastModified);</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </p>
+ </body>
+ </html>
+ </xsl:template>
+
+ <!--
+ Prints out the table of Declared Namespaces for the
+ current schema.
+ -->
+ <xsl:template match="xsd:schema" mode="namespaces">
+ <table class="namespaces">
+ <tr>
+ <th>Prefix</th>
+ <th>Namespace</th>
+ </tr>
+ <!-- Default namespace (no prefix) -->
+ <xsl:if test="namespace::*[local-name(.)='']">
+ <xsl:variable name="ns" select="namespace::*[local-name(.)='']"/>
+ <tr>
+ <td>
+ <a name="{$NS_PREFIX}">Default namespace</a>
+ </td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/xsd:schema/@targetNamespace and $ns=normalize-space(/xsd:schema/@targetNamespace)">
+ <span class="targetNS">
+ <xsl:value-of select="$ns"/>
+ </span>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$ns"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:if>
+ <!-- Namespaces with prefixes -->
+ <xsl:for-each select="namespace::*[local-name(.)!='']">
+ <xsl:variable name="prefix" select="local-name(.)"/>
+ <xsl:variable name="ns" select="."/>
+ <tr>
+ <td>
+ <a name="{concat($NS_PREFIX, $prefix)}">
+ <xsl:value-of select="$prefix"/>
+ </a>
+ </td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="/xsd:schema/@targetNamespace and $ns=normalize-space(/xsd:schema/@targetNamespace)">
+ <span class="targetNS">
+ <xsl:value-of select="$ns"/>
+ </span>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$ns"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Table of Contents.
+ -->
+ <xsl:template match="xsd:schema" mode="toc">
+ <ul>
+ <!-- Section: Schema Document Properties -->
+ <li>
+ <a href="#SchemaProperties">Schema Document Properties</a>
+ </li>
+
+ <!-- Section: Redefined Schema Components -->
+ <xsl:if test="xsd:redefine">
+ <li>
+ <a href="#Redefinitions">Redefined Schema Components</a>
+ </li>
+ </xsl:if>
+
+ <!-- Sections: Top-level Schema Components -->
+ <xsl:choose>
+ <!-- Sort schema components -->
+ <xsl:when test="normalize-space(translate($sortByComponent,'TRUE','true'))='true'">
+ <!-- Declarations -->
+ <xsl:if test="xsd:attribute or xsd:element">
+ <li><a href="#SchemaDeclarations">Global Declarations</a>
+ <ul>
+ <xsl:apply-templates select="xsd:attribute | xsd:element" mode="toc">
+ <xsl:sort select="local-name(.)" order="ascending"/>
+ <xsl:sort select="@name" order="ascending"/>
+ </xsl:apply-templates>
+ </ul>
+ </li>
+ </xsl:if>
+ <!-- Definitions -->
+ <xsl:if test="xsd:attributeGroup or xsd:complexType or xsd:group or xsd:notation or xsd:simpleType">
+ <li><a href="#SchemaDefinitions">Global Definitions</a>
+ <ul>
+ <xsl:apply-templates select="xsd:attributeGroup | xsd:complexType | xsd:group | xsd:notation | xsd:simpleType" mode="toc">
+ <xsl:sort select="local-name(.)" order="ascending"/>
+ <xsl:sort select="@name" order="ascending"/>
+ </xsl:apply-templates>
+ </ul>
+ </li>
+ </xsl:if>
+ </xsl:when>
+ <!-- Display schema components in order as they appear in schema -->
+ <xsl:otherwise>
+ <li><a href="#SchemaComponents">Global Schema Components</a>
+ <ul>
+ <xsl:apply-templates select="xsd:attribute | xsd:attributeGroup | xsd:complexType | xsd:element | xsd:group | xsd:notation | xsd:simpleType" mode="toc"/>
+ </ul>
+ </li>
+ </xsl:otherwise>
+ </xsl:choose>
+ </ul>
+
+ <!-- Section: Legend -->
+ <xsl:if test="normalize-space(translate($printLegend,'TRUE','true'))='true'">
+ <ul id="legendTOC" style="margin-top: 0em">
+ <li><a href="#Legend">Legend</a></li>
+ </ul>
+ </xsl:if>
+
+ <!-- Section: Glossary -->
+ <xsl:if test="normalize-space(translate($printGlossary,'TRUE','true'))='true'">
+ <ul id="glossaryTOC" style="margin-top: 0em">
+ <li><a href="#Glossary">Glossary</a></li>
+ </ul>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a link to a top-level schema component section in the
+ Table of Contents.
+ -->
+ <xsl:template match="xsd:*[@name]" mode="toc">
+ <xsl:variable name="componentID">
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <li>
+ <a href="#{$componentID}">
+ <xsl:call-template name="GetComponentDescription">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <xsl:text>: </xsl:text>
+ <strong><xsl:value-of select="@name"/></strong>
+ </a>
+ </li>
+ </xsl:template>
+
+ <!--
+ Prints out the section for a top-level schema component.
+ -->
+ <xsl:template match="xsd:*[@name]" mode="topSection">
+ <!-- Header -->
+ <xsl:call-template name="ComponentSectionHeader">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+
+ <!-- Hierarchy table (for types and elements) -->
+ <xsl:apply-templates select="." mode="hierarchy"/>
+
+ <!-- Properties table -->
+ <xsl:apply-templates select="." mode="properties"/>
+
+ <!-- XML Instance Representation table -->
+ <xsl:call-template name="SampleInstanceTable">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+
+ <!-- Schema Component Representation table -->
+ <xsl:call-template name="SchemaComponentTable">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+
+ <!-- Footer -->
+ <xsl:call-template name="SectionFooter"/>
+ </xsl:template>
+
+ <!--
+ Prints out the buttons that can expand and collapse all boxes in
+ this page.
+ -->
+ <xsl:template name="GlobalControlButtons">
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <div style="float: right;">
+ <!-- Printer-friendly Version -->
+ <div id="printerControls" style="display:none;">
+ <input type="checkbox" onclick="displayMode(this.checked)"/>
+ <xsl:text>Printer-friendly Version</xsl:text>
+ </div>
+ <xsl:call-template name="PrintJSCode">
+ <xsl:with-param name="code">
+var pc = getElementObject("printerControls");
+if (pc != null) {
+ pc.style.display="block";
+}
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- Expand/Collapse All buttons -->
+ <div id="globalControls" style="display:none">
+ <strong>XML Instance Representation:</strong><br/>
+ <span style="margin-left: 1em; white-space: nowrap">
+ <xsl:text>[ </xsl:text>
+ <a href="javascript:void(0)" onclick="expandAll(xiBoxes)">Expand All</a>
+ <xsl:text> | </xsl:text>
+ <a href="javascript:void(0)" onclick="collapseAll(xiBoxes)">Collapse All</a>
+ <xsl:text> ]</xsl:text>
+ </span><br/><br/>
+ <strong>Schema Component Representation:</strong><br/>
+ <span style="margin-left: 1em; white-space: nowrap">
+ <xsl:text>[ </xsl:text>
+ <a href="javascript:void(0)" onclick="expandAll(scBoxes)">Expand All</a>
+ <xsl:text> | </xsl:text>
+ <a href="javascript:void(0)" onclick="collapseAll(scBoxes)">Collapse All</a>
+ <xsl:text> ]</xsl:text>
+ </span>
+ </div>
+ <xsl:call-template name="PrintJSCode">
+ <xsl:with-param name="code">
+var gc = getElementObject("globalControls");
+if (gc != null) {
+ gc.style.display="block";
+}
+ </xsl:with-param>
+ </xsl:call-template>
+ </div>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the section header of a top-level schema component.
+ Param(s):
+ component (Node) required
+ Top-level schema component
+ -->
+ <xsl:template name="ComponentSectionHeader">
+ <xsl:param name="component"/>
+
+ <xsl:variable name="componentID">
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="componentDescription">
+ <xsl:call-template name="GetComponentDescription">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="componentTermRef">
+ <xsl:call-template name="GetComponentTermRef">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <h3>
+ <!-- Description -->
+ <xsl:choose>
+ <xsl:when test="$componentTermRef != ''">
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code" select="$componentTermRef"/>
+ <xsl:with-param name="term" select="$componentDescription"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$componentDescription"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>: </xsl:text>
+ <!-- Name -->
+ <a name="{$componentID}" class="name">
+ <xsl:value-of select="$component/@name"/>
+ </a>
+ </h3>
+ </xsl:template>
+
+ <!--
+ Prints out footer for top-level sections.
+ -->
+ <xsl:template name="SectionFooter">
+ <!-- Link to top of page-->
+ <div style="text-align: right; clear: both;"><a href="#top">top</a></div>
+ <hr/>
+ </xsl:template>
+
+ <!--
+ Java Script code required by the entire HTML document.
+ -->
+ <xsl:template name="DocumentJSCode">
+ <!-- Get all IDs of XML Instance Representation boxes
+ and place them in an array. -->
+ <xsl:text>/* IDs of XML Instance Representation boxes */
+</xsl:text>
+ <xsl:text>var xiBoxes = new Array(</xsl:text>
+ <xsl:for-each select="/xsd:schema/xsd:*[@name]">
+ <xsl:if test="position()!=1">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ <xsl:text>'</xsl:text>
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <xsl:text>_xibox'</xsl:text>
+ </xsl:for-each>
+ <xsl:text>);
+</xsl:text>
+ <!-- Get all IDs of Schema Component Representation boxes
+ and place them in an array. -->
+ <xsl:text>/* IDs of Schema Component Representation boxes */
+</xsl:text>
+ <xsl:text>var scBoxes = new Array('schema_scbox'</xsl:text>
+ <xsl:for-each select="/xsd:schema/xsd:*[@name]">
+ <xsl:text>, '</xsl:text>
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <xsl:text>_scbox'</xsl:text>
+ </xsl:for-each>
+ <xsl:text>);
+</xsl:text>
+ <!-- Functions -->
+ <xsl:text>
+/**
+ * Can get the ID of the button controlling
+ * a collapseable box by concatenating
+ * this string onto the ID of the box itself.
+ */
+var B_SFIX = "_button";
+
+/**
+ * Counter of documentation windows
+ * Used to give each window a unique name
+ */
+var windowCount = 0;
+
+/**
+ * Returns an element in the current HTML document.
+ *
+ * @param elementID Identifier of HTML element
+ * @return HTML element object
+ */
+function getElementObject(elementID) {
+ var elemObj = null;
+ if (document.getElementById) {
+ elemObj = document.getElementById(elementID);
+ }
+ return elemObj;
+}
+
+/**
+ * Closes a collapseable box.
+ *
+ * @param boxObj Collapseable box
+ * @param buttonObj Button controlling box
+ */
+function closeBox(boxObj, buttonObj) {
+ if (boxObj == null || buttonObj == null) {
+ // Box or button not found
+ } else {
+ // Change 'display' CSS property of box
+ boxObj.style.display="none";
+
+ // Change text of button
+ if (boxObj.style.display=="none") {
+ buttonObj.value=" + ";
+ }
+ }
+}
+
+/**
+ * Opens a collapseable box.
+ *
+ * @param boxObj Collapseable box
+ * @param buttonObj Button controlling box
+ */
+function openBox(boxObj, buttonObj) {
+ if (boxObj == null || buttonObj == null) {
+ // Box or button not found
+ } else {
+ // Change 'display' CSS property of box
+ boxObj.style.display="block";
+
+ // Change text of button
+ if (boxObj.style.display=="block") {
+ buttonObj.value=" - ";
+ }
+ }
+}
+
+/**
+ * Sets the state of a collapseable box.
+ *
+ * @param boxID Identifier of box
+ * @param open If true, box is "opened",
+ * Otherwise, box is "closed".
+ */
+function setState(boxID, open) {
+ var boxObj = getElementObject(boxID);
+ var buttonObj = getElementObject(boxID+B_SFIX);
+ if (boxObj == null || buttonObj == null) {
+ // Box or button not found
+ } else if (open) {
+ openBox(boxObj, buttonObj);
+ // Make button visible
+ buttonObj.style.display="inline";
+ } else {
+ closeBox(boxObj, buttonObj);
+ // Make button visible
+ buttonObj.style.display="inline";
+ }
+}
+
+/**
+ * Switches the state of a collapseable box, e.g.
+ * if it's opened, it'll be closed, and vice versa.
+ *
+ * @param boxID Identifier of box
+ */
+function switchState(boxID) {
+ var boxObj = getElementObject(boxID);
+ var buttonObj = getElementObject(boxID+B_SFIX);
+ if (boxObj == null || buttonObj == null) {
+ // Box or button not found
+ } else if (boxObj.style.display=="none") {
+ // Box is closed, so open it
+ openBox(boxObj, buttonObj);
+ } else if (boxObj.style.display=="block") {
+ // Box is opened, so close it
+ closeBox(boxObj, buttonObj);
+ }
+}
+
+/**
+ * Closes all boxes in a given list.
+ *
+ * @param boxList Array of box IDs
+ */
+function collapseAll(boxList) {
+ var idx;
+ for (idx = 0; idx &lt; boxList.length; idx++) {
+ var boxObj = getElementObject(boxList[idx]);
+ var buttonObj = getElementObject(boxList[idx]+B_SFIX);
+ closeBox(boxObj, buttonObj);
+ }
+}
+
+/**
+ * Open all boxes in a given list.
+ *
+ * @param boxList Array of box IDs
+ */
+function expandAll(boxList) {
+ var idx;
+ for (idx = 0; idx &lt; boxList.length; idx++) {
+ var boxObj = getElementObject(boxList[idx]);
+ var buttonObj = getElementObject(boxList[idx]+B_SFIX);
+ openBox(boxObj, buttonObj);
+ }
+}
+
+/**
+ * Makes all the control buttons of boxes appear.
+ *
+ * @param boxList Array of box IDs
+ */
+function viewControlButtons(boxList) {
+ var idx;
+ for (idx = 0; idx &lt; boxList.length; idx++) {
+ buttonObj = getElementObject(boxList[idx]+B_SFIX);
+ if (buttonObj != null) {
+ buttonObj.style.display = "inline";
+ }
+ }
+}
+
+/**
+ * Makes all the control buttons of boxes disappear.
+ *
+ * @param boxList Array of box IDs
+ */
+function hideControlButtons(boxList) {
+ var idx;
+ for (idx = 0; idx &lt; boxList.length; idx++) {
+ buttonObj = getElementObject(boxList[idx]+B_SFIX);
+ if (buttonObj != null) {
+ buttonObj.style.display = "none";
+ }
+ }
+}
+
+/**
+ * Sets the page for either printing mode
+ * or viewing mode. In printing mode, the page
+ * is made to be more readable when printing it out.
+ * In viewing mode, the page is more browsable.
+ *
+ * @param isPrinterVersion If true, display in
+ * printing mode; otherwise,
+ * in viewing mode
+ */
+function displayMode(isPrinterVersion) {
+ var obj;
+ if (isPrinterVersion) {
+ // Hide global control buttons
+ obj = getElementObject("globalControls");
+ if (obj != null) {
+ obj.style.visibility = "hidden";
+ }
+ // Hide Legend
+ obj = getElementObject("legend");
+ if (obj != null) {
+ obj.style.display = "none";
+ }
+ obj = getElementObject("legendTOC");
+ if (obj != null) {
+ obj.style.display = "none";
+ }
+ // Hide Glossary
+ obj = getElementObject("glossary");
+ if (obj != null) {
+ obj.style.display = "none";
+ }
+ obj = getElementObject("glossaryTOC");
+ if (obj != null) {
+ obj.style.display = "none";
+ }
+
+ // Expand all XML Instance Representation tables
+ expandAll(xiBoxes);
+ // Expand all Schema Component Representation tables
+ expandAll(scBoxes);
+
+ // Hide Control buttons
+ hideControlButtons(xiBoxes);
+ hideControlButtons(scBoxes);
+ } else {
+ // View global control buttons
+ obj = getElementObject("globalControls");
+ if (obj != null) {
+ obj.style.visibility = "visible";
+ }
+ // View Legend
+ obj = getElementObject("legend");
+ if (obj != null) {
+ obj.style.display = "block";
+ }
+ obj = getElementObject("legendTOC");
+ if (obj != null) {
+ obj.style.display = "block";
+ }
+ // View Glossary
+ obj = getElementObject("glossary");
+ if (obj != null) {
+ obj.style.display = "block";
+ }
+ obj = getElementObject("glossaryTOC");
+ if (obj != null) {
+ obj.style.display = "block";
+ }
+
+ // Expand all XML Instance Representation tables
+ expandAll(xiBoxes);
+ // Collapse all Schema Component Representation tables
+ collapseAll(scBoxes);
+
+ // View Control buttons
+ viewControlButtons(xiBoxes);
+ viewControlButtons(scBoxes);
+ }
+}
+
+/**
+ * Opens up a window displaying the documentation
+ * of a schema component in the XML Instance
+ * Representation table.
+ *
+ * @param compDesc Description of schema component
+ * @param compName Name of schema component
+ * @param docTextArray Array containing the paragraphs
+ * of the new document
+ */
+function viewDocumentation(compDesc, compName, docTextArray) {
+ var width = 400;
+ var height = 200;
+ var locX = 100;
+ var locY = 200;
+
+ /* Generate content */
+ var actualText = "&lt;html>";
+ actualText += "&lt;head>&lt;title>";
+ actualText += compDesc;
+ if (compName != '') {
+ actualText += ": " + compName;
+ }
+ actualText += "&lt;/title>&lt;/head>";
+ actualText += "&lt;body bgcolor=\"#FFFFEE\">";
+ // Title
+ actualText += "&lt;p style=\"font-family: Arial, sans-serif; font-size: 12pt; font-weight: bold; letter-spacing:1px;\">";
+ actualText += compDesc;
+ if (compName != '') {
+ actualText += ": &lt;span style=\"color:#006699\">" + compName + "&lt;/span>";
+ }
+ actualText += "&lt;/p>";
+ // Documentation
+ var idx;
+ for (idx = 0; idx &lt; docTextArray.length; idx++) {
+ actualText += "&lt;p style=\"font-family: Arial, sans-serif; font-size: 10pt;\">" + docTextArray[idx] + "&lt;/p>";
+ }
+ // Link to close window
+ actualText += "&lt;a href=\"javascript:void(0)\" onclick=\"window.close();\" style=\"font-family: Arial, sans-serif; font-size: 8pt;\">Close&lt;/a>";
+ actualText += "&lt;/body>&lt;/html>";
+
+ /* Display window */
+ windowCount++;
+ var docWindow = window.open("", "documentation"+windowCount, "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable,alwaysRaised,dependent,titlebar=no,width="+width+",height="+height+",screenX="+locX+",left="+locX+",screenY="+locY+",top="+locY);
+ docWindow.document.write(actualText);
+}
+</xsl:text>
+ </xsl:template>
+
+ <!--
+ CSS properties for the entire HTML document.
+ -->
+ <xsl:template name="DocumentCSSStyles">
+ <xsl:text>
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* More-configurable styles */
+
+/******** General ********/
+
+/* Document body */
+body {
+ color: Black;
+ background-color: White;
+ font-family: Arial, sans-serif;
+ font-size: 10pt;
+}
+/* Horizontal rules */
+hr {
+ color: black;
+}
+/* Document title */
+h1 {
+ font-size: 18pt;
+ letter-spacing: 2px;
+ border-bottom: 1px #ccc solid;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+/* Main section headers */
+h2 {
+ font-size: 14pt;
+ letter-spacing: 1px;
+}
+/* Sub-section headers */
+h3, h3 a, h3 span {
+ font-size: 12pt;
+ font-weight: bold;
+ color: black;
+}
+/* Table displaying the properties of the schema components or the
+ schema document itself */
+table.properties th, table.properties th a {
+ color: black;
+ background-color: #F99; /* Pink */
+}
+table.properties td {
+ background-color: #eee; /* Gray */
+}
+
+
+/******** Table of Contents Section ********/
+
+/* Controls for switching between printing and viewing modes */
+div#printerControls {
+ color: #963; /* Orange-brown */
+}
+/* Controls that can collapse or expand all XML Instance
+ Representation and Schema Component Representation boxes */
+div#globalControls {
+ border: 2px solid #999;
+}
+
+
+/******** Schema Document Properties Section ********/
+
+/* Table displaying the namespaces declared in the schema */
+table.namespaces th {
+ background-color: #ccc;
+}
+table.namespaces td {
+ background-color: #eee;
+}
+/* Target namespace of the schema */
+span.targetNS {
+ color: #06C;
+ font-weight: bold;
+}
+
+
+/******** Schema Components' Sections ********/
+
+/* Name of schema component */
+.name {
+ color: #F93; /* Orange */
+}
+
+/* Hierarchy table */
+table.hierarchy {
+ border: 2px solid #999; /* Gray */
+}
+
+/* XML Instance Representation table */
+div.sample div.contents {
+ border: 2px dashed black;
+}
+
+/* Schema Component Representation table */
+div.schemaComponent div.contents {
+ border: 2px black solid;
+}
+
+
+/******** Glossary Section ********/
+
+/* Glossary Terms */
+.glossaryTerm {
+ color: #036; /* Blue */
+}
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* Printer-version styles */
+
+@media print {
+
+/* Ensures that controls are hidden when printing */
+div#printerControls {
+ visibility: hidden;
+}
+div#globalControls {
+ visibility: hidden;
+}
+#legend {
+ display: none;
+}
+#legendTOC {
+ display: none;
+}
+#glossary {
+ display: none;
+}
+#glossaryTOC {
+ display: none;
+}
+
+}
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* Base styles */
+
+/******** General ********/
+
+/* Unordered lists */
+ul {
+ margin-left: 1.5em;
+ margin-bottom: 0em;
+}
+/* Tables */
+table {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ margin-left: 2px;
+ margin-right: 2px;
+}
+table th, table td {
+ font-size: 10pt;
+ vertical-align: top;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-left: 10px;
+ padding-right: 10px;
+}
+table th {
+ font-weight: bold;
+ text-align: left;
+}
+/* Table displaying the properties of the schema components or the
+ schema document itself */
+table.properties {
+ width: 90%;
+}
+table.properties th {
+ width: 30%;
+}
+/* Boxes that can make its content appear and disappear*/
+div.box {
+ margin: 1em;
+}
+ /* Box caption */
+div.box span.caption {
+ font-weight: bold;
+}
+ /* Button to open and close the box */
+div.box input.control {
+ width: 1.4em;
+ height: 1.4em;
+ text-align: center;
+ vertical-align: middle;
+ font-size: 11pt;
+}
+ /* Box contents */
+div.box div.contents {
+ margin-top: 3px;
+}
+
+
+/******** Table of Contents Section ********/
+
+/* Controls for switching between printing and viewing modes */
+div#printerControls {
+ white-space: nowrap;
+ font-weight: bold;
+ padding: 5px;
+ margin: 5px;
+}
+/* Controls that can collapse or expand all XML Instance
+ Representation and Schema Component Representation boxes */
+div#globalControls {
+ padding: 10px;
+ margin: 5px;
+}
+
+
+/******** Schema Document Properties Section ********/
+
+/* Table displaying the namespaces declared in the schema */
+table.namespaces th {
+}
+table.namespaces td {
+}
+/* Target namespace of the schema */
+span.targetNS {
+}
+
+
+/******** Schema Components' Sections ********/
+
+/* Name of schema component */
+.name {
+}
+
+/* Hierarchy table */
+table.hierarchy {
+ width: 90%;
+}
+table.hierarchy th {
+ font-weight: normal;
+ font-style: italic;
+ width: 20%;
+}
+table.hierarchy th, table.hierarchy td {
+ padding: 5px;
+}
+
+/* XML Instance Representation table */
+div.sample {
+ width: 90%;
+}
+div.sample div.contents {
+ padding: 5px;
+ font-family: Courier New, sans-serif;
+ font-size: 10pt;
+}
+ /* Normal elements and attributes */
+div.sample div.contents, div.sample div.contents a {
+ color: black;
+}
+ /* Group Headers */
+div.sample div.contents .group, div.sample div.contents .group a {
+ color: #999; /* Light gray */
+}
+ /* Type Information */
+div.sample div.contents .type, div.sample div.contents .type a {
+ color: #999; /* Light gray */
+}
+ /* Occurrence Information */
+div.sample div.contents .occurs, div.sample div.contents .occurs a {
+ color: #999; /* Light gray */
+}
+ /* Fixed values */
+div.sample div.contents .fixed {
+ color: #063; /* Green */
+ font-weight: bold;
+}
+ /* Simple type constraints */
+div.sample div.contents .constraint, div.sample div.contents .constraint a {
+ color: #999; /* Light gray */
+}
+ /* Elements and attributes inherited from base type */
+div.sample div.contents .inherited, div.sample div.contents .inherited a {
+ color: #666; /* Dark gray */
+}
+ /* Elements and attributes added to or changed from base type */
+div.sample div.contents .newFields {
+ font-weight: bold;
+}
+ /* Other type of information */
+div.sample div.contents .other, div.sample div.contents .other a {
+ color: #369; /* Blue */
+ font-style: italic;
+}
+ /* Link to open up window displaying documentation */
+div.sample div.contents a.documentation {
+ text-decoration: none;
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ font-weight: bold;
+ font-size: 11pt;
+ background-color: #FFD;
+ color: #069;
+}
+ /* Invert colors when hovering over link to open up window
+ displaying documentation */
+div.sample div.contents a.documentation:hover {
+ color: #FFD;
+ background-color: #069;
+}
+
+/* Schema Component Representation table */
+div.schemaComponent {
+ width: 90%;
+}
+div.schemaComponent div.contents {
+ font-family: Courier New, sans-serif;
+ font-size: 10pt;
+ padding: 5px;
+}
+ /* Syntax characters */
+div.schemaComponent div.contents {
+ color: #00f; /* blue */
+}
+ /* Element and attribute tags */
+div.schemaComponent div.contents .scTag {
+ color: #933; /* maroon */
+}
+ /* Element and attribute content */
+div.schemaComponent div.contents .scContent, div.schemaComponent div.contents .scContent a {
+ color: black;
+ font-weight: bold;
+}
+ /* Comments */
+div.schemaComponent div.contents .comment {
+ color: #999; /* Light gray */
+}
+
+/******** Legend Section ********/
+
+div#legend table, div#legend div {
+ margin-bottom: 3px;
+}
+div#legend div.hint {
+ color: #999; /* Light gray */
+ width: 90%;
+ margin-left: 1em;
+ margin-bottom: 2em;
+}
+
+
+/******** Glossary Section ********/
+
+/* Glossary Terms */
+.glossaryTerm {
+ font-weight: bold;
+}
+
+
+/******** Footer ********/
+
+.footer {
+ font-size: 8pt;
+}
+</xsl:text>
+ </xsl:template>
+
+ <!--
+ Print outs a legend describing the meaning of colors, bold items,
+ etc. in the top-level sections.
+ -->
+ <xsl:template name="Legend">
+ <!-- Header -->
+ <div style="float: left; width: 15em;">
+ <h3 style="margin-bottom: 0px;">Complex Type:</h3>
+ <div class="hint" style="margin-left: 0em;">Schema Component Type</div>
+ </div>
+ <div style="float: left; width: 15em;">
+ <h3 style="margin-bottom: 0px;"><span class="name">AusAddress</span></h3>
+ <div class="hint" style="margin-left: 0em;">Schema Component Name</div>
+ </div>
+
+ <!-- Hierarchy Table -->
+ <table class="hierarchy" style="clear : both">
+ <tr>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSuperTypes, 'TRUE', 'true'))='true'">
+ <th>Super-types:</th>
+ <td>
+ <span class="type" style="color: #0000FF; text-decoration:underline;">Address</span>
+ <xsl:text> &lt; </xsl:text>
+ <span class="current">AusAddress</span>
+ <xsl:text> (by extension)</xsl:text>
+ </td>
+ </xsl:when>
+ <xsl:otherwise>
+ <th>Parent type:</th>
+ <td>
+ <span class="type" style="color: #0000FF; text-decoration:underline;">Address</span>
+ <xsl:text> (derivation method: extension)</xsl:text>
+ </td>
+ </xsl:otherwise>
+ </xsl:choose>
+ </tr>
+ <tr>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <th>Sub-types:</th>
+ <td>
+ <ul>
+ <li>
+ <span class="type" style="color: #0000FF; text-decoration:underline;">QLDAddress</span>
+ <xsl:text> (by restriction)</xsl:text>
+ </li>
+ </ul>
+ </td>
+ </xsl:when>
+ <xsl:otherwise>
+ <th>Direct sub-types:</th>
+ <td>
+ <ul>
+ <li>
+ <span class="type" style="color: #0000FF; text-decoration:underline;">QLDAddress</span>
+ <xsl:text> (by restriction)</xsl:text>
+ </li>
+ </ul>
+ </td>
+ </xsl:otherwise>
+ </xsl:choose>
+ </tr>
+ </table>
+ <div class="hint">If this schema component is a type definition, its type hierarchy is shown in a gray-bordered box.</div>
+
+ <!-- Properties Table -->
+ <table class="properties">
+ <tr>
+ <th>Name</th>
+ <td>AusAddress</td>
+ </tr>
+ <tr>
+ <th>
+ <a title="Look up 'Abstract' in glossary" href="#term_Abstract">Abstract</a>
+ </th>
+ <td>no</td>
+ </tr>
+ </table>
+ <div class="hint">The table above displays the properties of this schema component.</div>
+
+ <!-- XML Instance Representation Table -->
+ <div class="sample box">
+ <div>
+ <span class="caption">XML Instance Representation</span>
+ </div>
+ <div class="contents">
+ <span style="margin-left: 0em">&lt;...</span>
+ <span class="newFields">
+ <span> country="<span class="fixed">Australia</span>"</span>
+ </span>
+ <xsl:text>&gt; </xsl:text><br/>
+ <span style="margin-left: 1.5em" class="inherited">&lt;unitNo&gt; <span class="type">string</span> &lt;/unitNo&gt; <span class="occurs">[0..1]</span></span><br/>
+ <span style="margin-left: 1.5em" class="inherited">&lt;houseNo&gt; <span class="type">string</span> &lt;/houseNo&gt; <span class="occurs">[1]</span></span><br/>
+ <span style="margin-left: 1.5em" class="inherited">&lt;street&gt; <span class="type">string</span> &lt;/street&gt; <span class="occurs">[1]</span></span><br/>
+ <span class="group" style="margin-left: 1.5em">Start <a title="Look up 'Choice' in glossary" href="#term_Choice">Choice</a> <span class="occurs">[1]</span></span><br/>
+ <span style="margin-left: 3em" class="inherited">&lt;city&gt; <span class="type">string</span> &lt;/city&gt; <span class="occurs">[1]</span></span><br/>
+ <span style="margin-left: 3em" class="inherited">&lt;town&gt; <span class="type">string</span> &lt;/town&gt; <span class="occurs">[1]</span></span><br/>
+ <span class="group" style="margin-left: 1.5em">End Choice</span><br/>
+ <span class="newFields">
+ <span style="margin-left: 1.5em">&lt;state&gt; <span class="type" style="text-decoration:underline;">AusStates</span> &lt;/state&gt; <span class="occurs">[1]</span></span><br/>
+ <span style="margin-left: 1.5em">&lt;postcode&gt; <span class="constraint">string &lt;&lt;<em>pattern</em> = [1-9][0-9]{3}>></span> &lt;/postcode&gt; <span class="occurs">[1]</span>
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <a href="javascript:void(0)" title="View Documentation" class="documentation" onclick="docArray = new Array('Post code must be a four-digit number.'); viewDocumentation('Element', 'postcode', docArray);">?</a>
+ </xsl:if>
+ </span><br/>
+ </span>
+ <span style="margin-left: 0em">&lt;/...&gt;</span><br/>
+ </div>
+ </div>
+ <div class="hint">
+ <p>The XML Instance Representation table above shows the schema component's content as an XML instance.</p>
+ <ul>
+ <li>The minimum and maximum occurrence of elements and attributes are provided in square brackets, e.g. [0..1].</li>
+ <li>Model group information are shown in gray, e.g. Start Choice ... End Choice.</li>
+ <li>For type derivations, the elements and attributes that have been added to or changed from the base type's content are shown in <span style="font-weight: bold">bold</span>.</li>
+ <li>If an element/attribute has a fixed value, the fixed value is shown in green, e.g. country="Australia".</li>
+ <li>Otherwise, the type of the element/attribute is displayed.
+ <ul>
+ <li>If the element/attribute's type is in the schema, a link is provided to it.</li>
+ <!--<li>An <em>E</em> symbol is shown if the element/attribute's type is located in an external schema.</li>-->
+ <li>For local simple type definitions, the constraints are displayed in angle brackets, e.g. &lt;&lt;<em>pattern</em> = [1-9][0-9]{3}>>.</li>
+ </ul>
+ </li>
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <li>If a local element/attribute has documentation, it will be displayed in a window that pops up when the question mark inside the attribute or next to the element is clicked, e.g. &lt;postcode>.</li>
+ </xsl:if>
+ </ul>
+ </div>
+
+ <!-- Schema Component Representation Table -->
+ <div class="schemaComponent box">
+ <div>
+ <span class="caption">Schema Component Representation</span>
+ </div>
+ <div class="contents">
+ <span style="margin-left: 0em">&lt;<span class="scTag">complexType</span> <span class="scTag">name</span>="<span class="scContent">AusAddress</span>"&gt;</span><br/>
+ <span style="margin-left: 1.5em">&lt;<span class="scTag">complexContent</span>&gt;</span><br/>
+ <span style="margin-left: 3em">&lt;<span class="scTag">extension</span> <span class="scTag">base</span>="<span class="scContent"><span class="type" style="text-decoration:underline;">Address</span></span>"&gt;</span><br/>
+ <span style="margin-left: 4.5em">&lt;<span class="scTag">sequence</span>&gt;</span><br/>
+ <span style="margin-left: 6em">&lt;<span class="scTag">element</span> <span class="scTag">name</span>="<span class="scContent">state</span>" <span class="scTag">type</span>="<span class="scContent"><span class="type" style="text-decoration:underline;">AusStates</span></span>"/&gt;</span><br/>
+ <span style="margin-left: 6em">&lt;<span class="scTag">element</span> <span class="scTag">name</span>="<span class="scContent">postcode</span>"&gt;</span><br/>
+ <span style="margin-left: 7.5em">&lt;<span class="scTag">simpleType</span>&gt;</span><br/>
+ <span style="margin-left: 9em">&lt;<span class="scTag">restriction</span> <span class="scTag">base</span>="<span class="scContent"><span class="type">string</span></span>"&gt;</span><br/>
+ <span style="margin-left: 10.5em">&lt;<span class="scTag">pattern</span> <span class="scTag">value</span>="<span class="scContent">[1-9][0-9]{3}</span>"/&gt;</span><br/>
+ <span style="margin-left: 9em">&lt;/<span class="scTag">restriction</span>&gt;</span><br/>
+ <span style="margin-left: 7.5em">&lt;/<span class="scTag">simpleType</span>&gt;</span><br/>
+ <span style="margin-left: 6em">&lt;/<span class="scTag">element</span>&gt;</span><br/>
+ <span style="margin-left: 4.5em">&lt;/<span class="scTag">sequence</span>&gt;</span><br/>
+ <span style="margin-left: 4.5em">&lt;<span class="scTag">attribute</span> <span class="scTag">name</span>="<span class="scContent">country</span>" <span class="scTag">type</span>="<span class="scContent"><span class="type">string</span></span>" <span class="scTag">fixed</span>="<span class="scContent">Australia</span>"/&gt;</span><br/>
+ <span style="margin-left: 3em">&lt;/<span class="scTag">extension</span>&gt;</span><br/>
+ <span style="margin-left: 1.5em">&lt;/<span class="scTag">complexContent</span>&gt;</span><br/>
+ <span style="margin-left: 0em">&lt;/<span class="scTag">complexType</span>&gt;</span><br/>
+ </div>
+ </div>
+ <div class="hint">The Schema Component Representation table above displays the underlying XML representation of the schema component. (Annotations are not shown.)</div>
+ </xsl:template>
+
+ <!--
+ Print outs all terms for the glossary section.
+ -->
+ <xsl:template name="Glossary">
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Abstract</xsl:with-param>
+ <xsl:with-param name="term">Abstract</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to complex type definitions and element declarations).</xsl:text>
+ <xsl:text> An abstract element or complex type cannot used to validate an element instance.</xsl:text>
+ <xsl:text> If there is a reference to an abstract element, only element declarations that can substitute the abstract element can be used to validate the instance.</xsl:text>
+ <xsl:text> For references to abstract type definitions, only derived types can be used.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">All</xsl:with-param>
+ <xsl:with-param name="term">All Model Group</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Child elements can be provided </xsl:text>
+ <em>
+ <xsl:text>in any order</xsl:text>
+ </em>
+ <xsl:text> in instances.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#element-all</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Choice</xsl:with-param>
+ <xsl:with-param name="term">Choice Model Group</xsl:with-param>
+ <xsl:with-param name="description">
+ <em>
+ <xsl:text>Only one</xsl:text>
+ </em>
+ <xsl:text> from the list of child elements and model groups can be provided in instances.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#element-choice</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">CollapseWS</xsl:with-param>
+ <xsl:with-param name="term">Collapse Whitespace Policy</xsl:with-param>
+ <xsl:with-param name="description">Replace tab, line feed, and carriage return characters with space character (Unicode character 32). Then, collapse contiguous sequences of space characters into single space character, and remove leading and trailing space characters.</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">ElemBlock</xsl:with-param>
+ <xsl:with-param name="term">Disallowed Substitutions</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to element declarations).</xsl:text>
+ <xsl:text> If </xsl:text>
+ <em>substitution</em>
+ <xsl:text> is specified, then </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">SubGroup</xsl:with-param>
+ <xsl:with-param name="term">substitution group</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text> members cannot be used in place of the given element declaration to validate element instances.</xsl:text>
+
+ <xsl:text> If </xsl:text>
+ <em>derivation methods</em>
+ <xsl:text>, e.g. extension, restriction, are specified, then the given element declaration will not validate element instances that have types derived from the element declaration's type using the specified derivation methods.</xsl:text>
+ <xsl:text> Normally, element instances can override their declaration's type by specifying an </xsl:text>
+ <code>xsi:type</code>
+ <xsl:text> attribute.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Key</xsl:with-param>
+ <xsl:with-param name="term">Key Constraint</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Like </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Unique</xsl:with-param>
+ <xsl:with-param name="term">Uniqueness Constraint</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>, but additionally requires that the specified value(s) must be provided.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">KeyRef</xsl:with-param>
+ <xsl:with-param name="term">Key Reference Constraint</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Ensures that the specified value(s) must match value(s) from a </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Key</xsl:with-param>
+ <xsl:with-param name="term">Key Constraint</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text> or </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Unique</xsl:with-param>
+ <xsl:with-param name="term">Uniqueness Constraint</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">ModelGroup</xsl:with-param>
+ <xsl:with-param name="term">Model Group</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Groups together element content, specifying the order in which the element content can occur and the number of times the group of element content may be repeated.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#Model_Groups</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Nillable</xsl:with-param>
+ <xsl:with-param name="term">Nillable</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to element declarations). </xsl:text>
+ <xsl:text>If an element declaration is nillable, instances can use the </xsl:text>
+ <code>xsi:nil</code>
+ <xsl:text> attribute.</xsl:text>
+ <xsl:text> The </xsl:text>
+ <code>xsi:nil</code>
+ <xsl:text> attribute is the boolean attribute, </xsl:text>
+ <em>nil</em>
+ <xsl:text>, from the </xsl:text>
+ <em>http://www.w3.org/2001/XMLSchema-instance</em>
+ <xsl:text> namespace.</xsl:text>
+ <xsl:text> If an element instance has an </xsl:text>
+ <code>xsi:nil</code>
+ <xsl:text> attribute set to true, it can be left empty, even though its element declaration may have required content.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Notation</xsl:with-param>
+ <xsl:with-param name="term">Notation</xsl:with-param>
+ <xsl:with-param name="description">A notation is used to identify the format of a piece of data. Values of elements and attributes that are of type, NOTATION, must come from the names of declared notations.</xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#cNotation_Declarations</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">PreserveWS</xsl:with-param>
+ <xsl:with-param name="term">Preserve Whitespace Policy</xsl:with-param>
+ <xsl:with-param name="description">Preserve whitespaces exactly as they appear in instances.</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">TypeFinal</xsl:with-param>
+ <xsl:with-param name="term">Prohibited Derivations</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to type definitions). </xsl:text>
+ <xsl:text>Derivation methods that cannot be used to create sub-types from a given type definition.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">TypeBlock</xsl:with-param>
+ <xsl:with-param name="term">Prohibited Substitutions</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to complex type definitions). </xsl:text>
+ <xsl:text>Prevents sub-types that have been derived using the specified derivation methods from validating element instances in place of the given type definition.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">ReplaceWS</xsl:with-param>
+ <xsl:with-param name="term">Replace Whitespace Policy</xsl:with-param>
+ <xsl:with-param name="description">Replace tab, line feed, and carriage return characters with space character (Unicode character 32).</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Sequence</xsl:with-param>
+ <xsl:with-param name="term">Sequence Model Group</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Child elements and model groups must be provided </xsl:text>
+ <em>
+ <xsl:text>in the specified order</xsl:text>
+ </em>
+ <xsl:text> in instances.</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#element-sequence</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">SubGroup</xsl:with-param>
+ <xsl:with-param name="term">Substitution Group</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>Elements that are </xsl:text>
+ <em>
+ <xsl:text>members</xsl:text>
+ </em>
+ <xsl:text> of a substitution group can be used wherever the </xsl:text>
+ <em>
+ <xsl:text>head</xsl:text>
+ </em>
+ <xsl:text> element of the substitution group is referenced.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">ElemFinal</xsl:with-param>
+ <xsl:with-param name="term">Substitution Group Exclusions</xsl:with-param>
+ <xsl:with-param name="description">
+ <xsl:text>(Applies to element declarations). </xsl:text>
+ <xsl:text>Prohibits element declarations from nominating themselves as being able to substitute a given element declaration, if they have types that are derived from the original element's type using the specified derivation methods.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">TargetNS</xsl:with-param>
+ <xsl:with-param name="term">Target Namespace</xsl:with-param>
+ <xsl:with-param name="description">The target namespace identifies the namespace that components in this schema belongs to. If no target namespace is provided, then the schema components do not belong to any namespace.</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="PrintGlossaryTerm">
+ <xsl:with-param name="code">Unique</xsl:with-param>
+ <xsl:with-param name="term">Uniqueness Constraint</xsl:with-param>
+ <xsl:with-param name="description">Ensures uniqueness of an element/attribute value, or a combination of values, within a specified scope.</xsl:with-param>
+ <xsl:with-param name="link">http://www.w3.org/TR/xmlschema-1/#cIdentity-constraint_Definitions</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out a term in the glossary section.
+ Param(s):
+ code (String) required
+ Unique ID of glossary term
+ term (String) required
+ Glossary term
+ description (Result Tree Fragment) required
+ Meaning of term; may contain HTML tags and links
+ link (String) optional
+ URI containing more info about term
+ -->
+ <xsl:template name="PrintGlossaryTerm">
+ <xsl:param name="code"/>
+ <xsl:param name="term"/>
+ <xsl:param name="description"/>
+ <xsl:param name="link"/>
+
+ <p>
+ <span class="glossaryTerm">
+ <a name="{concat($TERM_PREFIX, $code)}"><xsl:value-of select="$term"/></a>
+ <xsl:text> </xsl:text>
+ </span>
+ <xsl:copy-of select="$description"/>
+ <xsl:if test="$link != ''">
+ <xsl:text> See: </xsl:text>
+ <xsl:call-template name="PrintURI">
+ <xsl:with-param name="uri" select="$link"/>
+ </xsl:call-template>
+ <xsl:text>.</xsl:text>
+ </xsl:if>
+ </p>
+ </xsl:template>
+
+
+ <!-- ******** Hierarchy table ******** -->
+
+ <!--
+ Prints out substitution group hierarchy for
+ element declarations.
+ -->
+ <xsl:template match="xsd:element" mode="hierarchy">
+ <!--
+ Find out members of substitution group that this element
+ heads.
+ -->
+ <xsl:variable name="members">
+ <ul>
+ <xsl:call-template name="PrintSGroupMembers">
+ <xsl:with-param name="element" select="."/>
+ </xsl:call-template>
+ </ul>
+ </xsl:variable>
+ <xsl:variable name="hasMembers">
+ <xsl:if test="normalize-space($members)!=''">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <!-- Print hierarchy table -->
+ <xsl:if test="@substitutionGroup or normalize-space($hasMembers)='true'">
+ <table class="hierarchy">
+ <tr>
+ <td>
+ <ul>
+ <!-- Print substitution group that this element belongs to -->
+ <xsl:if test="@substitutionGroup">
+ <li>
+ <em>This element can be used wherever the following element is referenced:</em>
+ <ul>
+ <li>
+ <xsl:call-template name="PrintElementRef">
+ <xsl:with-param name="ref" select="@substitutionGroup"/>
+ </xsl:call-template>
+ </li>
+ </ul>
+ </li>
+ </xsl:if>
+ <!-- Print substitution group that this element heads -->
+ <xsl:if test="normalize-space($hasMembers)='true'">
+ <li>
+ <em>The following elements can be used wherever this element is referenced:</em>
+ <xsl:copy-of select="$members"/>
+ </li>
+ </xsl:if>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out Hierarchy table for complex type definitions.
+ -->
+ <xsl:template match="xsd:complexType" mode="hierarchy">
+ <table class="hierarchy">
+ <!-- Print super types -->
+ <tr>
+ <th>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSuperTypes, 'TRUE', 'true'))='true'">
+ <xsl:text>Super-types:</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Parent type:</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </th>
+ <td>
+ <xsl:choose>
+ <xsl:when test="xsd:simpleContent or xsd:complexContent">
+ <xsl:call-template name="PrintSupertypes">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>None</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <!-- Print sub types -->
+ <tr>
+ <th>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <xsl:text>Sub-types:</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Direct sub-types:</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </th>
+ <td>
+ <xsl:call-template name="PrintComplexSubtypes">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out Hierarchy table for simple type definitions.
+ -->
+ <xsl:template match="xsd:simpleType" mode="hierarchy">
+ <table class="hierarchy">
+ <!-- Print super types -->
+ <tr>
+ <th>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSuperTypes, 'TRUE', 'true'))='true'">
+ <xsl:text>Super-types:</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Parent type:</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </th>
+ <td>
+ <xsl:choose>
+ <xsl:when test="xsd:restriction">
+ <xsl:call-template name="PrintSupertypes">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>None</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <!-- Print sub types -->
+ <tr>
+ <th>
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <xsl:text>Sub-types:</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Direct sub-types:</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </th>
+ <td>
+ <xsl:call-template name="PrintSimpleSubtypes">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <!--
+ Unmatched template for 'hierarchy' mode
+ -->
+ <xsl:template match="*" mode="hierarchy"/>
+
+ <!--
+ Prints out members, if any, of the substitution group that a
+ given element declaration heads.
+ Assumes it will be called within XHTML <ul> tags.
+ Param(s):
+ element (Node) required
+ Top-level element declaration
+ elementList (String) optional
+ List of elements in this call chain. Name of element starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSGroupMembers">
+ <xsl:param name="element"/>
+ <xsl:param name="elementList"/>
+
+ <xsl:variable name="elemName" select="normalize-space($element/@name)"/>
+ <xsl:choose>
+ <xsl:when test="contains($elementList, concat('*', $elemName, '+'))">
+ <!-- Circular element substitution group hierarchy -->
+ <li>
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">false</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+ <xsl:text>Circular element reference to: </xsl:text>
+ <xsl:value-of select="$elemName"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </li>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Get 'block' attribute. -->
+ <xsl:variable name="block">
+ <xsl:call-template name="PrintBlockSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="$element/@block">
+ <xsl:value-of select="$element/@block"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@blockDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:for-each select="/xsd:schema/xsd:element[normalize-space(@substitutionGroup)=$elemName or normalize-space(substring-after(@substitutionGroup, ':'))=$elemName]">
+ <li>
+ <xsl:call-template name="PrintElementRef">
+ <xsl:with-param name="name" select="@name"/>
+ </xsl:call-template>
+ </li>
+ <!-- Recursively find members of a substitution group that
+ current element in list might head, since substitution
+ groups are transitive (unless 'substitution' is
+ blocked).
+ -->
+ <xsl:if test="not(contains($block, 'substitution'))">
+ <xsl:call-template name="PrintSGroupMembers">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="elementList" select="concat($elementList, '*', $elemName, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out the super types of a given type definition.
+ Param(s):
+ type (Node) required
+ Type definition
+ isCallingType (boolean) optional
+ If true, 'type' is the type definition that starts
+ this call. Otherwise, this is a recursive call from
+ 'PrintSupertypes' itself.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSupertypes">
+ <xsl:param name="type"/>
+ <xsl:param name="isCallingType">true</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:variable name="typeName" select="$type/@name"/>
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="contains($typeList, concat('*', $typeName, '+'))">
+ <!-- Note: Error message will be written out in the Sample Instance table. -->
+ <xsl:value-of select="$typeName"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Get base type reference -->
+ <xsl:variable name="baseTypeRef">
+ <xsl:choose>
+ <!-- Complex type definition -->
+ <xsl:when test="local-name($type)='complexType'">
+ <xsl:choose>
+ <xsl:when test="$type/xsd:simpleContent/xsd:extension">
+ <xsl:value-of select="$type/xsd:simpleContent/xsd:extension/@base"/>
+ </xsl:when>
+ <xsl:when test="$type/xsd:simpleContent/xsd:restriction">
+ <xsl:value-of select="$type/xsd:simpleContent/xsd:restriction/@base"/>
+ </xsl:when>
+ <xsl:when test="$type/xsd:complexContent/xsd:extension">
+ <xsl:value-of select="$type/xsd:complexContent/xsd:extension/@base"/>
+ </xsl:when>
+ <xsl:when test="$type/xsd:complexContent/xsd:restriction">
+ <xsl:value-of select="$type/xsd:complexContent/xsd:restriction/@base"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- Simple type definition -->
+ <xsl:when test="local-name($type)='simpleType'">
+ <xsl:choose>
+ <xsl:when test="$type/xsd:restriction/@base">
+ <xsl:value-of select="$type/xsd:restriction/@base"/>
+ </xsl:when>
+ <xsl:when test="$type/xsd:restriction/xsd:simpleType">
+ <xsl:text>Local type definition</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get derivation method -->
+ <xsl:variable name="derive">
+ <!-- Complex type definition -->
+ <xsl:choose>
+ <xsl:when test="local-name($type)='complexType'">
+ <xsl:choose>
+ <xsl:when test="$type/xsd:simpleContent/xsd:extension or $type/xsd:complexContent/xsd:extension">
+ <xsl:text>extension</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type/xsd:simpleContent/xsd:restriction or $type/xsd:complexContent/xsd:restriction">
+ <xsl:text>restriction</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- Simple type definition -->
+ <xsl:when test="local-name($type)='simpleType'">
+ <xsl:text>restriction</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Print out entire hierarchy -->
+ <xsl:when test="normalize-space(translate($printAllSuperTypes, 'TRUE', 'true'))='true'">
+ <xsl:choose>
+ <xsl:when test="normalize-space($baseTypeRef)='Local type definition'">
+ <xsl:value-of select="$baseTypeRef"/>
+ <!-- Symbol to indicate type derivation-->
+ <xsl:text> &lt; </xsl:text>
+ </xsl:when>
+ <xsl:when test="normalize-space($baseTypeRef)!=''">
+ <!-- Get base type name from reference -->
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Get base type definition from schema -->
+ <xsl:variable name="baseType" select="key('type', $baseTypeName)"/>
+ <xsl:choose>
+ <!-- Base type was found in this schema -->
+ <xsl:when test="$baseType">
+ <!-- Make recursive call to print out base type and itself -->
+ <xsl:call-template name="PrintSupertypes">
+ <xsl:with-param name="type" select="$baseType"/>
+ <xsl:with-param name="isCallingType">false</xsl:with-param>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $typeName, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Base type was not found in this schema -->
+ <xsl:otherwise>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Symbol to indicate type derivation -->
+ <xsl:text> &lt; </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- IGNORE: Base type may not be exist probably because
+ current type does not be derived from another type.
+ -->
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Print out current type's name -->
+ <xsl:choose>
+ <xsl:when test="$isCallingType='true'">
+ <strong><xsl:value-of select="$typeName"/></strong>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="name" select="$typeName"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Print out derivation method -->
+ <xsl:if test="$derive != ''">
+ <xsl:text> (by </xsl:text>
+ <xsl:value-of select="$derive"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <!-- Print out parent type only -->
+ <xsl:otherwise>
+ <!-- Print out base type reference -->
+ <xsl:choose>
+ <xsl:when test="normalize-space($baseTypeRef)='Local type definition'">
+ <xsl:value-of select="$baseTypeRef"/>
+ </xsl:when>
+ <xsl:when test="$baseTypeRef!=''">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- IGNORE: Base type may not be exist probably because
+ current type does not be derived from another type.
+ -->
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Print out derivation method -->
+ <xsl:if test="$derive != ''">
+ <xsl:text> (derivation method: </xsl:text>
+ <xsl:value-of select="$derive"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out the sub types of a given complex type definition.
+ Param(s):
+ type (Node) required
+ Complex type definition
+ isCallingType (boolean) optional
+ If true, 'type' is the type definition that starts this
+ call. Otherwise, this is a recursive call from
+ 'PrintComplexSubtypes' itself.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintComplexSubtypes">
+ <xsl:param name="type"/>
+ <xsl:param name="isCallingType">true</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:variable name="typeName" select="normalize-space($type/@name)"/>
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="contains($typeList, concat('*', $typeName, '+'))">
+ <!-- Do nothing. Note: Error message will be written out in the Sample Instance table. -->
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Find sub types -->
+ <xsl:variable name="subTypes">
+ <ul>
+ <xsl:for-each select="/xsd:schema/xsd:complexType/xsd:complexContent/xsd:restriction[normalize-space(@base)=$typeName or normalize-space(substring-after(@base, ':'))=$typeName] | /xsd:schema/xsd:complexType/xsd:complexContent/xsd:extension[normalize-space(@base)=$typeName or normalize-space(substring-after(@base, ':'))=$typeName]">
+ <li>
+ <xsl:variable name="subType" select="../.."/>
+ <!-- Write out type name -->
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="name" select="$subType/@name"/>
+ </xsl:call-template>
+ <!-- Write derivation method -->
+ <xsl:text> (by </xsl:text>
+ <xsl:value-of select="local-name(.)"/>
+ <xsl:text>)</xsl:text>
+ <!-- Make recursive call to write sub-types of this sub-type -->
+ <xsl:if test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <xsl:call-template name="PrintComplexSubtypes">
+ <xsl:with-param name="type" select="$subType"/>
+ <xsl:with-param name="isCallingType">false</xsl:with-param>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $typeName, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:variable>
+ <!-- Print out sub types -->
+ <xsl:choose>
+ <xsl:when test="normalize-space($subTypes)!=''">
+ <xsl:copy-of select="$subTypes"/>
+ </xsl:when>
+ <xsl:when test="$isCallingType='true'">
+ <xsl:text>None</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out the sub types of a given simple type definition.
+ Param(s):
+ type (Node) required
+ Simple type definition
+ isCallingType (boolean) optional
+ If true, 'type' is the type definition that starts this
+ call. Otherwise, this is a recursive call from
+ 'PrintSimpleSubtypes'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSimpleSubtypes">
+ <xsl:param name="type"/>
+ <xsl:param name="isCallingType">true</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:variable name="typeName" select="normalize-space($type/@name)"/>
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="contains($typeList, concat('*', $typeName, '+'))">
+ <!-- Do nothing. Note: Error message will be written out in the Sample Instance table. -->
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Find sub-types that are simple type definitions -->
+ <xsl:variable name="simpleSubTypes">
+ <ul>
+ <xsl:for-each select="/xsd:schema/xsd:simpleType/xsd:restriction[normalize-space(@base)=$typeName or normalize-space(substring-after(@base, ':'))=$typeName]">
+ <li>
+ <xsl:variable name="subType" select=".."/>
+ <!-- Write out type name -->
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="name" select="$subType/@name"/>
+ </xsl:call-template>
+ <!-- Write derivation method -->
+ <xsl:text> (by restriction)</xsl:text>
+ <!-- Make recursive call to write sub-types of this sub-type -->
+ <xsl:if test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <xsl:call-template name="PrintSimpleSubtypes">
+ <xsl:with-param name="type" select="$subType"/>
+ <xsl:with-param name="isCallingType">false</xsl:with-param>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $typeName, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:variable>
+ <!-- Find sub-types that are complex type definitions -->
+ <xsl:variable name="complexSubTypes">
+ <ul>
+ <xsl:for-each select="/xsd:schema/xsd:complexType/xsd:simpleContent/xsd:restriction[normalize-space(@base)=$typeName or normalize-space(substring-after(@base, ':'))=$typeName] | /xsd:schema/xsd:complexType/xsd:simpleContent/xsd:extension[normalize-space(@base)=$typeName or normalize-space(substring-after(@base, ':'))=$typeName]">
+ <li>
+ <xsl:variable name="subType" select="../.."/>
+ <!-- Write out type name -->
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="name" select="$subType/@name"/>
+ </xsl:call-template>
+ <!-- Write derivation method -->
+ <xsl:text> (by </xsl:text>
+ <xsl:value-of select="local-name(.)"/>
+ <xsl:text>)</xsl:text>
+ <!-- Make recursive call to write sub-types of this sub-type -->
+ <xsl:if test="normalize-space(translate($printAllSubTypes, 'TRUE', 'true'))='true'">
+ <xsl:call-template name="PrintComplexSubtypes">
+ <xsl:with-param name="type" select="$subType"/>
+ <xsl:with-param name="isCallingType">false</xsl:with-param>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $typeName, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:variable>
+
+ <xsl:variable name="hasSimpleSubTypes">
+ <xsl:if test="normalize-space($simpleSubTypes)!=''">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="hasComplexSubTypes">
+ <xsl:if test="normalize-space($complexSubTypes)!=''">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <!-- Print out sub types -->
+ <xsl:choose>
+ <xsl:when test="$hasSimpleSubTypes='true' or $hasComplexSubTypes='true'">
+ <xsl:if test="$hasSimpleSubTypes='true'">
+ <xsl:copy-of select="$simpleSubTypes"/>
+ </xsl:if>
+ <xsl:if test="$hasComplexSubTypes='true'">
+ <xsl:copy-of select="$complexSubTypes"/>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="$isCallingType='true'">
+ <xsl:text>None</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <!-- ******** Properties table ******** -->
+
+ <!--
+ Prints out the contents of an 'appinfo' or a 'documentation'
+ element in the Properties table.
+ -->
+ <xsl:template match="xsd:appinfo | xsd:documentation" mode="properties">
+ <!-- Print out children using XML pretty printer templates -->
+ <xsl:choose>
+ <xsl:when test="local-name(.)='appinfo'">
+ <xsl:apply-templates select="* | text()" mode="xpp"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates select="* | text()" mode="html"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Print out URL in the source attribute. -->
+ <xsl:if test="@source">
+ <xsl:if test="* | text()"><br/></xsl:if>
+ <xsl:text> More information at: </xsl:text>
+ <xsl:call-template name="PrintURI">
+ <xsl:with-param name="uri" select="@source"/>
+ </xsl:call-template>
+ <xsl:text>.</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for a top-level
+ attribute declaration.
+ -->
+ <xsl:template match="xsd:attribute" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Type -->
+ <tr>
+ <th>Type</th>
+ <td>
+ <xsl:choose>
+ <xsl:when test="xsd:simpleType">
+ <xsl:text>Locally-defined simple type</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="@type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>anySimpleType</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <!-- Default Value -->
+ <xsl:if test="@default">
+ <tr>
+ <th>Default Value</th>
+ <td><xsl:value-of select="@default"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Fixed Value -->
+ <xsl:if test="@fixed">
+ <tr>
+ <th>Fixed Value</th>
+ <td><xsl:value-of select="@fixed"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out Properties table for a top-level
+ attribute group or model group definition.
+ -->
+ <xsl:template match="xsd:attributeGroup | xsd:group" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for a top-level
+ complex type definition.
+ -->
+ <xsl:template match="xsd:complexType" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Abstract -->
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Abstract</xsl:with-param>
+ <xsl:with-param name="term">Abstract</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td>
+ <xsl:call-template name="PrintBoolean">
+ <xsl:with-param name="boolean" select="@abstract"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ <!-- Final -->
+ <xsl:variable name="final">
+ <xsl:call-template name="PrintDerivationSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="@final">
+ <xsl:value-of select="@final"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@finalDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="normalize-space($final)!=''">
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">TypeFinal</xsl:with-param>
+ <xsl:with-param name="term">Prohibited Derivations</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td><xsl:value-of select="$final"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Block -->
+ <xsl:variable name="block">
+ <xsl:call-template name="PrintDerivationSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="@block">
+ <xsl:value-of select="@block"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@blockDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="normalize-space($block)!=''">
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">TypeBlock</xsl:with-param>
+ <xsl:with-param name="term">Prohibited Substitutions</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td><xsl:value-of select="$block"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for a top-level
+ element declaration.
+ -->
+ <xsl:template match="xsd:element" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Type -->
+ <tr>
+ <th>Type</th>
+ <td>
+ <xsl:choose>
+ <xsl:when test="xsd:simpleType">
+ <xsl:text>Locally-defined simple type</xsl:text>
+ </xsl:when>
+ <xsl:when test="xsd:complexType">
+ <xsl:text>Locally-defined complex type</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="@type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>anyType</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <!-- Nillable -->
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Nillable</xsl:with-param>
+ <xsl:with-param name="term">Nillable</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td>
+ <xsl:call-template name="PrintBoolean">
+ <xsl:with-param name="boolean" select="@nillable"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ <!-- Abstract -->
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Abstract</xsl:with-param>
+ <xsl:with-param name="term">Abstract</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td>
+ <xsl:call-template name="PrintBoolean">
+ <xsl:with-param name="boolean" select="@abstract"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ <!-- Default Value -->
+ <xsl:if test="@default">
+ <tr>
+ <th>Default Value</th>
+ <td><xsl:value-of select="@default"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Fixed Value -->
+ <xsl:if test="@fixed">
+ <tr>
+ <th>Fixed Value</th>
+ <td><xsl:value-of select="@fixed"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Final -->
+ <xsl:variable name="final">
+ <xsl:call-template name="PrintDerivationSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="@final">
+ <xsl:value-of select="@final"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@finalDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="normalize-space($final)!=''">
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">ElemFinal</xsl:with-param>
+ <xsl:with-param name="term">Substitution Group Exclusions</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td><xsl:value-of select="$final"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Block -->
+ <xsl:variable name="block">
+ <xsl:call-template name="PrintBlockSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="@block">
+ <xsl:value-of select="@block"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@blockDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="normalize-space($block)!=''">
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">ElemBlock</xsl:with-param>
+ <xsl:with-param name="term">Disallowed Substitutions</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td><xsl:value-of select="$block"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for a top-level
+ notation declaration.
+ -->
+ <xsl:template match="xsd:notation" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Public Identifier -->
+ <tr>
+ <th>Public Identifier</th>
+ <td><xsl:value-of select="@public"/></td>
+ </tr>
+ <!-- System Identifier -->
+ <xsl:if test="@system">
+ <tr>
+ <th>System Identifier</th>
+ <td><xsl:value-of select="@system"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for the root
+ schema element.
+ -->
+ <xsl:template match="xsd:schema" mode="properties">
+ <table class="properties">
+ <!-- Target Namespace -->
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">TargetNS</xsl:with-param>
+ <xsl:with-param name="term">Target Namespace</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td>
+ <xsl:choose>
+ <xsl:when test="@targetNamespace">
+ <span class="targetNS">
+ <xsl:value-of select="@targetNamespace"/>
+ </span>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>None</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ </tr>
+ <!-- Version -->
+ <xsl:if test="@version">
+ <tr>
+ <th>Version</th>
+ <td><xsl:value-of select="@version"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Language -->
+ <xsl:if test="@xml:lang">
+ <tr>
+ <th>Language</th>
+ <td><xsl:value-of select="@xml:lang"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Element/Attribute Form Defaults -->
+ <tr>
+ <th>Element and Attribute Namespaces</th>
+ <td>
+ <ul>
+ <li>Global element and attribute declarations belong to this schema's target namespace.</li>
+ <li>
+ <xsl:choose>
+ <xsl:when test="normalize-space(@elementFormDefault)='qualified'">
+ <xsl:text>By default, local element declarations belong to this schema's target namespace.</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>By default, local element declarations have no namespace.</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </li>
+ <li>
+ <xsl:choose>
+ <xsl:when test="normalize-space(@attributeFormDefault)='qualified'">
+ <xsl:text>By default, local attribute declarations belong to this schema's target namespace.</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>By default, local attribute declarations have no namespace.</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ <!-- Schema Composition, e.g. include, import, redefine -->
+ <xsl:if test="xsd:include or xsd:import or xsd:redefine">
+ <tr>
+ <th>Schema Composition</th>
+ <td>
+ <ul>
+ <!-- Import -->
+ <xsl:if test="xsd:import">
+ <li>
+ <xsl:text>This schema imports schema(s) from the following namespace(s):</xsl:text>
+ <ul>
+ <xsl:for-each select="xsd:import">
+ <li>
+ <em><xsl:value-of select="@namespace"/></em>
+ <xsl:if test="@schemaLocation">
+ <xsl:text> (at </xsl:text>
+ <xsl:call-template name="PrintSchemaLink">
+ <xsl:with-param name="uri" select="@schemaLocation"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </li>
+ </xsl:if>
+ <!-- Include -->
+ <xsl:if test="xsd:include">
+ <li>
+ <xsl:text>This schema includes components from the following schema document(s):</xsl:text>
+ <ul>
+ <xsl:for-each select="xsd:include">
+ <li>
+ <xsl:call-template name="PrintSchemaLink">
+ <xsl:with-param name="uri" select="@schemaLocation"/>
+ </xsl:call-template>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </li>
+ </xsl:if>
+ <!-- Redefine -->
+ <xsl:if test="xsd:redefine">
+ <li>
+ <xsl:text>This schema includes components from the following schema document(s), where some of the components have been redefined:</xsl:text>
+ <ul>
+ <xsl:for-each select="xsd:redefine">
+ <li>
+ <xsl:call-template name="PrintSchemaLink">
+ <xsl:with-param name="uri" select="@schemaLocation"/>
+ </xsl:call-template>
+ </li>
+ </xsl:for-each>
+ </ul>
+ <xsl:text>See </xsl:text><a href="#Redefinitions">Redefined Schema Components</a><xsl:text> section.</xsl:text>
+ </li>
+ </xsl:if>
+ </ul>
+ </td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Prints out the Properties table for a top-level
+ simple type definition.
+ -->
+ <xsl:template match="xsd:simpleType" mode="properties">
+ <table class="properties">
+ <!-- Name -->
+ <tr>
+ <th>Name</th>
+ <td><xsl:value-of select="@name"/></td>
+ </tr>
+ <!-- Constraints -->
+ <tr>
+ <th>Content</th>
+ <td>
+ <xsl:call-template name="PrintSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="."/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ <!-- Final -->
+ <xsl:variable name="final">
+ <xsl:call-template name="PrintSimpleDerivationSet">
+ <xsl:with-param name="EBV">
+ <xsl:choose>
+ <xsl:when test="@final">
+ <xsl:value-of select="@final"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/xsd:schema/@finalDefault"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="normalize-space($final)!=''">
+ <tr>
+ <th>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">TypeFinal</xsl:with-param>
+ <xsl:with-param name="term">Prohibited Derivations</xsl:with-param>
+ </xsl:call-template>
+ </th>
+ <td><xsl:value-of select="$final"/></td>
+ </tr>
+ </xsl:if>
+ <!-- Annotation -->
+ <xsl:call-template name="PrintAnnotation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </table>
+ </xsl:template>
+
+ <!--
+ Unmatched template for 'properties' mode
+ -->
+ <xsl:template match="*" mode="properties"/>
+
+ <!--
+ Prints out the rows to display 'annotation' elements of an
+ component in the Properties table. This template assumes it
+ will be called within an HTML 'table' element.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="PrintAnnotation">
+ <xsl:param name="component"/>
+
+ <xsl:if test="$component/xsd:annotation/xsd:documentation">
+ <tr>
+ <th>Documentation</th>
+ <td>
+ <xsl:for-each select="$component/xsd:annotation/xsd:documentation">
+ <xsl:if test="position()!=1"><br/><br/></xsl:if>
+ <xsl:apply-templates select="." mode="properties"/>
+ </xsl:for-each>
+ </td>
+ </tr>
+ </xsl:if>
+ <xsl:if test="$component/xsd:annotation/xsd:appinfo">
+ <tr>
+ <th>Application Data</th>
+ <td>
+ <xsl:for-each select="$component/xsd:annotation/xsd:appinfo">
+ <xsl:if test="position()!=1"><br/><br/></xsl:if>
+ <xsl:apply-templates select="." mode="properties"/>
+ </xsl:for-each>
+ </td>
+ </tr>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of simple content
+ to be displayed within a Properties table.
+ Param(s):
+ simpleContent (Node) required
+ Node containing the simple content
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSimpleConstraints">
+ <xsl:param name="simpleContent"/>
+ <xsl:param name="typeList"/>
+
+ <xsl:choose>
+ <!-- Derivation by restriction -->
+ <xsl:when test="$simpleContent/xsd:restriction">
+ <xsl:call-template name="PrintSimpleRestriction">
+ <xsl:with-param name="restriction" select="$simpleContent/xsd:restriction"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Derivation by list -->
+ <xsl:when test="$simpleContent/xsd:list">
+ <ul><li>
+ <xsl:text>List of: </xsl:text>
+ <xsl:choose>
+ <!-- Globally-defined item type -->
+ <xsl:when test="$simpleContent/xsd:list/@itemType">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$simpleContent/xsd:list/@itemType"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Locally-defined item type -->
+ <xsl:otherwise>
+ <ul>
+ <li>
+ <xsl:text>Locally defined type:</xsl:text>
+ <xsl:call-template name="PrintSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="$simpleContent/xsd:list/xsd:simpleType"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </li>
+ </ul>
+ </xsl:otherwise>
+ </xsl:choose>
+ </li></ul>
+ </xsl:when>
+ <!-- Derivation by union -->
+ <xsl:when test="$simpleContent/xsd:union">
+ <ul><li>
+ <xsl:text>Union of following types: </xsl:text>
+ <ul>
+ <!-- Globally-defined member types -->
+ <xsl:if test="$simpleContent/xsd:union/@memberTypes">
+ <xsl:call-template name="PrintWhitespaceList">
+ <xsl:with-param name="value" select="$simpleContent/xsd:union/@memberTypes"/>
+ <xsl:with-param name="compType">type</xsl:with-param>
+ <xsl:with-param name="isInList">true</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Locally-defined member types -->
+ <xsl:for-each select="$simpleContent/xsd:union/xsd:simpleType">
+ <li>
+ <xsl:text>Locally defined type:</xsl:text>
+ <xsl:call-template name="PrintSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="."/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </li></ul>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of simple content derived by
+ restriction, which is to be displayed in a Properties table.
+ Param(s):
+ restriction (Node) required
+ Node containing the restriction
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSimpleRestriction">
+ <xsl:param name="restriction"/>
+ <xsl:param name="typeList"/>
+
+ <xsl:variable name="typeName" select="$restriction/parent::xsd:simpleType/@name"/>
+
+ <!-- Print out base type info -->
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="$typeName != '' and contains($typeList, concat('*', $typeName, '+'))">
+ <li>
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">false</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+ <xsl:text>Circular type reference to '</xsl:text>
+ <xsl:value-of select="$typeName"/>
+ <xsl:text>' in type hierarchy.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </li>
+ </xsl:when>
+ <!-- Locally-defined base type -->
+ <xsl:when test="$restriction/xsd:simpleType">
+ <xsl:call-template name="PrintSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="$restriction/xsd:simpleType"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Base type reference -->
+ <xsl:when test="$restriction">
+ <xsl:variable name="baseTypeRef" select="$restriction/@base"/>
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="baseTypeNS">
+ <xsl:call-template name="GetRefNS">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Base type is built-in XSD type -->
+ <xsl:when test="$baseTypeNS=$XSD_NS">
+ <ul><li>
+ <xsl:text>Base XSD Type: </xsl:text>
+ <xsl:choose>
+ <!-- Current schema is the schema for XSDL -->
+ <xsl:when test="normalize-space(/xsd:schema/@targetNamespace)=$XSD_NS">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Current schema is not XSD namespace -->
+ <xsl:otherwise>
+ <xsl:value-of select="$baseTypeName"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </li></ul>
+ </xsl:when>
+ <!-- Other types -->
+ <xsl:otherwise>
+ <xsl:variable name="baseType" select="key('simpleType', $baseTypeName)"/>
+ <xsl:choose>
+ <!-- Base type found -->
+ <xsl:when test="$baseType">
+ <xsl:call-template name="PrintSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="$baseType"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $typeName, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Base type not found -->
+ <xsl:otherwise>
+ <ul><li><strong>
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="$baseTypeName"/>
+ <xsl:text>' super type was not found in this schema. </xsl:text>
+ <xsl:text>Its facets could not be printed out.</xsl:text>
+ </strong></li></ul>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Find constraints in current restriction -->
+ <xsl:variable name="enumeration">
+ <xsl:call-template name="PrintEnumFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="pattern">
+ <xsl:call-template name="PrintPatternFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="range">
+ <xsl:call-template name="PrintRangeFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="totalDigits">
+ <xsl:call-template name="PrintTotalDigitsFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="fractionDigits">
+ <xsl:call-template name="PrintFractionDigitsFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="length">
+ <xsl:call-template name="PrintLengthFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="whitespace">
+ <xsl:call-template name="PrintWhitespaceFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Print out facets -->
+ <xsl:if test="$enumeration!='' or $pattern!='' or $range!='' or $totalDigits!='' or $fractionDigits!='' or $length!='' or $whitespace!=''">
+ <ul>
+ <xsl:if test="$enumeration!=''">
+ <li>
+ <xsl:copy-of select="$enumeration"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$pattern!=''">
+ <li>
+ <xsl:copy-of select="$pattern"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$range!=''">
+ <li>
+ <xsl:copy-of select="$range"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$totalDigits!=''">
+ <li>
+ <xsl:copy-of select="$totalDigits"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$fractionDigits!=''">
+ <li>
+ <xsl:copy-of select="$fractionDigits"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$length!=''">
+ <li>
+ <xsl:copy-of select="$length"/>
+ </li>
+ </xsl:if>
+ <xsl:if test="$whitespace!=''">
+ <li>
+ <xsl:copy-of select="$whitespace"/>
+ </li>
+ </xsl:if>
+ </ul>
+ </xsl:if>
+ </xsl:template>
+
+
+ <!-- ******** XML Instance Representation table ******** -->
+
+ <!--
+ Prints out the XML Instance Representation table for a top-level
+ schema component.
+ Param(s):
+ component (Node) required
+ Top-level schema component
+ -->
+ <xsl:template name="SampleInstanceTable">
+ <xsl:param name="component"/>
+
+ <!-- Not applicable for simple type definitions and notation
+ declarations -->
+ <xsl:if test="local-name($component)!='simpleType' and local-name($component)!='notation'">
+ <xsl:variable name="componentID">
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:call-template name="CollapseableBox">
+ <xsl:with-param name="id" select="concat($componentID, '_xibox')"/>
+ <xsl:with-param name="styleClass">sample</xsl:with-param>
+ <xsl:with-param name="caption">XML Instance Representation</xsl:with-param>
+ <xsl:with-param name="contents">
+ <xsl:apply-templates select="$component" mode="sample"/>
+ </xsl:with-param>
+ <xsl:with-param name="isOpened">true</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation of an 'all'
+ model group.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this all model group;
+ if in current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:all" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:if test="normalize-space(@maxOccurs)!='0'">
+ <!-- Header -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>Start </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">All</xsl:with-param>
+ <xsl:with-param name="term">All</xsl:with-param>
+ </xsl:call-template>
+ <!-- Min/max occurs information-->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </span><br/>
+
+ <!-- Content -->
+ <xsl:apply-templates select="xsd:*" mode="sample">
+ <xsl:with-param name="margin" select="number($margin) + number($ELEM_INDENT)"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+
+ <!-- Footer -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>End All</xsl:text>
+ </span><br/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation of an element
+ content wild card.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:any | xsd:anyAttribute" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:call-template name="PrintWildcard">
+ <xsl:with-param name="componentType">
+ <xsl:choose>
+ <xsl:when test="local-name(.)='anyAttribute'">attribute</xsl:when>
+ <xsl:otherwise>element</xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="namespace" select="@namespace"/>
+ <xsl:with-param name="processContents" select="@processContents"/>
+ <xsl:with-param name="minOccurs" select="@minOccurs"/>
+ <xsl:with-param name="maxOccurs" select="@maxOccurs"/>
+ </xsl:call-template>
+ </div>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation
+ of an attribute declaration.
+ Param(s):
+ subTypeAttrs (String) optional
+ List of attributes in sub-types of the type that
+ contains this attribute
+ isInherited (boolean) optional
+ If true, display attribute using 'inherited' CSS
+ class.
+ isNewField (boolean) optional
+ If true, display attribute using 'newFields' CSS
+ class.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ addBR (boolean) optional
+ If true, add <br/> before attribute.
+ schemaLoc (String) optional
+ Schema file containing this attribute declaration;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template match="xsd:attribute[@name]" mode="sample">
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="addBR">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <!-- Get attribute namespace -->
+ <xsl:variable name="attrNS">
+ <xsl:call-template name="GetAttributeNS">
+ <xsl:with-param name="attribute" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($subTypeAttrs, concat('*', normalize-space($attrNS), '+', normalize-space(@name), '+'))">
+ <!-- IGNORE: Sub type has attribute with same name;
+ Sub-type's attribute declaration will override this
+ one. -->
+ </xsl:when>
+ <xsl:when test="@use and normalize-space(@use)='prohibited'">
+ <!-- IGNORE: Attribute is prohibited. -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="$addBR!='false'"><br/></xsl:if>
+
+ <span style="margin-left: {$margin}em">
+ <xsl:choose>
+ <xsl:when test="$isNewField!='false'">
+ <xsl:attribute name="class">newFields</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$isInherited!='false'">
+ <xsl:attribute name="class">inherited</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:text> </xsl:text>
+ <xsl:variable name="prefix">
+ <xsl:call-template name="GetAttributePrefix">
+ <xsl:with-param name="attribute" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix" select="$prefix"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:value-of select="@name"/>
+ <xsl:text>="</xsl:text>
+
+ <xsl:choose>
+ <!-- Fixed value is provided -->
+ <xsl:when test="@fixed">
+ <span class="fixed">
+ <xsl:value-of select="@fixed"/>
+ </span>
+ </xsl:when>
+ <!-- Type reference is provided -->
+ <xsl:when test="@type">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="@type"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Local type definition is provided -->
+ <xsl:when test="xsd:simpleType">
+ <xsl:apply-templates select="xsd:simpleType" mode="sample">
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:when>
+ <xsl:otherwise>
+ <span class="type">anySimpleType</span>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Don't print occurrence info and documentation
+ for global attributes. -->
+ <xsl:if test="local-name(..)!='schema'">
+ <!-- Occurrence info-->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:text>"</xsl:text>
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation
+ of an attribute reference.
+ Param(s):
+ subTypeAttrs (String) optional
+ List of attribute in sub-types of the type that
+ contains this attribute
+ isInherited (boolean) optional
+ If true, display attributes using 'inherited' CSS
+ class.
+ isNewField (boolean) optional
+ If true, display attributes using 'newFields' CSS
+ class.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ addBR (boolean) optional
+ If true, add <br/> before attribute.
+ schemaLoc (String) optional
+ Schema file containing this attribute reference;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template match="xsd:attribute[@ref]" mode="sample">
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="addBR">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <!-- Get attribute name -->
+ <xsl:variable name="attrName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get attribute namespace -->
+ <xsl:variable name="attrNS">
+ <xsl:call-template name="GetAttributeNS">
+ <xsl:with-param name="attribute" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($subTypeAttrs, concat('*', normalize-space($attrNS), '+', normalize-space($attrName), '+'))">
+ <!-- IGNORE: Sub type has attribute with same name;
+ Sub-type's attribute declaration will override this
+ one. -->
+ </xsl:when>
+ <xsl:when test="@use and normalize-space(@use)='prohibited'">
+ <!-- IGNORE: Attribute is prohibited. -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="$addBR!='false'"><br/></xsl:if>
+
+ <span style="margin-left: {$margin}em">
+ <xsl:choose>
+ <xsl:when test="$isNewField!='false'">
+ <xsl:attribute name="class">newFields</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$isInherited!='false'">
+ <xsl:attribute name="class">inherited</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintAttributeRef">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text>="</xsl:text>
+ <!-- Fixed value is provided -->
+ <xsl:if test="@fixed">
+ <span class="fixed">
+ <xsl:value-of select="@fixed"/>
+ </span>
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ <!-- Print occurs info-->
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+
+ <xsl:text>"</xsl:text>
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation of an attribute
+ group definition.
+ Param(s):
+ schemaLoc (String) optional
+ Schema file containing this attribute group
+ definition; if in current schema, 'schemaLoc' is
+ set to 'this'.
+ -->
+ <xsl:template match="xsd:attributeGroup[@name]" mode="sample">
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:for-each select="xsd:attribute | xsd:attributeGroup | xsd:anyAttribute">
+ <xsl:variable name="addBR">
+ <xsl:choose>
+ <xsl:when test="position()!=1">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:apply-templates select="." mode="sample">
+ <xsl:with-param name="addBR" select="$addBR"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation of an attribute
+ group reference.
+ Param(s):
+ subTypeAttrs (String) optional
+ List of attributes in sub-types of the type that
+ contains this attribute group
+ isInherited (boolean) optional
+ If true, display attributes using 'inherited' CSS
+ class.
+ isNewField (boolean) optional
+ If true, display attributes using 'newFields' CSS
+ class.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ parentGroups (String) optional
+ List of parent attribute group definitions that
+ contain this attribute group. Used to prevent
+ infinite loops when displaying attribute group
+ definitions. In such a case, writes out an error
+ message and stops processing.
+ schemaLoc (String) optional
+ Schema file containing this attribute group
+ reference if in current schema, 'schemaLoc' is
+ set to 'this'.
+ -->
+ <xsl:template match="xsd:attributeGroup[@ref]" mode="sample">
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="parentGroups"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <!-- Get attribute group name -->
+ <xsl:variable name="attrGrpName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($parentGroups, concat('*', normalize-space($attrGrpName), '+'))">
+ <!-- Circular attribute group definition -->
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">false</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+ <xsl:text>Circular attribute group reference: </xsl:text>
+ <xsl:value-of select="$attrGrpName"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Look for attribute group definition -->
+ <xsl:variable name="defLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="compType">attribute group</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Not found -->
+ <xsl:when test="normalize-space($defLoc)='' or normalize-space($defLoc)='none' or normalize-space($defLoc)='xml' or normalize-space($defLoc)='xsd'">
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:text>Attribute group reference (not shown): </xsl:text>
+ <xsl:call-template name="PrintAttributeGroupRef">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </div>
+ </xsl:when>
+ <!-- Found in current schema -->
+ <xsl:when test="normalize-space($defLoc)='this'">
+ <xsl:variable name="attrGrpDef" select="key('attributeGroup', $attrGrpName)"/>
+ <xsl:apply-templates select="$attrGrpDef/xsd:attribute | $attrGrpDef/xsd:attributeGroup" mode="sample">
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="concat($parentGroups, concat('*', normalize-space($attrGrpName), '+'))"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="addBR">true</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:when>
+ <!-- Found in external schema -->
+ <xsl:otherwise>
+ <xsl:variable name="attrGrpDef" select="document($defLoc)/xsd:schema/xsd:attributeGroup[@name=$attrGrpName]"/>
+ <xsl:apply-templates select="$attrGrpDef/xsd:attribute | $attrGrpDef/xsd:attributeGroup" mode="sample">
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="concat($parentGroups, concat('*', normalize-space($attrGrpName), '+'))"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="addBR">true</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$defLoc"/>
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance representation of a 'choice'
+ model group.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ parentGroups (String) optional
+ List of parent model group definitions that contain this
+ model group. Used to prevent infinite loops when
+ displaying model group definitions.
+ schemaLoc (String) optional
+ Schema file containing this choice model group;
+ if in current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:choice" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="parentGroups"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:if test="normalize-space(@maxOccurs)!='0'">
+ <!-- Header -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>Start </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Choice</xsl:with-param>
+ <xsl:with-param name="term">Choice</xsl:with-param>
+ </xsl:call-template>
+ <!-- Min/max occurrence -->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </span><br/>
+
+ <!-- Content -->
+ <xsl:apply-templates select="xsd:*" mode="sample">
+ <xsl:with-param name="margin" select="number($margin)+number($ELEM_INDENT)"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="$parentGroups"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+
+ <!-- Footer -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>End Choice</xsl:text>
+ </span><br/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from a complex type definition.
+ Param(s):
+ schemaLoc (String) optional
+ Schema file containing this complex type definition;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template match="xsd:complexType" mode="sample">
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:call-template name="PrintSampleComplexElement">
+ <xsl:with-param name="type" select="."/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from an element declaration.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this element declaration;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:element[@name]" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:choose>
+ <!-- Prohibited element declaration -->
+ <xsl:when test="normalize-space(@maxOccurs)='0'">
+ <!-- IGNORE if max occurs is zero -->
+ </xsl:when>
+ <!-- Global element declaration -->
+ <xsl:when test="local-name(..)='schema'">
+ <xsl:choose>
+ <!-- With type reference -->
+ <xsl:when test="@type">
+ <xsl:variable name="elemTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@type"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Look for complex type definition -->
+ <xsl:variable name="defLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="@type"/>
+ <xsl:with-param name="compType">complex type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Complex type was found in current
+ schema. -->
+ <xsl:when test="normalize-space($defLoc)='this'">
+ <xsl:variable name="ctype" select="key('complexType', $elemTypeName)"/>
+ <xsl:call-template name="PrintSampleComplexElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="type" select="$ctype"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Complex type was not found. -->
+ <xsl:when test="normalize-space($defLoc)='' or normalize-space($defLoc)='none' or normalize-space($defLoc)='xml' or normalize-space($defLoc)='xsd'">
+ <xsl:call-template name="PrintSampleSimpleElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Complex type was found in external
+ schema. -->
+ <xsl:otherwise>
+ <xsl:variable name="ctype" select="document($defLoc)/xsd:schema/xsd:complexType[@name=$elemTypeName]"/>
+ <xsl:call-template name="PrintSampleComplexElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="type" select="$ctype"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- With local complex type definition -->
+ <xsl:when test="xsd:complexType">
+ <xsl:call-template name="PrintSampleComplexElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="type" select="xsd:complexType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="PrintSampleSimpleElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- Local element declaration -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- With local complex type definition -->
+ <xsl:when test="xsd:complexType">
+ <xsl:call-template name="PrintSampleComplexElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="type" select="xsd:complexType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="PrintSampleSimpleElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from an element
+ reference.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this element reference;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template match="xsd:element[@ref]" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:if test="normalize-space(@maxOccurs)!='0'">
+ <xsl:call-template name="PrintSampleSimpleElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from a group definition.
+ Param(s):
+ schemaLoc (String) optional
+ Schema file containing this model group definition;
+ if in current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:group[@name]" mode="sample">
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:apply-templates select="xsd:*" mode="sample">
+ <xsl:with-param name="parentGroups" select="concat('*', normalize-space(@name), '+')"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from a group reference.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS
+ class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS
+ class.
+ parentGroups (String) optional
+ List of parent model group definitions that contain
+ this model group. Used to prevent infinite loops
+ when displaying model group definitions.
+ schemaLoc (String) optional
+ Schema file containing this model group reference;
+ if in current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:group[@ref]" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="parentGroups"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <!-- Get group name -->
+ <xsl:variable name="grpName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Create link to the group definition -->
+ <xsl:variable name="grpLink">
+ <xsl:call-template name="PrintGroupRef">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get occurrence info -->
+ <xsl:variable name="occursInfo">
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Circular group definition -->
+ <xsl:when test="contains($parentGroups, concat('*', normalize-space($grpName), '+'))">
+ <!-- Don't show contents -->
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:text>Circular model group reference: </xsl:text>
+ <xsl:copy-of select="$grpLink"/>
+ <!-- Occurrence info -->
+ <xsl:text> </xsl:text>
+ <xsl:copy-of select="$occursInfo"/>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </div>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Look for group definition -->
+ <xsl:variable name="grpDefLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="compType">group</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Not found -->
+ <xsl:when test="normalize-space($grpDefLoc)='' or normalize-space($grpDefLoc)='none' or normalize-space($grpDefLoc)='xml' or normalize-space($grpDefLoc)='xsd'">
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:text>Model group reference (not shown): </xsl:text>
+ <xsl:copy-of select="$grpLink"/>
+ <!-- Occurrence info -->
+ <xsl:text> </xsl:text>
+ <xsl:copy-of select="$occursInfo"/>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </div>
+ </xsl:when>
+ <!-- Found in current schema -->
+ <xsl:when test="normalize-space($grpDefLoc)='this'">
+ <xsl:variable name="grpDef" select="key('group', $grpName)"/>
+ <xsl:call-template name="PrintSampleGroup">
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="concat($parentGroups, concat('*', normalize-space($grpName), '+'))"/>
+ <xsl:with-param name="occursInfo" select="$occursInfo"/>
+ <xsl:with-param name="grpLink" select="$grpLink"/>
+ <xsl:with-param name="grpRef" select="."/>
+ <xsl:with-param name="grpDef" select="$grpDef"/>
+ <xsl:with-param name="grpDefLoc" select="$grpDefLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Found in external schema -->
+ <xsl:otherwise>
+ <xsl:variable name="grpDef" select="document($grpDefLoc)/xsd:schema/xsd:group[@name=$grpName]"/>
+ <xsl:call-template name="PrintSampleGroup">
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="concat($parentGroups, concat('*', normalize-space($grpName), '+'))"/>
+ <xsl:with-param name="occursInfo" select="$occursInfo"/>
+ <xsl:with-param name="grpLink" select="$grpLink"/>
+ <xsl:with-param name="grpRef" select="."/>
+ <xsl:with-param name="grpDef" select="$grpDef"/>
+ <xsl:with-param name="grpDefLoc" select="$grpDefLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a sample XML instance from a 'sequence' model group.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ parentGroups (String) optional
+ List of parent model group definitions that contain
+ this model group. Used to prevent infinite loops when
+ displaying model group definitions.
+ schemaLoc (String) optional
+ Schema file containing this sequence model group;
+ if in current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template match="xsd:sequence" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="parentGroups"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:if test="normalize-space(@maxOccurs)!='0'">
+ <!-- Get occurrence info -->
+ <xsl:variable name="occursInfo">
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Header -->
+ <xsl:if test="normalize-space($occursInfo)!='[1]'">
+ <!-- Don't display header if min/max occurs is one. -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>Start </xsl:text>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Sequence</xsl:with-param>
+ <xsl:with-param name="term">Sequence</xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:text> </xsl:text>
+ <xsl:copy-of select="$occursInfo"/>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </span><br/>
+ </xsl:if>
+
+ <xsl:apply-templates select="xsd:*" mode="sample">
+ <xsl:with-param name="margin">
+ <xsl:choose>
+ <xsl:when test="normalize-space($occursInfo)='[1]'">
+ <xsl:value-of select="$margin"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="number($margin)+number($ELEM_INDENT)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="$parentGroups"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+
+ <!-- Footer -->
+ <xsl:if test="normalize-space($occursInfo)!='[1]'">
+ <!-- Don't display footer if min/max occurs is one. -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>End Sequence</xsl:text>
+ </span><br/>
+ </xsl:if>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of a complex type with simple content
+ to be displayed within a sample XML instance.
+ Param(s):
+ schemaLoc (String) optional
+ Schema file containing this simple content
+ restriction; if in current schema, 'schemaLoc' is
+ set to 'this'
+ -->
+ <xsl:template match="xsd:simpleContent[xsd:restriction]" mode="sample">
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <span class="constraint">
+ <xsl:call-template name="PrintSampleSimpleRestriction">
+ <xsl:with-param name="restriction" select="./xsd:restriction"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </span>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of a simple type definition to be
+ displayed within a sample XML instance.
+ Param(s):
+ schemaLoc (String) optional
+ Schema file containing this simple type definition;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template match="xsd:simpleType" mode="sample">
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <span class="constraint">
+ <xsl:call-template name="PrintSampleSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="."/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </span>
+ </xsl:template>
+
+ <!--
+ Prints out the identity constraints of an element to be displayed
+ within a sample XML instance.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ schemaLoc (String) optional
+ Schema file containing this simple type definition;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template match="xsd:unique | xsd:key | xsd:keyref" mode="sample">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:text>&lt;!-- </xsl:text><br/>
+
+ <xsl:choose>
+ <xsl:when test="local-name(.)='unique'">
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Unique</xsl:with-param>
+ <xsl:with-param name="term">Uniqueness</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="local-name(.)='key'">
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">Key</xsl:with-param>
+ <xsl:with-param name="term">Key</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">KeyRef</xsl:with-param>
+ <xsl:with-param name="term">Key Reference</xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> Constraint - </xsl:text>
+ <strong>
+ <xsl:choose>
+ <xsl:when test="local-name(.)='keyref'">
+ <xsl:value-of select="@name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="componentID">
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <a name="{$componentID}"><xsl:value-of select="@name"/></a>
+ </xsl:otherwise>
+ </xsl:choose>
+ </strong><br/>
+
+ <xsl:text>Selector - </xsl:text>
+ <strong>
+ <xsl:value-of select="xsd:selector/@xpath"/>
+ </strong><br/>
+
+ <xsl:text>Field(s) - </xsl:text>
+ <xsl:for-each select="xsd:field">
+ <xsl:if test="position()!=1">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ <strong>
+ <xsl:value-of select="@xpath"/>
+ </strong>
+ </xsl:for-each>
+ <br/>
+
+ <xsl:if test="local-name(.)='keyref'">
+ <xsl:text>Refers to - </xsl:text>
+ <xsl:call-template name="PrintKeyRef">
+ <xsl:with-param name="ref" select="@refer"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <br/>
+ </xsl:if>
+
+ <xsl:text>--></xsl:text>
+ </div>
+ </xsl:template>
+
+ <!--
+ Unmatched template for 'sample' mode
+ -->
+ <xsl:template match="*" mode="sample"/>
+
+ <!--
+ Prints out a link which will open up a window, displaying a
+ schema component's documentation.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="PrintSampleDocumentation">
+ <xsl:param name="component"/>
+
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true' and $component and $component/xsd:annotation/xsd:documentation">
+ <xsl:variable name="documentation">
+ <xsl:for-each select="$component/xsd:annotation/xsd:documentation">
+ <!-- Check for two dashes, which will break the JavaScript
+ code -->
+ <xsl:if test="contains(., '--') or contains(@source, '--')">
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">true</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+A local schema component contains two dashes in
+'documentation' elements within its 'annotation' element.
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="position()!=1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+ <xsl:text>'</xsl:text>
+ <xsl:choose>
+ <xsl:when test="@source">
+ <xsl:text>More information at: </xsl:text>
+ <xsl:call-template name="EscapeQuotes">
+ <xsl:with-param name="value" select="@source"/>
+ </xsl:call-template>
+ <xsl:text>.</xsl:text>
+ </xsl:when>
+ <xsl:when test="normalize-space(.)!=''">
+ <xsl:call-template name="EscapeQuotes">
+ <xsl:with-param name="value" select="normalize-space(.)"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:text>'</xsl:text>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:text> </xsl:text>
+ <a href="javascript:void(0)" title="View Documentation" class="documentation">
+ <!-- onclick attribute -->
+ <xsl:attribute name="onclick">
+ <xsl:text>docArray = new Array(</xsl:text>
+ <xsl:value-of select="$documentation"/>
+ <xsl:text>); viewDocumentation('</xsl:text>
+ <xsl:call-template name="GetComponentDescription">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ <xsl:text>', '</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$component/@name">
+ <xsl:value-of select="$component/@name"/>
+ </xsl:when>
+ <xsl:when test="$component/@ref">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$component/@ref"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:text>', docArray);</xsl:text>
+ </xsl:attribute>
+ <xsl:text>?</xsl:text>
+ </a>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Translates occurrences of single and double quotes
+ in a piece of text with single and double quote
+ escape characters.
+ Param(s):
+ value (String) required
+ Text to translate
+ -->
+ <xsl:template name="EscapeQuotes">
+ <xsl:param name="value"/>
+
+ <xsl:variable name="noSingleQuotes">
+ <xsl:call-template name="TranslateStr">
+ <xsl:with-param name="value" select="$value"/>
+ <xsl:with-param name="strToReplace">'</xsl:with-param>
+ <xsl:with-param name="replacementStr">\'</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="noDoubleQuotes">
+ <xsl:call-template name="TranslateStr">
+ <xsl:with-param name="value" select="$noSingleQuotes"/>
+ <xsl:with-param name="strToReplace">"</xsl:with-param>
+ <xsl:with-param name="replacementStr">\"</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$noDoubleQuotes"/>
+ </xsl:template>
+
+ <!--
+ Helper template for template, match="xsd:group[@ref]"
+ mode="sample". Basically prints out a group reference, for
+ which we are able to look up the group definition that it
+ is referring to. This template is a work-around because XSLT
+ doesn't have variables (in the traditional sense of
+ programming languages) and it doesn't allow you to query
+ result tree fragments.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ parentGroups (String) optional
+ List of parent model group definitions that contain this
+ model group. Used to prevent infinite loops when
+ displaying model group definitions.
+ occursInfo (Result tree fragment) required
+ Pre-formatted occurrence info of group reference
+ grpLink (Result tree fragment) required
+ Pre-formatted <a> link representing group reference
+ grpRef (Node) required
+ Group reference
+ grpDef (Node) required
+ Group definition that the reference is pointing to
+ grpDefLoc (String) optional
+ Schema file containing 'grpDef' group definition;
+ if current schema, 'schemaLoc' is set to 'this'
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleGroup">
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="parentGroups"/>
+ <xsl:param name="occursInfo"/>
+ <xsl:param name="grpLink"/>
+ <xsl:param name="grpRef"/>
+ <xsl:param name="grpDef"/>
+ <xsl:param name="grpDefLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <!-- Header -->
+ <xsl:if test="normalize-space($occursInfo)!='[1]'">
+ <!-- Don't print out header if min/max occurs is one. -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>Start Group: </xsl:text>
+ <xsl:copy-of select="$grpLink"/>
+ <!-- Occurrence info -->
+ <xsl:text> </xsl:text>
+ <xsl:copy-of select="$occursInfo"/>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="$grpRef"/>
+ </xsl:call-template>
+ </span><br/>
+ </xsl:if>
+
+ <!-- Content -->
+ <xsl:apply-templates select="$grpDef/xsd:*" mode="sample">
+ <xsl:with-param name="margin">
+ <xsl:choose>
+ <xsl:when test="normalize-space($occursInfo)!='[1]'">
+ <xsl:value-of select="number($margin)+number($ELEM_INDENT)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$margin"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="parentGroups" select="$parentGroups"/>
+ <xsl:with-param name="schemaLoc" select="$grpDefLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+
+ <!-- Footer -->
+ <xsl:if test="normalize-space($occursInfo)!='[1]'">
+ <!-- Don't print out footer if min/max occurs is one. -->
+ <span class="group" style="margin-left: {$margin}em">
+ <xsl:text>End Group: </xsl:text>
+ <xsl:copy-of select="$grpLink"/>
+ </span><br/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a sample element instance in one line.
+ Param(s):
+ element (Node) required
+ Element declaration or reference
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display element using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display element using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this element declaration
+ or reference; if in current schema, 'schemaLoc' is
+ set to 'this'.
+ -->
+ <xsl:template name="PrintSampleSimpleElement">
+ <xsl:param name="element"/>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <!-- Element Tag -->
+ <xsl:variable name="elemTag">
+ <!-- Local Name -->
+ <xsl:choose>
+ <!-- Element reference -->
+ <xsl:when test="$element/@ref">
+ <!-- Note: Prefix will be automatically written out
+ in call to 'PrintElementRef'. -->
+ <xsl:call-template name="PrintElementRef">
+ <xsl:with-param name="ref" select="@ref"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Element declaration -->
+ <xsl:otherwise>
+ <!-- Prefix -->
+ <xsl:variable name="prefix">
+ <xsl:call-template name="GetElementPrefix">
+ <xsl:with-param name="element" select="$element"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix" select="$prefix"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:value-of select="$element/@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <div style="margin-left: {$margin}em">
+ <xsl:choose>
+ <xsl:when test="$isNewField!='false'">
+ <xsl:attribute name="class">newFields</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$isInherited!='false'">
+ <xsl:attribute name="class">inherited</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Start Tag -->
+ <xsl:text>&lt;</xsl:text>
+ <xsl:copy-of select="$elemTag"/>
+ <xsl:text>></xsl:text>
+
+ <!-- Contents -->
+ <xsl:text> </xsl:text>
+ <xsl:choose>
+ <!-- Fixed value is provided -->
+ <xsl:when test="$element/@fixed">
+ <span class="fixed">
+ <xsl:value-of select="$element/@fixed"/>
+ </span>
+ </xsl:when>
+ <!-- Type reference is provided -->
+ <xsl:when test="$element/@name and $element/@type">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$element/@type"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Local simple type definition is provided -->
+ <xsl:when test="$element/@name and $element/xsd:simpleType">
+ <xsl:apply-templates select="$element/xsd:simpleType" mode="sample">
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>...</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> </xsl:text>
+
+ <!-- Identity Constraints -->
+ <xsl:if test="$element/xsd:unique or $element/xsd:key or $element/xsd:keyref">
+ <xsl:apply-templates select="$element/xsd:unique | $element/xsd:key | $element/xsd:keyref" mode="sample">
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:if>
+
+ <!-- End Tag -->
+ <xsl:text>&lt;/</xsl:text>
+ <xsl:copy-of select="$elemTag"/>
+ <xsl:text>></xsl:text>
+
+ <xsl:if test="local-name($element/..)!='schema'">
+ <!-- Min/max occurs information -->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+ </xsl:if>
+ </div>
+ </xsl:template>
+
+ <!--
+ Prints out a sample element instance that has complex content.
+ Param(s):
+ type (Node) required
+ Complex type definition
+ element (Node) optional
+ Element declaration
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display element using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display element using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this element declaration
+ or type definition; if in current schema, 'schemaLoc'
+ is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleComplexElement">
+ <xsl:param name="type"/>
+ <xsl:param name="element"/>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="$type/@name and contains($typeList, concat('*', $type/@name, '+'))"/>
+ <xsl:otherwise>
+ <xsl:variable name="tag">
+ <xsl:choose>
+ <xsl:when test="$element">
+ <!-- Prefix -->
+ <xsl:variable name="prefix">
+ <xsl:call-template name="GetElementPrefix">
+ <xsl:with-param name="element" select="$element"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix" select="$prefix"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:value-of select="$element/@name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>...</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="fromTopCType">
+ <xsl:choose>
+ <xsl:when test="not($element) and local-name($type/..)='schema'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <div style="margin-left: {$margin}em">
+ <xsl:choose>
+ <xsl:when test="$isNewField!='false'">
+ <xsl:attribute name="class">newFields</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$isInherited!='false'">
+ <xsl:attribute name="class">inherited</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Start Tag -->
+ <xsl:text>&lt;</xsl:text>
+ <xsl:copy-of select="$tag"/>
+
+ <!-- Print attributes -->
+ <xsl:call-template name="PrintSampleTypeAttrs">
+ <xsl:with-param name="type" select="$type"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="margin" select="$ATTR_INDENT"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+
+ <!-- Get content -->
+ <xsl:variable name="content">
+ <xsl:call-template name="PrintSampleTypeContent">
+ <xsl:with-param name="type" select="$type"/>
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Find out if content type is mixed -->
+ <xsl:variable name="mixed">
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($type/xsd:complexContent/@mixed, 'TRUE', 'true'))='true' or normalize-space($type/xsd:complexContent/@mixed)='1'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:when test="normalize-space(translate($type/@mixed, 'TRUE', 'true'))='true' or normalize-space($type/@mixed)='1'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Find out if there are identity constraints -->
+ <xsl:variable name="hasIdConstraints">
+ <xsl:if test="$element and ($element/xsd:unique or $element/xsd:key or $element/xsd:keyref)">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Print content -->
+ <xsl:choose>
+ <!-- Empty content -->
+ <xsl:when test="$hasIdConstraints!='true' and normalize-space($content)=''">
+ <!-- Close start tag -->
+ <xsl:text>/> </xsl:text>
+
+ <xsl:if test="$element and local-name($element/..)!='schema'">
+ <!-- Occurrence info -->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+
+ <!-- Documentation -->
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Close start tag -->
+ <xsl:text>> </xsl:text>
+
+ <xsl:if test="$element and local-name($element/..)!='schema'">
+ <!-- Occurrence info -->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+
+ <!-- Documentation -->
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintSampleDocumentation">
+ <xsl:with-param name="component" select="$element"/>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- Identity Constraints -->
+ <xsl:if test="$element">
+ <xsl:apply-templates select="$element/xsd:unique | $element/xsd:key | $element/xsd:keyref" mode="sample">
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ </xsl:apply-templates>
+ </xsl:if>
+
+ <!-- Print out restriction/extension information -->
+ <xsl:choose>
+ <xsl:when test="false()">
+ <!--<xsl:when test="$type/xsd:complexContent/xsd:restriction/@base">-->
+ <br/><span class="other" style="margin-left: {$ELEM_INDENT}em">
+ <xsl:text>&lt;!-- Restricts : </xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$type/xsd:complexContent/xsd:restriction/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> --></xsl:text>
+ </span>
+ </xsl:when>
+ <xsl:when test="false()">
+ <!--<xsl:when test="$type/xsd:complexContent/xsd:extension/@base">-->
+ <br/><span class="other" style="margin-left: {$ELEM_INDENT}em">
+ <xsl:text>&lt;!-- Extends : </xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$type/xsd:complexContent/xsd:extension/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> --></xsl:text>
+ </span>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Print out message if has mixed content -->
+ <xsl:if test="$mixed='true'">
+ <br/><span class="other" style="margin-left: {$ELEM_INDENT}em">
+ <xsl:text>&lt;!-- Mixed content --></xsl:text>
+ </span>
+ </xsl:if>
+
+ <!-- Element Content -->
+ <xsl:copy-of select="$content"/>
+
+ <!-- End Tag -->
+ <xsl:text>&lt;/</xsl:text>
+ <xsl:copy-of select="$tag"/>
+ <xsl:text>></xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out attributes of a complex type definition, including
+ those inherited from a base type.
+ Param(s):
+ type (Node) required
+ Complex type definition
+ subTypeAttrs (String) optional
+ List of attributes in sub-types of this current type
+ definition
+ isInherited (boolean) optional
+ If true, display attributes using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display attributes using 'newFields' CSS class.
+ fromTopCType (boolean) optional
+ Set to true if this is being displayed in the XML
+ Instance Representation table of a top-level complex
+ type definition, in which case, 'inherited' attributes
+ and elements are distinguished.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ schemaLoc (String) optional
+ Schema file containing this complex type definition;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleTypeAttrs">
+ <xsl:param name="type"/>
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="fromTopCType">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="$type/@name and contains($typeList, concat('*', $type/@name, '+'))">
+ <!-- Do nothing.
+ Error message will be written out by 'PrintSampleTypeContent' template.
+ -->
+ </xsl:when>
+ <!-- Derivation -->
+ <xsl:when test="$type/xsd:complexContent or $type/xsd:simpleContent">
+ <xsl:choose>
+ <xsl:when test="$type/xsd:complexContent/xsd:restriction">
+ <xsl:call-template name="PrintSampleDerivedTypeAttrs">
+ <xsl:with-param name="derivationElem" select="$type/xsd:complexContent/xsd:restriction"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$type/xsd:simpleContent/xsd:restriction">
+ <xsl:call-template name="PrintSampleDerivedTypeAttrs">
+ <xsl:with-param name="derivationElem" select="$type/xsd:simpleContent/xsd:restriction"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$type/xsd:complexContent/xsd:extension">
+ <xsl:call-template name="PrintSampleDerivedTypeAttrs">
+ <xsl:with-param name="derivationElem" select="$type/xsd:complexContent/xsd:extension"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$type/xsd:simpleContent/xsd:extension">
+ <xsl:call-template name="PrintSampleDerivedTypeAttrs">
+ <xsl:with-param name="derivationElem" select="$type/xsd:simpleContent/xsd:extension"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <!-- No derivation -->
+ <xsl:when test="local-name($type)='complexType'">
+ <xsl:call-template name="PrintSampleAttrList">
+ <xsl:with-param name="list" select="$type"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Ignore base types that are simple types -->
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Helper function 'PrintSampleTypeAttrs' template to
+ handle case of derived types.
+ Param(s):
+ derivationElem (Node) required
+ 'restriction' or 'extension' element
+ subTypeAttrs (String) optional
+ List of attributes in sub-types of
+ this current type definition
+ isInherited (boolean) optional
+ If true, display attributes using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display attributes using 'newFields' CSS class.
+ fromTopCType (boolean) optional
+ Set to true if this is being displayed
+ in the XML Instance Representation table
+ of a top-level complex type definition, in
+ which case, 'inherited' attributes and
+ elements are distinguished.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ schemaLoc (String) optional
+ Schema file containing this derivation element;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleDerivedTypeAttrs">
+ <xsl:param name="derivationElem"/>
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="fromTopCType">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <!-- Get attributes from this type to add to
+ 'subTypeAttrs' list for recursive call on base type -->
+ <xsl:variable name="thisAttrs">
+ <xsl:call-template name="GetAttrList">
+ <xsl:with-param name="list" select="$derivationElem"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Look for base type -->
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$derivationElem/@base"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="defLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="$derivationElem/@base"/>
+ <xsl:with-param name="compType">complex type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <!-- Complex type was found in current schema. -->
+ <xsl:when test="normalize-space($defLoc)='this'">
+ <xsl:variable name="ctype" select="key('complexType', $baseTypeName)"/>
+ <xsl:call-template name="PrintSampleTypeAttrs">
+ <xsl:with-param name="type" select="$ctype"/>
+ <xsl:with-param name="subTypeAttrs" select="concat($subTypeAttrs, $thisAttrs)"/>
+ <xsl:with-param name="isInherited">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Complex type was not found. -->
+ <xsl:when test="normalize-space($defLoc)='' or normalize-space($defLoc)='none' or normalize-space($defLoc)='xml' or normalize-space($defLoc)='xsd'">
+ <!-- IGNORE: Error message will be printed out be
+ 'PrintSampleTypeContent' template. -->
+ </xsl:when>
+ <!-- Complex type was found in external schema. -->
+ <xsl:otherwise>
+ <xsl:variable name="ctype" select="document($defLoc)/xsd:schema/xsd:complexType[@name=$baseTypeName]"/>
+ <xsl:call-template name="PrintSampleTypeAttrs">
+ <xsl:with-param name="type" select="$ctype"/>
+ <xsl:with-param name="subTypeAttrs" select="concat($subTypeAttrs, $thisAttrs)"/>
+ <xsl:with-param name="isInherited">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$defLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Print out attributes in this type -->
+ <xsl:call-template name="PrintSampleAttrList">
+ <xsl:with-param name="list" select="$derivationElem"/>
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false' and $isInherited='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Returns the names and namespaces of attributes
+ in a list of attributes and attribute groups.
+ Param(s):
+ list (Node) required
+ Node containing list of attributes and attribute groups
+ -->
+ <xsl:template name="GetAttrList">
+ <xsl:param name="list"/>
+
+ <xsl:if test="$list">
+ <xsl:for-each select="$list/xsd:attribute | $list/xsd:attributeGroup | $list/xsd:anyAttribute">
+ <xsl:choose>
+ <!-- Attribute declaration -->
+ <xsl:when test="local-name(.)='attribute' and @name">
+ <!-- Get attribute name -->
+ <xsl:variable name="attrName" select="@name"/>
+ <!-- Get attribute namespace -->
+ <xsl:variable name="attrNS">
+ <xsl:call-template name="GetAttributeNS">
+ <xsl:with-param name="attribute" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:value-of select="concat('*', normalize-space($attrNS), '+', normalize-space($attrName), '+')"/>
+ </xsl:when>
+ <!-- Attribute reference -->
+ <xsl:when test="local-name(.)='attribute' and @ref">
+ <!-- Get attribute name -->
+ <xsl:variable name="attrName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Get attribute namespace -->
+ <xsl:variable name="attrNS">
+ <xsl:call-template name="GetAttributeNS">
+ <xsl:with-param name="attribute" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:value-of select="concat('*', normalize-space($attrNS), '+', normalize-space($attrName), '+')"/>
+ </xsl:when>
+ <!-- Attribute Group reference -->
+ <xsl:when test="local-name(.)='attributeGroup' and @ref">
+ <xsl:variable name="attrGrpName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="@ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="GetAttrList">
+ <xsl:with-param name="list" select="key('attributeGroup', $attrGrpName)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Attribute wildcard -->
+ <xsl:when test="local-name(.)='anyAttribute'">
+ </xsl:when>
+ </xsl:choose>
+ </xsl:for-each>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out sample XML instances from a list of attributes and
+ attribute groups.
+ Param(s):
+ list (Node) required
+ Node containing list of attributes and attribute groups
+ subTypeAttrs (String) optional
+ List of attributes in sub-types of
+ the type definition containing this list
+ isInherited (boolean) optional
+ If true, display attributes using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display attributes using 'newFields' CSS class.
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ schemaLoc (String) optional
+ Schema file containing this attribute list;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template name="PrintSampleAttrList">
+ <xsl:param name="list"/>
+ <xsl:param name="subTypeAttrs"/>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:apply-templates select="$list/xsd:attribute | $list/xsd:attributeGroup | $list/xsd:anyAttribute" mode="sample">
+ <xsl:with-param name="subTypeAttrs" select="$subTypeAttrs"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="addBR">true</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ </xsl:template>
+
+ <!--
+ Prints out the element content of a complex type definition,
+ including those inherited from a base type.
+ Param(s):
+ type (Node) required
+ Complex type definition
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ fromTopCType (boolean) optional
+ Set to true if this is being displayed in the XML
+ Instance Representation table of a top-level complex
+ type definition, in which case, 'inherited' attributes
+ and elements are distinguished.
+ addBR (boolean) optional
+ If true, can add <br/> before element content.
+ Applicable only if displaying complex content.
+ schemaLoc (String) optional
+ Schema file containing this type definition;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleTypeContent">
+ <xsl:param name="type"/>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="fromTopCType">false</xsl:param>
+ <xsl:param name="addBR">true</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:if test="$addBR='true'"><br/></xsl:if>
+
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="$type/@name and contains($typeList, concat('*', $type/@name, '+'))"/>
+ <!-- Derivation by restriction on complex content -->
+ <xsl:when test="$type/xsd:complexContent/xsd:restriction">
+ <xsl:variable name="restriction" select="$type/xsd:complexContent/xsd:restriction"/>
+
+ <!-- Test if base type is in schema to print out warning comment-->
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$restriction/@base"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Look for base type -->
+ <xsl:variable name="defLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="$restriction/@base"/>
+ <xsl:with-param name="compType">complex type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Complex type was not found. -->
+ <xsl:when test="normalize-space($defLoc)='' or normalize-space($defLoc)='none' or normalize-space($defLoc)='xml' or normalize-space($defLoc)='xsd'">
+ <div class="other" style="margin-left: {$margin}em;">
+ <xsl:text>&lt;!-- '</xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$restriction/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text>' super type was not found in this schema. Some elements and attributes may be missing. --></xsl:text>
+ </div>
+ </xsl:when>
+ <!-- Complex type was found. -->
+ <xsl:otherwise>
+ <!-- IGNORE element content of base type if by restriction,
+ since current content will override restricted
+ base type's content. -->
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Print out content from this type -->
+ <xsl:if test="$restriction/xsd:*[local-name(.)!='annotation']">
+ <xsl:call-template name="PrintSampleParticleList">
+ <xsl:with-param name="list" select="$restriction"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false' and $isInherited='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+ <!-- Derivation by extension on complex content -->
+ <xsl:when test="$type/xsd:complexContent/xsd:extension">
+ <xsl:variable name="extension" select="$type/xsd:complexContent/xsd:extension"/>
+
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$extension/@base"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="contains($typeList, concat('*', $baseTypeName, '+'))">
+ <div class="other" style="margin-left: {$margin}em">
+ <xsl:text>&lt;-- Extends: </xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$extension/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> (Circular type hierarchy) --&gt;</xsl:text>
+ </div>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Look for base type -->
+ <xsl:variable name="defLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="$extension/@base"/>
+ <xsl:with-param name="compType">complex type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Complex type was found in current schema. -->
+ <xsl:when test="normalize-space($defLoc)='this'">
+ <xsl:variable name="ctype" select="key('complexType', $baseTypeName)"/>
+ <xsl:call-template name="PrintSampleTypeContent">
+ <xsl:with-param name="type" select="$ctype"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="addBR">false</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Complex type was not found. -->
+ <xsl:when test="normalize-space($defLoc)='' or normalize-space($defLoc)='none' or normalize-space($defLoc)='xml' or normalize-space($defLoc)='xsd'">
+ <div class="other" style="margin-left: {$margin}em;">
+ <xsl:text>&lt;!-- '</xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$extension/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text>' super type was not found in this schema. Some elements and attributes may be missing. --></xsl:text>
+ </div>
+ </xsl:when>
+ <!-- Complex type was found in external schema. -->
+ <xsl:otherwise>
+ <xsl:variable name="ctype" select="document($defLoc)/xsd:schema/xsd:complexType[@name=$baseTypeName]"/>
+ <xsl:call-template name="PrintSampleTypeContent">
+ <xsl:with-param name="type" select="$ctype"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="fromTopCType" select="$fromTopCType"/>
+ <xsl:with-param name="addBR">false</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$defLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Print out content from this type -->
+ <xsl:if test="$extension/xsd:*[local-name(.)!='annotation']">
+ <xsl:call-template name="PrintSampleParticleList">
+ <xsl:with-param name="list" select="$extension"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField">
+ <xsl:choose>
+ <xsl:when test="$fromTopCType!='false' and $isInherited='false'">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- Derivation by restriction on simple content -->
+ <xsl:when test="$type/xsd:simpleContent/xsd:restriction">
+ <!-- Print out simple type constraints-->
+ <span style="margin-left: {$margin}em">
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates select="$type/xsd:simpleContent" mode="sample">
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:apply-templates>
+ <xsl:text> </xsl:text>
+ </span><br/>
+ </xsl:when>
+ <!-- Derivation by extension on simple content -->
+ <xsl:when test="$type/xsd:simpleContent/xsd:extension">
+ <!-- Print out base type name -->
+ <span style="margin-left: {$margin}em">
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$type/xsd:simpleContent/xsd:extension/@base"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ </span><br/>
+ </xsl:when>
+ <!-- No derivation: complex type definition -->
+ <xsl:when test="local-name($type)='complexType'">
+ <!-- Print out content from this type -->
+ <xsl:call-template name="PrintSampleParticleList">
+ <xsl:with-param name="list" select="$type"/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="concat($typeList, '*', $type/@name, '+')"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out sample XML instances from a list of
+ element particle.
+ Param(s):
+ list (Node) required
+ Node containing list of element particles
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ isInherited (boolean) optional
+ If true, display elements using 'inherited' CSS class.
+ isNewField (boolean) optional
+ If true, display elements using 'newFields' CSS class.
+ schemaLoc (String) optional
+ Schema file containing this particle list;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ typeList (String) optional
+ List of types in this call chain. Name of type starts
+ with '*', and ends with '+'. (Used to prevent infinite
+ recursive loop.)
+ -->
+ <xsl:template name="PrintSampleParticleList">
+ <xsl:param name="list"/>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="isInherited">false</xsl:param>
+ <xsl:param name="isNewField">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:if test="$list">
+ <xsl:apply-templates select="$list/xsd:group | $list/xsd:sequence | $list/xsd:choice | $list/xsd:all | $list/xsd:element" mode="sample">
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="isInherited" select="$isInherited"/>
+ <xsl:with-param name="isNewField" select="$isNewField"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:apply-templates>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of simple content
+ to be displayed within a sample XML instance.
+ Param(s):
+ simpleContent (Node) required
+ Node containing with the simple content
+ schemaLoc (String) optional
+ Schema file containing these simple constraints;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template name="PrintSampleSimpleConstraints">
+ <xsl:param name="simpleContent"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:choose>
+ <!-- Derivation by restriction -->
+ <xsl:when test="$simpleContent/xsd:restriction">
+ <xsl:call-template name="PrintSampleSimpleRestriction">
+ <xsl:with-param name="restriction" select="$simpleContent/xsd:restriction"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Derivation by list -->
+ <xsl:when test="$simpleContent/xsd:list">
+ <xsl:choose>
+ <xsl:when test="$simpleContent/xsd:list/@itemType">
+ <xsl:text>list of: </xsl:text>
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$simpleContent/xsd:list/@itemType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>list of: [ </xsl:text>
+ <xsl:call-template name="PrintSampleSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="$simpleContent/xsd:list/xsd:simpleType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> ]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- Derivation by union -->
+ <xsl:when test="$simpleContent/xsd:union">
+ <xsl:text>union of: [ </xsl:text>
+
+ <xsl:variable name="hasMemberTypes">
+ <xsl:if test="normalize-space($simpleContent/xsd:union/@memberTypes)!=''">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <xsl:if test="$hasMemberTypes='true'">
+ <xsl:call-template name="PrintWhitespaceList">
+ <xsl:with-param name="value" select="$simpleContent/xsd:union/@memberTypes"/>
+ <xsl:with-param name="compType">type</xsl:with-param>
+ <xsl:with-param name="separator">,</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:for-each select="$simpleContent/xsd:union/xsd:simpleType">
+ <xsl:if test="position()!=1 or $hasMemberTypes='true'">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ <xsl:text>[ </xsl:text>
+ <xsl:call-template name="PrintSampleSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="."/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <xsl:text> ]</xsl:text>
+ </xsl:for-each>
+
+ <xsl:text> ]</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out the constraints of simple content
+ derived by restriction, which is to be displayed
+ within a sample XML instance.
+ Param(s):
+ restriction (Node) required
+ Node containing with the restriction
+ schemaLoc (String) optional
+ Schema file containing this restriction element;
+ if in current schema, 'schemaLoc' is set to 'this'.
+ -->
+ <xsl:template name="PrintSampleSimpleRestriction">
+ <xsl:param name="restriction"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="typeList"/>
+
+ <xsl:variable name="typeName" select="$restriction/parent::xsd:simpleType/@name"/>
+
+ <!-- Print out base type info -->
+ <xsl:choose>
+ <!-- Circular type hierarchy -->
+ <xsl:when test="$typeName != '' and contains($typeList, concat('*', $typeName, '+'))">
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">false</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+ <xsl:text>Circular type reference to '</xsl:text>
+ <xsl:value-of select="$typeName"/>
+ <xsl:text>' in type hierarchy.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Locally-defined base type -->
+ <xsl:when test="$restriction/xsd:simpleType">
+ <xsl:call-template name="PrintSampleSimpleConstraints">
+ <xsl:with-param name="simpleContent" select="$restriction/xsd:simpleType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ <xsl:with-param name="typeList" select="$typeList"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Base type reference -->
+ <xsl:when test="$restriction">
+ <xsl:variable name="baseTypeRef" select="$restriction/@base"/>
+ <xsl:variable name="baseTypeName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="baseTypeNS">
+ <xsl:call-template name="GetRefNS">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Write out reference to base type -->
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="$baseTypeRef"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- Regular Expression Pattern -->
+ <xsl:variable name="pattern">
+ <xsl:call-template name="PrintPatternFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Range -->
+ <xsl:variable name="range">
+ <xsl:call-template name="PrintRangeFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Length -->
+ <xsl:variable name="length">
+ <xsl:call-template name="PrintLengthFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Print out facets -->
+ <xsl:if test="$restriction/xsd:enumeration">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="PrintEnumFacets">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$pattern !=''">
+ <xsl:text> (</xsl:text>
+ <xsl:copy-of select="$pattern"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$range !=''">
+ <xsl:text> (</xsl:text>
+ <xsl:copy-of select="$range"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$restriction/xsd:totalDigits">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="PrintTotalDigitsFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$restriction/xsd:fractionDigits">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="PrintFractionDigitsFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$length !=''">
+ <xsl:text> (</xsl:text>
+ <xsl:copy-of select="$length"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ <xsl:if test="$restriction/xsd:whiteSpace">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="PrintWhitespaceFacet">
+ <xsl:with-param name="simpleRestrict" select="$restriction"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!-- ******** Schema Component Representation table ******** -->
+
+ <!--
+ Prints out the Schema Component Representation table
+ for a top-level schema component.
+ Param(s):
+ component (Node) required
+ Top-level schema component
+ -->
+ <xsl:template name="SchemaComponentTable">
+ <xsl:param name="component"/>
+
+ <xsl:variable name="componentID">
+ <xsl:call-template name="GetComponentID">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:call-template name="CollapseableBox">
+ <xsl:with-param name="id" select="concat($componentID, '_scbox')"/>
+ <xsl:with-param name="styleClass">schemaComponent</xsl:with-param>
+ <xsl:with-param name="caption">Schema Component Representation</xsl:with-param>
+ <xsl:with-param name="contents">
+ <xsl:apply-templates select="$component" mode="schemaComponent"/>
+ </xsl:with-param>
+ <xsl:with-param name="isOpened">false</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ declarations.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:attribute[@name] | xsd:element[@name]" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: name -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">name</xsl:with-param>
+ <xsl:with-param name="attrValue" select="normalize-space(@name)"/>
+ </xsl:call-template>
+ <!-- Attribute: type -->
+ <xsl:if test="@type">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">type</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="normalize-space(@type)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*name+*type+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ definitions and key/uniqueness constraints.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:attributeGroup[@name] | xsd:complexType[@name] | xsd:simpleType[@name] | xsd:group[@name] | xsd:key | xsd:unique" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: name -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">name</xsl:with-param>
+ <xsl:with-param name="attrValue" select="normalize-space(@name)"/>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*name+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of attribute
+ references.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:attribute[@ref]" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: ref -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">ref</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintAttributeRef">
+ <xsl:with-param name="ref" select="normalize-space(@ref)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*ref+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of attribute group
+ references.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:attributeGroup[@ref]" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: ref -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">ref</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintAttributeGroupRef">
+ <xsl:with-param name="ref" select="normalize-space(@ref)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*ref+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of element
+ references.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:element[@ref]" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: ref -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">ref</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintElementRef">
+ <xsl:with-param name="ref" select="normalize-space(@ref)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*ref+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of model group
+ references.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:group[@ref]" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: ref -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">ref</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintGroupRef">
+ <xsl:with-param name="ref" select="normalize-space(@ref)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*ref+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ 'appinfo' and 'documentation' elements.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:appinfo | xsd:documentation" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: source -->
+ <xsl:if test="@source">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">source</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintURI">
+ <xsl:with-param name="uri" select="normalize-space(@source)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*source+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="hasAnyContent">true</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ key reference constraints.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:keyref" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: name -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">name</xsl:with-param>
+ <xsl:with-param name="attrValue" select="normalize-space(@name)"/>
+ </xsl:call-template>
+ <!-- Attribute: refers -->
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">refer</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintKeyRef">
+ <xsl:with-param name="ref">
+ <xsl:value-of select="normalize-space(@refer)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*name+*refer+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ derivations by extension and restrictions.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:extension | xsd:restriction" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: base -->
+ <xsl:if test="@base">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">base</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="normalize-space(@base)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*base+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ derivations by list.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:list" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: itemType-->
+ <xsl:if test="@itemType">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">itemType</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintTypeRef">
+ <xsl:with-param name="ref" select="normalize-space(@itemType)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*itemType+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ derivations by union.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:union" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: memberTypes-->
+ <xsl:if test="@memberTypes">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">memberTypes</xsl:with-param>
+ <xsl:with-param name="attrValue">
+ <xsl:call-template name="PrintWhitespaceList">
+ <xsl:with-param name="value" select="normalize-space(@memberTypes)"/>
+ <xsl:with-param name="compType">type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*memberTypes+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out schema component representation of
+ the root schema element.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="xsd:schema" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <!-- Attribute: source -->
+ <xsl:if test="@xml:lang">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName">xml:lang</xsl:with-param>
+ <xsl:with-param name="attrValue" select="normalize-space(@xml:lang)"/>
+ </xsl:call-template>
+ </xsl:if>
+ <!-- Other attributes -->
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="attrsNotToDisplay">*lang+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="includeFilter">*include+*import+*redefine+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Default way to print out schema component representation.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="*" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <xsl:call-template name="DisplaySchemaComponent">
+ <xsl:with-param name="component" select="."/>
+ <xsl:with-param name="margin" select="$margin"/>
+ <xsl:with-param name="attributes">
+ <xsl:call-template name="DisplayOtherAttributes">
+ <xsl:with-param name="component" select="."/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="excludeFilter">*annotation+</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Prints out comments in schema component representation.
+ Param(s):
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ -->
+ <xsl:template match="comment()" mode="schemaComponent">
+ <xsl:param name="margin">0</xsl:param>
+
+ <div class="comment" style="margin-left: {$margin}em">
+ <xsl:text>&lt;--</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>--&gt;</xsl:text>
+ </div>
+ </xsl:template>
+
+ <!--
+ Displays a schema element in the correct format
+ for the Schema Component Representation table, e.g.
+ tags are one color, and content are another.
+ Param(s):
+ component (Node) required
+ Schema element to be displayed
+ attributes (Result Tree Fragment) optional
+ Pre-formatted attributes of schema element
+ margin (nonNegativeInteger) optional
+ Number of 'em' to indent from left
+ hasAnyContent (boolean) optional
+ Set to true if schema element can accept
+ child elements from namespaces other than
+ the schema namespace, e.g. 'documentation'
+ and 'appinfo'
+ includeFilter (String) optional
+ List of element names, sandwiched between the
+ characters, '*' and '+'. If specified, only the
+ child elements of the component with tags in
+ the list will be displayed.
+ excludeFilter (String) optional
+ List of element names, sandwiched between the
+ characters, '*' and '+'. If specified, display
+ all child elements of the component, except
+ those with tags in the list.
+ -->
+ <xsl:template name="DisplaySchemaComponent">
+ <xsl:param name="component"/>
+ <xsl:param name="attributes"/>
+ <xsl:param name="margin">0</xsl:param>
+ <xsl:param name="hasAnyContent">false</xsl:param>
+ <xsl:param name="includeFilter"/>
+ <xsl:param name="excludeFilter"/>
+
+ <xsl:variable name="tag">
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix">
+ <xsl:call-template name="GetXSDPrefix"/>
+ </xsl:with-param>
+ <xsl:with-param name="nolink">true</xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="local-name($component)"/>
+ </xsl:variable>
+
+ <div style="margin-left: {$margin}em">
+ <!-- Start Tag -->
+ <xsl:text>&lt;</xsl:text>
+ <span class="scTag">
+ <xsl:copy-of select="$tag"/>
+ </span>
+ <!-- Attributes -->
+ <xsl:copy-of select="$attributes"/>
+ <!-- Content -->
+ <xsl:variable name="content">
+ <xsl:choose>
+ <!-- Include filter is on -->
+ <xsl:when test="$includeFilter!=''">
+ <xsl:apply-templates select="$component/xsd:*[contains($includeFilter, concat('*', local-name(.), '+'))]" mode="schemaComponent">
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ </xsl:apply-templates>
+ <div class="scContent" style="margin-left: {$ELEM_INDENT}em">...</div>
+ </xsl:when>
+ <!-- Exclude filter is on -->
+ <xsl:when test="$excludeFilter!=''">
+ <xsl:apply-templates select="comment() | $component/xsd:*[not(contains($excludeFilter, concat('*', local-name(.), '+')))]" mode="schemaComponent">
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ </xsl:apply-templates>
+ </xsl:when>
+ <!-- Permits any content -->
+ <xsl:when test="$hasAnyContent='true'">
+ <div class="scContent" style="margin-left: {$ELEM_INDENT}em">
+ <xsl:apply-templates select="comment() | $component/* | $component/text()" mode="xpp"/>
+ </div>
+ </xsl:when>
+ <!-- Contains schema elements -->
+ <xsl:otherwise>
+ <xsl:apply-templates select="comment() | $component/xsd:*" mode="schemaComponent">
+ <xsl:with-param name="margin" select="$ELEM_INDENT"/>
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Has content -->
+ <xsl:when test="normalize-space($content)!=''">
+ <!-- End of start tag -->
+ <xsl:text>></xsl:text>
+
+ <!-- Content -->
+ <xsl:copy-of select="$content"/>
+
+ <!-- End Tag -->
+ <xsl:text>&lt;/</xsl:text>
+ <span class="scTag">
+ <xsl:copy-of select="$tag"/>
+ </span>
+ <xsl:text>></xsl:text>
+ </xsl:when>
+ <!-- Empty content -->
+ <xsl:otherwise>
+ <!-- End of start tag -->
+ <xsl:text>/></xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </div>
+ </xsl:template>
+
+ <!--
+ Displays a schema attribute in the correct format
+ for the Schema Component Representation table, e.g.
+ tags are one color, and content are another.
+ Param(s):
+ attrName (String) required
+ Name of attribute
+ attrValue (Result Tree Fragment) required
+ Value of attribute, which may be links
+ -->
+ <xsl:template name="DisplayAttr">
+ <xsl:param name="attrName"/>
+ <xsl:param name="attrValue"/>
+
+ <xsl:text> </xsl:text>
+ <span class="scTag">
+ <xsl:value-of select="$attrName"/>
+ </span>
+ <xsl:text>="</xsl:text>
+ <xsl:if test="normalize-space($attrValue)!=''">
+ <span class="scContent">
+ <xsl:copy-of select="$attrValue"/>
+ </span>
+ </xsl:if>
+ <xsl:text>"</xsl:text>
+ </xsl:template>
+
+ <!--
+ Displays attributes from a schema element, unless
+ otherwise specified, in the correct format
+ for the Schema Component Representation table, e.g.
+ tags are one color, and content are another.
+ Param(s):
+ component (Node) required
+ Schema element whose attributes are to be displayed
+ attrsNotToDisplay (String) required
+ List of attributes not to be displayed
+ Each attribute name should prepended with '*'
+ and appended with '+'
+ -->
+ <xsl:template name="DisplayOtherAttributes">
+ <xsl:param name="component"/>
+ <xsl:param name="attrsNotToDisplay"/>
+
+ <xsl:for-each select="$component/attribute::*">
+ <xsl:variable name="attrName" select="local-name(.)"/>
+ <xsl:if test="not(contains($attrsNotToDisplay, concat('*', $attrName, '+')))">
+ <xsl:call-template name="DisplayAttr">
+ <xsl:with-param name="attrName" select="normalize-space($attrName)"/>
+ <xsl:with-param name="attrValue" select="normalize-space(.)"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+
+ <!-- ******** XML Pretty Printer ******** -->
+
+ <!--
+ Puts XHTML elements into the result.
+ -->
+ <xsl:template match="html:*" mode="html">
+ <xsl:element name="{local-name(.)}">
+ <xsl:for-each select="@*">
+ <xsl:copy-of select="."/>
+ </xsl:for-each>
+ <xsl:apply-templates select="* | text()" mode="html"/>
+ </xsl:element>
+ </xsl:template>
+
+ <!--
+ Displays non-XHTML elements found within XHTML elements.
+ -->
+ <xsl:template match="*" mode="html">
+ <xsl:call-template name="WriteElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="mode">html</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!--
+ Displays text node.
+ -->
+ <xsl:template match="text()" mode="html">
+ <xsl:value-of select="."/>
+ </xsl:template>
+
+ <!--
+ Displays an arbitrary XML element.
+ -->
+ <xsl:template match="*" mode="xpp">
+ <code>
+ <xsl:call-template name="WriteElement">
+ <xsl:with-param name="element" select="."/>
+ <xsl:with-param name="mode">xpp</xsl:with-param>
+ </xsl:call-template>
+ </code>
+ </xsl:template>
+
+ <!--
+ Displays an arbitrary XML text node.
+ -->
+ <xsl:template match="text()" mode="xpp">
+ <xsl:value-of select="."/>
+ </xsl:template>
+
+ <!--
+ Displays an XML comment.
+ -->
+ <xsl:template match="comment()" mode="xpp">
+ <div class="comment">
+ <xsl:text>&lt;--</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>--&gt;</xsl:text>
+ </div>
+ </xsl:template>
+
+ <!--
+ Displays an XML element in the documentation, e.g.
+ tags are escaped.
+ Param(s):
+ element (Node) required
+ XML element to display
+ mode (xpp|html) required
+ Which mode to invoke for child elements
+ -->
+ <xsl:template name="WriteElement">
+ <xsl:param name="element"/>
+ <xsl:param name="mode">xpp</xsl:param>
+
+ <!-- Start Tag -->
+ <xsl:text>&lt;</xsl:text>
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="name($element)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="local-name($element)"/>
+ <!-- Attributes -->
+ <xsl:for-each select="$element/@*">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="name(.)"/>
+ <xsl:text>="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>"</xsl:text>
+ </xsl:for-each>
+
+ <xsl:choose>
+ <xsl:when test="$element/* | $element/text()">
+ <!-- Close Start Tag -->
+ <xsl:text>> </xsl:text>
+ <!-- Content -->
+ <xsl:choose>
+ <xsl:when test="$mode!='xpp'">
+ <xsl:apply-templates select="$element/* | $element/text()" mode="html"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <div style="margin-left: {$ELEM_INDENT}em">
+ <xsl:apply-templates select="$element/* | $element/text()" mode="xpp"/>
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- End Tag -->
+ <xsl:text>&lt;/</xsl:text>
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="name($element)"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="local-name($element)"/>
+ <xsl:text>></xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Close Start Tag -->
+ <xsl:text>/></xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <!-- ******** Templates for Handling References ******** -->
+
+ <!--
+ Prints out a reference to a term in the glossary section.
+ Param(s):
+ code (String) required
+ Unique ID of glossary term
+ term (String) optional
+ Glossary term
+ -->
+ <xsl:template name="PrintGlossaryTermRef">
+ <xsl:param name="code"/>
+ <xsl:param name="term"/>
+
+ <xsl:choose>
+ <xsl:when test="$code !='' and normalize-space(translate($printGlossary,'TRUE','true'))='true'">
+ <a title="Look up '{$term}' in glossary" href="#{concat($TERM_PREFIX, $code)}">
+ <xsl:choose>
+ <xsl:when test="$term!=''">
+ <xsl:value-of select="$term"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>[term]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$term"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a reference to a namespace in the schema.
+ Param(s):
+ prefix (String) required
+ Namespace prefix referenced
+ schemaLoc (String) optional
+ Schema file containing this namespace prefix;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintNamespaceRef">
+ <xsl:param name="prefix"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:if test="$prefix!=''">
+ <xsl:choose>
+ <xsl:when test="/xsd:schema/namespace::*[local-name(.)=normalize-space($prefix)]">
+ <xsl:variable name="link">
+ <xsl:if test="normalize-space($schemaLoc)!='this'">
+ <xsl:call-template name="GetSchemaDocLocation">
+ <xsl:with-param name="uri" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="concat('#', $NS_PREFIX, $prefix)"/>
+ </xsl:variable>
+ <a href="{$link}" title="Find out namespace of '{$prefix}' prefix">
+ <xsl:value-of select="$prefix"/>
+ </a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="title">
+ <xsl:text>Unknown namespace prefix, </xsl:text>
+ <xsl:value-of select="$prefix"/>
+ <xsl:text>.</xsl:text>
+ </xsl:variable>
+ <a href="javascript:void(0)" onclick="alert('{$title}')" title="{$title}">
+ <xsl:value-of select="$prefix"/>
+ </a>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Generates a link to an attribute.
+ Param(s):
+ name (String) optional
+ Name of attribute
+ ref (String) optional
+ Reference to attribute
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this attribute reference
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintAttributeRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">attribute</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">attribute</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Generates a link to an attribute group.
+ Param(s):
+ name (String) optional
+ Name of attribute group
+ ref (String) optional
+ Reference to attribute group
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this attribute group reference
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintAttributeGroupRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">attribute group</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">attribute group</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Generates a link to an element.
+ Param(s):
+ name (String) optional
+ Name of element
+ ref (String) optional
+ Reference to element
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this element reference
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintElementRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">element</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">element</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Generates a link to a group.
+ Param(s):
+ name (String) optional
+ Name of group
+ ref (String) optional
+ Reference to group
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this group reference
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintGroupRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">group</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">group</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Generates a link to a key/uniqueness constraint.
+ Param(s):
+ name (String) optional
+ Name of key/uniqueness constraint
+ ref (String) optional
+ Reference to key/uniqueness constraint
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this key/uniqueness constraint
+ reference; if in current schema, 'schemaLoc' is set
+ to 'this'
+ -->
+ <xsl:template name="PrintKeyRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">uniqueness/key constraint</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">uniqueness/key constraint</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Generates a link to a type.
+ Param(s):
+ name (String) optional
+ Name of type
+ ref (String) optional
+ Reference to type
+ (One of 'name' and 'ref' must be provided.)
+ schemaLoc (String) optional
+ Schema file containing this type reference'
+ if in current schema, 'schemaLoc' is set
+ to 'this'
+ -->
+ <xsl:template name="PrintTypeRef">
+ <xsl:param name="name"/>
+ <xsl:param name="ref"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$name!=''">
+ <span class="type">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType">type</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </span>
+ </xsl:when>
+ <xsl:when test="$ref!=''">
+ <span class="type">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType">type</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </span>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a link to a schema component's section.
+ Param(s):
+ baseFile (String) optional
+ Documentation file of schema containing this
+ component.
+ If this component belongs to the current schema,
+ omit this variable.
+ If this component is from an included or imported
+ schema, provide this variable.
+ name (String) required
+ Name of schema component
+ compType (String) required
+ Type of schema component
+ errMsg (String) optional
+ Sentence fragment.
+ If specified, link will open up an alert box with
+ an error message. For example, if 'errMsg' was set
+ to "could not be found", 'name' was "x", and
+ 'compType' was "type", the error message would be:
+ "x" type definition could not be found.
+ The sentence fragment should not:
+ -start with a capital letter.
+ -have a space in front
+ -end with a period.
+ schemaLoc (String) optional
+ Schema file containing this component;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintCompName">
+ <xsl:param name="baseFile"/>
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+ <xsl:param name="errMsg"/>
+
+ <!-- Get correct terminology for statements -->
+ <xsl:variable name="noun">
+ <xsl:choose>
+ <xsl:when test="$compType='element' or $compType='attribute'">
+ <xsl:text>declaration</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>definition</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get prefix to use in anchor name. -->
+ <xsl:variable name="compPrefix">
+ <xsl:choose>
+ <xsl:when test="$compType='attribute'">
+ <xsl:value-of select="$ATTR_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="$compType='attribute group'">
+ <xsl:value-of select="$ATTR_GRP_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="$compType='element'">
+ <xsl:value-of select="$ELEM_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="$compType='group'">
+ <xsl:value-of select="$GRP_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="$compType='type'">
+ <xsl:value-of select="$TYPE_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="$compType='uniqueness/key constraint'">
+ <xsl:value-of select="$KEY_PREFIX"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get base URI. -->
+ <xsl:variable name="baseURI">
+ <xsl:choose>
+ <xsl:when test="$baseFile!=''">
+ <xsl:value-of select="$baseFile"/>
+ </xsl:when>
+ <xsl:when test="normalize-space($schemaLoc)!='this'">
+ <xsl:call-template name="GetSchemaDocLocation">
+ <xsl:with-param name="uri" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Generate message. -->
+ <xsl:variable name="title">
+ <xsl:choose>
+ <!-- Error message was provided. -->
+ <xsl:when test="$errMsg!=''">
+ <xsl:text>"</xsl:text><xsl:value-of select="$name"/><xsl:text>" </xsl:text>
+ <xsl:value-of select="$compType"/><xsl:text> </xsl:text>
+ <xsl:value-of select="$noun"/><xsl:text> </xsl:text>
+ <xsl:value-of select="$errMsg"/>
+ </xsl:when>
+ <!-- There exists a link to the schema component's
+ documentation. -->
+ <xsl:otherwise>
+ <xsl:text>Jump to "</xsl:text>
+ <xsl:value-of select="$name"/>
+ <xsl:text>" </xsl:text>
+ <xsl:value-of select="$compType"/>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="$noun"/>
+ <!-- External link -->
+ <xsl:if test="normalize-space($baseURI)!=''">
+ <xsl:text>(located in external schema documentation)</xsl:text>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>.</xsl:text>
+ </xsl:variable>
+
+ <!-- Generate href link -->
+ <xsl:variable name="link">
+ <xsl:choose>
+ <!-- Error message was provided. -->
+ <xsl:when test="$errMsg!=''">
+ <xsl:text>javascript:void(0)</xsl:text>
+ </xsl:when>
+ <!-- There exists a link to the schema component's
+ documentation. -->
+ <xsl:otherwise>
+ <!-- Base URI -->
+ <xsl:value-of select="normalize-space($baseURI)"/>
+ <!-- Anchor within URI -->
+ <xsl:value-of select="concat('#',normalize-space($compPrefix),normalize-space($name))"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <a title="{$title}" href="{$link}">
+ <!-- External link -->
+ <xsl:if test="normalize-space($baseURI)!=''">
+ <xsl:attribute name="class">externalLink</xsl:attribute>
+ </xsl:if>
+
+ <!-- Error message was provided. -->
+ <xsl:if test="$errMsg!=''">
+ <xsl:attribute name="onclick">
+ <xsl:text>alert('</xsl:text>
+ <xsl:value-of select="$title"/>
+ <xsl:text>');</xsl:text>
+ </xsl:attribute>
+ </xsl:if>
+
+ <xsl:value-of select="$name"/>
+ </a>
+ </xsl:template>
+
+ <!--
+ Prints out a reference to a schema component.
+ This template will try to work out which schema that this
+ component belongs to and print out the appropriate link.
+ component.
+ It will also print out the namespace prefix given
+ in the reference.
+ Param(s):
+ ref (String) required
+ Reference to schema component
+ compType (String) required
+ Type of schema component
+ schemaLoc (String) optional
+ Schema file containing this component reference;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintCompRef">
+ <xsl:param name="ref"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <!-- Get correct terminology for statements -->
+ <xsl:variable name="noun">
+ <xsl:choose>
+ <xsl:when test="$compType='element' or $compType='attribute'">
+ <xsl:text>declaration</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>definition</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get local name -->
+ <xsl:variable name="refName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get prefix -->
+ <xsl:variable name="refPrefix">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="$ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get prefix of this schema's target namespace -->
+ <xsl:variable name="tnPrefix">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:variable>
+
+ <!-- Get file location of the schema component that is
+ being referenced. -->
+ <xsl:variable name="compLoc">
+ <xsl:call-template name="FindComponent">
+ <xsl:with-param name="ref" select="$ref"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Print prefix -->
+ <xsl:call-template name="PrintNSPrefix">
+ <xsl:with-param name="prefix" select="$refPrefix"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+
+ <!-- Print local name -->
+ <xsl:choose>
+ <!-- Component from XML Schema's or XML's namespace -->
+ <xsl:when test="normalize-space($compLoc)='xsd' or normalize-space($compLoc)='xml'">
+ <xsl:value-of select="$refName"/>
+ </xsl:when>
+ <!-- Component found in this schema. -->
+ <xsl:when test="normalize-space($compLoc)='this'">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$refName"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Component not found. -->
+ <xsl:when test="normalize-space($compLoc)='' or normalize-space($compLoc)='none'">
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="name" select="$refName"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="errMsg">could not be found</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Component found in an external schema. -->
+ <xsl:otherwise>
+ <!-- Get documentation file for included schema. -->
+ <xsl:variable name="docFile">
+ <xsl:call-template name="GetSchemaDocLocation">
+ <xsl:with-param name="uri" select="$compLoc"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:call-template name="PrintCompName">
+ <xsl:with-param name="baseFile" select="$docFile"/>
+ <xsl:with-param name="name" select="$refName"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <!-- ******** Templates for Finding Components in Different
+ Schema documents ******** -->
+
+ <!-- Special key words: xml, xsd, this, none -->
+ <xsl:template name="FindComponent">
+ <xsl:param name="ref"/>
+ <xsl:param name="compType"/>
+
+ <!-- Get local name -->
+ <xsl:variable name="refName">
+ <xsl:call-template name="GetRefName">
+ <xsl:with-param name="ref" select="$ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get prefix -->
+ <xsl:variable name="refPrefix">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="$ref"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get prefix of this schema's target namespace -->
+ <xsl:variable name="tnPrefix">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:variable>
+
+ <!-- Get prefix of XML Schema -->
+ <xsl:variable name="xsdPrefix">
+ <xsl:call-template name="GetXSDPrefix"/>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Schema component from XML Schema's namespace,
+ unless this schema is for XML Schema -->
+ <xsl:when test="$refPrefix=$xsdPrefix and $xsdPrefix!=$tnPrefix">
+ <xsl:text>xsd</xsl:text>
+ </xsl:when>
+ <!-- Schema component from XML's namespace -->
+ <xsl:when test="$refPrefix='xml'">
+ <xsl:text>xml</xsl:text>
+ </xsl:when>
+ <!-- Schema component from current schema's namespace -->
+ <xsl:when test="$refPrefix=$tnPrefix">
+ <xsl:call-template name="FindComponentInSchema">
+ <xsl:with-param name="name" select="$refName"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schema" select="/xsd:schema"/>
+ <xsl:with-param name="schemaFileLoc">this</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Schema component from imported namespace -->
+ <xsl:when test="normalize-space(translate($searchImportedSchemas, 'TRUE', 'true'))='true'">
+ <xsl:variable name="refNS" select="/xsd:schema/namespace::*[local-name(.)=normalize-space($refPrefix)]"/>
+ <xsl:call-template name="FindComponentInImportedSchemas">
+ <xsl:with-param name="name" select="$refName"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="compNS" select="$refNS"/>
+ <xsl:with-param name="schema" select="/xsd:schema"/>
+ <xsl:with-param name="index">1</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="FindComponentInSchema">
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schema"/>
+ <xsl:param name="schemaFileLoc"/>
+ <xsl:param name="schemasSearched"/>
+
+ <!-- Don't examine this schema if we've already
+ searched it. Prevents infinite recursion.
+ Also check if schema actually exists. -->
+ <xsl:if test="$schema and not(contains($schemasSearched, concat('*', $schemaFileLoc, '+')))">
+ <!-- Find out if the component is in this schema -->
+ <xsl:variable name="thisResult">
+ <xsl:call-template name="IsComponentInSchema">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schema" select="$schema"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Component found -->
+ <xsl:when test="normalize-space($thisResult)='true'">
+ <xsl:value-of select="$schemaFileLoc"/>
+ </xsl:when>
+ <!-- Component not found -->
+ <xsl:when test="normalize-space(translate($searchIncludedSchemas, 'TRUE', 'true'))='true'">
+ <!-- Search included schemas -->
+ <xsl:variable name="includeResult">
+ <xsl:call-template name="FindComponentInIncludedSchemas">
+ <xsl:with-param name="schema" select="$schema"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="index">1</xsl:with-param>
+ <xsl:with-param name="schemasSearched" select="concat($schemasSearched, '*', $schemaFileLoc, '+')"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space($includeResult)!='' and normalize-space($includeResult)!='none'">
+ <xsl:value-of select="$includeResult"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Search redefined schemas -->
+ <xsl:call-template name="FindComponentInRedefinedSchemas">
+ <xsl:with-param name="schema" select="$schema"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="index">1</xsl:with-param>
+ <xsl:with-param name="schemasSearched" select="concat($schemasSearched, '*', $schemaFileLoc, '+')"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="IsComponentInSchema">
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schema"/>
+
+ <xsl:choose>
+ <!-- Schema not found. -->
+ <xsl:when test="not($schema)">
+ <xsl:text>false</xsl:text>
+ </xsl:when>
+ <!-- Search for attribute declaration. -->
+ <xsl:when test="$compType='attribute' and $schema/xsd:attribute[@name=$name]">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for attribute group definition. -->
+ <xsl:when test="$compType='attribute group' and ($schema/xsd:attributeGroup[@name=$name] or $schema/xsd:redefine/xsd:attributeGroup[@name=$name])">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for element declaration. -->
+ <xsl:when test="$compType='element' and $schema/xsd:element[@name=$name]">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for group definition. -->
+ <xsl:when test="$compType='group' and ($schema/xsd:group[@name=$name] or $schema/xsd:redefine/xsd:group[@name=$name])">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for complex type definition. -->
+ <xsl:when test="($compType='type' or $compType='complex type') and ($schema/xsd:complexType[@name=$name] or $schema/xsd:redefine/xsd:complexType[@name=$name])">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for simple type definition. -->
+ <xsl:when test="($compType='type' or $compType='simple type') and ($schema/xsd:simpleType[@name=$name] or $schema/xsd:redefine/xsd:simpleType[@name=$name])">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Search for uniqueness/key constraint definition. -->
+ <xsl:when test="$compType='uniqueness/key constraint' and ($schema//xsd:element/xsd:key[@name=$name] or $schema//xsd:element/xsd:unique[@name=$name])">
+ <xsl:text>true</xsl:text>
+ </xsl:when>
+ <!-- Component not found. -->
+ <xsl:otherwise>
+ <xsl:text>false</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="FindComponentInIncludedSchemas">
+ <xsl:param name="schema"/>
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schemasSearched"/>
+ <xsl:param name="index">1</xsl:param>
+
+ <xsl:if test="count($schema/xsd:include) &gt;= number($index)">
+ <!-- Get the 'schemaLocation' attribute of the 'include'
+ element in this schema at position, 'index'. -->
+ <xsl:variable name="schemaLoc" select="$schema/xsd:include[position()=$index]/@schemaLocation"/>
+
+ <xsl:variable name="thisResult">
+ <!-- Search for the component in the current
+ included schema. -->
+ <xsl:call-template name="FindComponentInSchema">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schema" select="document($schemaLoc)/xsd:schema"/>
+ <xsl:with-param name="schemaFileLoc" select="$schemaLoc"/>
+ <xsl:with-param name="schemasSearched" select="$schemasSearched"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Component was found, so return result. -->
+ <xsl:when test="normalize-space($thisResult)!='' and normalize-space($thisResult)!='none'">
+ <xsl:value-of select="$thisResult"/>
+ </xsl:when>
+ <!-- Component was not found, so keep on searching. -->
+ <xsl:otherwise>
+ <!-- Examine other included schemas in this schema -->
+ <xsl:call-template name="FindComponentInIncludedSchemas">
+ <xsl:with-param name="schema" select="$schema"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="index" select="number($index)+1"/>
+ <xsl:with-param name="schemasSearched" select="concat($schemasSearched, '*', $schemaLoc, '+')"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="FindComponentInRedefinedSchemas">
+ <xsl:param name="schema"/>
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="schemasSearched"/>
+ <xsl:param name="index">1</xsl:param>
+
+ <xsl:if test="count($schema/xsd:redefine) &gt;= number($index)">
+ <!-- Get the 'schemaLocation' attribute of the 'redefine'
+ element in this schema at position, 'index'. -->
+ <xsl:variable name="schemaLoc" select="$schema/xsd:redefine[position()=$index]/@schemaLocation"/>
+
+ <xsl:variable name="thisResult">
+ <!-- Search for the component in the current
+ redefined schema. -->
+ <xsl:call-template name="FindComponentInSchema">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schema" select="document($schemaLoc)/xsd:schema"/>
+ <xsl:with-param name="schemaFileLoc" select="$schemaLoc"/>
+ <xsl:with-param name="schemasSearched" select="$schemasSearched"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Component was found, so return result. -->
+ <xsl:when test="normalize-space($thisResult)!='' and normalize-space($thisResult)!='none'">
+ <xsl:value-of select="$thisResult"/>
+ </xsl:when>
+ <!-- Component was not found, so keep on searching. -->
+ <xsl:otherwise>
+ <!-- Examine other redefined schemas in this schema -->
+ <xsl:call-template name="FindComponentInRedefinedSchemas">
+ <xsl:with-param name="schema" select="$schema"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="index" select="number($index)+1"/>
+ <xsl:with-param name="schemasSearched" select="concat($schemasSearched, '*', $schemaLoc, '+')"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="FindComponentInImportedSchemas">
+ <xsl:param name="schema"/>
+ <xsl:param name="name"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="compNS"/>
+ <xsl:param name="schemasSearched"/>
+ <xsl:param name="index">1</xsl:param>
+
+ <xsl:if test="count($schema/xsd:import) &gt;= number($index)">
+ <!-- Get the 'namespace' attribute of the 'import'
+ element in this schema at position, 'index'. -->
+ <xsl:variable name="schemaNS" select="$schema/xsd:import[position()=$index]/@namespace"/>
+ <!-- Get the 'schemaLocation' attribute. -->
+ <xsl:variable name="schemaLoc" select="$schema/xsd:import[position()=$index]/@schemaLocation"/>
+
+ <xsl:variable name="thisResult">
+ <!-- Check that the imported schema has the matching
+ namespace as the component that we're looking
+ for. -->
+ <xsl:if test="normalize-space($compNS)=normalize-space($schemaNS)">
+ <!-- Search for the component in the current
+ imported schema. -->
+ <xsl:call-template name="FindComponentInSchema">
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schema" select="document($schemaLoc)/xsd:schema"/>
+ <xsl:with-param name="schemaFileLoc" select="$schemaLoc"/>
+ <xsl:with-param name="schemasSearched" select="$schemasSearched"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- Component was found, so return result. -->
+ <xsl:when test="normalize-space($thisResult)!='' and normalize-space($thisResult)!='none'">
+ <xsl:value-of select="$thisResult"/>
+ </xsl:when>
+ <!-- Component was not found, so keep on searching. -->
+ <xsl:otherwise>
+ <!-- Examine other included schemas in this schema -->
+ <xsl:call-template name="FindComponentInImportedSchemas">
+ <xsl:with-param name="schema" select="$schema"/>
+ <xsl:with-param name="name" select="$name"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="compNS" select="$compNS"/>
+ <xsl:with-param name="index" select="number($index)+1"/>
+ <xsl:with-param name="schemasSearched" select="concat($schemasSearched, '*', $schemaLoc, '+')"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+
+ <!-- ******** General Utility Templates ******** -->
+
+ <!--
+ Creates a box that can be opened and closed, such
+ that the contents can be hidden away until a button
+ is pressed.
+ Param(s):
+ id (String) required
+ Unique ID of the 'div' box
+ caption (String) required
+ Text describing the contents of the box;
+ it will always be shown even when the box
+ is closed
+ contents (String) required
+ Contents of box, which may appear and disappear
+ with the press of a button.
+ anchor (String) optional
+ Anchor, e.g. <a name="...", for this box
+ styleClass (String) optional
+ Additional CSS class for the entire collapseable box
+ isOpened (String) optional
+ Set to true if initially opened, and
+ false if initially closed
+ -->
+ <xsl:template name="CollapseableBox">
+ <xsl:param name="id"/>
+ <xsl:param name="caption"/>
+ <xsl:param name="contents"/>
+ <xsl:param name="anchor"/>
+ <xsl:param name="styleClass"/>
+ <xsl:param name="isOpened">false</xsl:param>
+
+ <xsl:variable name="buttonID" select="concat($id, '_button')"/>
+
+ <div class="{$styleClass} box">
+ <div>
+ <!-- Button to control the opening and closing of the 'div' box -->
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <input type="button" id="{$buttonID}" class="control" onclick="switchState('{$id}'); return false;" style="display: none"/>
+ <!--
+ Button's 'display' property is set to 'none',
+ so that button will only be displayed if
+ box can be successfully opened and closed.
+ -->
+ </xsl:if>
+ <!-- Box Title -->
+ <xsl:text> </xsl:text>
+ <span class="caption">
+ <xsl:choose>
+ <xsl:when test="$anchor != ''">
+ <a name="{$anchor}">
+ <xsl:value-of select="$caption"/>
+ </a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$caption"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </span>
+ </div>
+
+ <!-- Box Contents -->
+ <div id="{$id}" class="contents">
+ <xsl:copy-of select="$contents"/>
+ </div>
+ <xsl:if test="normalize-space(translate($useJavaScript,'TRUE','true'))='true'">
+ <xsl:call-template name="PrintJSCode">
+ <xsl:with-param name="code">setState('<xsl:value-of select="$id"/>', <xsl:value-of select="$isOpened"/>);</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </div>
+ </xsl:template>
+
+ <!--
+ Returns the namespace of an attribute
+ declaration or reference.
+ Param(s):
+ attribute (Node) required
+ Attribute declaration or reference
+ -->
+ <xsl:template name="GetAttributeNS">
+ <xsl:param name="attribute"/>
+
+ <xsl:choose>
+ <!-- Qualified local attribute declaration -->
+ <xsl:when test="$attribute[@name] and (normalize-space(translate($attribute/@form, 'QUALIFED', 'qualifed'))='qualified' or normalize-space(translate(/xsd:schema/@attributeFormDefault, 'QUALIFED', 'qualifed'))='qualified')">
+ <xsl:value-of select="/xsd:schema/@targetNamespace"/>
+ </xsl:when>
+ <!-- Reference to global attribute declaration -->
+ <xsl:when test="$attribute[@ref]">
+ <xsl:call-template name="GetRefNS">
+ <xsl:with-param name="ref" select="$attribute/@ref"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Otherwise, attribute has no namespace -->
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the description that can be used in
+ headers for a schema component.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="GetComponentDescription">
+ <xsl:param name="component"/>
+
+ <xsl:choose>
+ <xsl:when test="local-name($component)='all'">
+ <xsl:text>All Model Group</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='attribute'">
+ <xsl:text>Attribute</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='attributeGroup'">
+ <xsl:text>Attribute Group</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='choice'">
+ <xsl:text>Choice Model Group</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='complexType'">
+ <xsl:text>Complex Type</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='element'">
+ <xsl:text>Element</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='group'">
+ <xsl:text>Model Group</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='notation'">
+ <xsl:text>Notation</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='sequence'">
+ <xsl:text>Sequence Model Group</xsl:text>
+ </xsl:when>
+ <xsl:when test="local-name($component)='simpleType'">
+ <xsl:text>Simple Type</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">true</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+Unknown schema component, <xsl:value-of select="local-name($component)"/>.
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the unique identifier for a top-level schema
+ component. Returns the string "schema" if the 'component'
+ is the root schema element.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="GetComponentID">
+ <xsl:param name="component"/>
+
+ <xsl:choose>
+ <xsl:when test="local-name($component)='schema'">
+ <xsl:text>schema</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="componentPrefix">
+ <xsl:call-template name="GetComponentPrefix">
+ <xsl:with-param name="component" select="$component"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat($componentPrefix, $component/@name)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the prefix to add in front of a schema component
+ name when generating anchor names.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="GetComponentPrefix">
+ <xsl:param name="component"/>
+
+ <xsl:choose>
+ <xsl:when test="local-name($component)='attribute'">
+ <xsl:value-of select="$ATTR_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='attributeGroup'">
+ <xsl:value-of select="$ATTR_GRP_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='complexType'">
+ <xsl:value-of select="$CTYPE_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='element'">
+ <xsl:value-of select="$ELEM_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='group'">
+ <xsl:value-of select="$GRP_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='notation'">
+ <xsl:value-of select="$NOTA_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='simpleType'">
+ <xsl:value-of select="$STYPE_PREFIX"/>
+ </xsl:when>
+ <xsl:when test="local-name($component)='key' or local-name($component)='unique'">
+ <xsl:value-of select="$KEY_PREFIX"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">true</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+Unknown schema component, <xsl:value-of select="local-name($component)"/>.
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns a glossary term reference for the
+ schema component type, if applicable.
+ Param(s):
+ component (Node) required
+ Schema component
+ -->
+ <xsl:template name="GetComponentTermRef">
+ <xsl:param name="component"/>
+
+ <xsl:choose>
+ <xsl:when test="local-name($component)='notation'">
+ <xsl:text>Notation</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the namespace prefix of an attribute.
+ Param(s):
+ attribute (Node) required
+ Attribute declaration or reference
+ -->
+ <xsl:template name="GetAttributePrefix">
+ <xsl:param name="attribute"/>
+
+ <xsl:choose>
+ <!-- Element reference -->
+ <xsl:when test="$attribute/@ref">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="$attribute/@ref"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Global element declaration -->
+ <xsl:when test="local-name($attribute/..)='schema'">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:when>
+ <!-- Local element declaration -->
+ <xsl:otherwise>
+ <xsl:if test="($attribute/@form and normalize-space($attribute/@form)='qualified') or (/xsd:schema/@attributeFormDefault and normalize-space(/xsd:schema/@attributeFormDefault)='qualified')">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the namespace prefix of an element.
+ Param(s):
+ element (Node) required
+ Element declaration or reference
+ -->
+ <xsl:template name="GetElementPrefix">
+ <xsl:param name="element"/>
+
+ <xsl:choose>
+ <!-- Element reference -->
+ <xsl:when test="$element/@ref">
+ <xsl:call-template name="GetRefPrefix">
+ <xsl:with-param name="ref" select="$element/@ref"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- Global element declaration -->
+ <xsl:when test="local-name($element/..)='schema'">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:when>
+ <!-- Local element declaration -->
+ <xsl:otherwise>
+ <xsl:if test="($element/@form and normalize-space($element/@form)='qualified') or (/xsd:schema/@elementFormDefault and normalize-space(/xsd:schema/@elementFormDefault)='qualified')">
+ <xsl:call-template name="GetThisPrefix"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the local name of a reference.
+ Param(s):
+ ref (String) required
+ Reference
+ -->
+ <xsl:template name="GetRefName">
+ <xsl:param name="ref"/>
+
+ <xsl:choose>
+ <xsl:when test="contains($ref, ':')">
+ <!-- Ref has namespace prefix -->
+ <xsl:value-of select="substring-after($ref, ':')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Ref has no namespace prefix -->
+ <xsl:value-of select="$ref"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Returns the namespace prefix of a reference.
+ Param(s):
+ ref (String) required
+ Reference
+ -->
+ <xsl:template name="GetRefPrefix">
+ <xsl:param name="ref"/>
+ <!-- Get namespace prefix -->
+ <xsl:value-of select="substring-before($ref, ':')"/>
+ </xsl:template>
+
+ <!--
+ Returns the namespace of a reference.
+ Param(s):
+ ref (String) required
+ Reference
+ -->
+ <xsl:template name="GetRefNS">
+ <xsl:param name="ref"/>
+ <!-- Get namespace prefix -->
+ <xsl:variable name="refPrefix" select="substring-before($ref, ':')"/>
+ <!-- Get namespace -->
+ <xsl:value-of select="/xsd:schema/namespace::*[local-name(.)=normalize-space($refPrefix)]"/>
+ </xsl:template>
+
+ <!--
+ Returns the declared prefix of this schema's target namespace.
+ -->
+ <xsl:template name="GetThisPrefix">
+ <xsl:if test="/xsd:schema/@targetNamespace">
+ <xsl:value-of select="local-name(/xsd:schema/namespace::*[normalize-space(.)=normalize-space(/xsd:schema/@targetNamespace)])"/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Returns the declared prefix of XML Schema namespace.
+ -->
+ <xsl:template name="GetXSDPrefix">
+ <xsl:value-of select="local-name(/xsd:schema/namespace::*[normalize-space(.)=$XSD_NS])"/>
+ </xsl:template>
+
+ <!--
+ Returns the schema documentation file location for a
+ given URI for a schema, done by looking up the file
+ specified in 'linksFile' variable.
+ It'll throw a fatal error if a value for 'linksFile' was
+ provided and the documentation file for 'uri' could not be
+ found.
+ Param(s):
+ uri (String) required
+ Location of schema file
+ -->
+ <xsl:template name="GetSchemaDocLocation">
+ <xsl:param name="uri"/>
+
+ <xsl:if test="$linksFile!=''">
+ <xsl:variable name="schemaDocFile" select="document($linksFile)/ppp:links/ppp:schema[@file-location=$uri]/@docfile-location"/>
+ <xsl:if test="$schemaDocFile=''">
+ <xsl:call-template name="HandleError">
+ <xsl:with-param name="isTerminating">true</xsl:with-param>
+ <xsl:with-param name="errorMsg">
+Documentation file for the schema at, <xsl:value-of select="$uri"/>,
+was not specified in the links file, <xsl:value-of select="$linksFile"/>.
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$schemaDocFile"/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out a boolean value.
+ Param(s):
+ boolean (String) required
+ Boolean value
+ -->
+ <xsl:template name="PrintBoolean">
+ <xsl:param name="boolean"/>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space(translate($boolean,'TRUE', 'true'))='true' or normalize-space($boolean)='1'">
+ <xsl:text>yes</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>no</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out a link to the namespace of a prefix,
+ and tacks on a colon in the end.
+ Param(s):
+ prefix (String) required
+ Namespace prefix
+ nolink (boolean) optional
+ If true, doesn't provide a link to the
+ namespace in the namespaces table.
+ schemaLoc (String) optional
+ Schema file containing this namespace prefix;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintNSPrefix">
+ <xsl:param name="prefix"/>
+ <xsl:param name="nolink">false</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:if test="$prefix!='' and normalize-space(translate($printNSPrefixes,'TRUE','true'))='true'">
+ <xsl:choose>
+ <xsl:when test="$nolink='false'">
+ <xsl:call-template name="PrintNamespaceRef">
+ <xsl:with-param name="prefix" select="$prefix"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$prefix"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>:</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Prints out the min/max occurrences of a schema component.
+ Param(s):
+ component (Node) optional
+ Schema component
+ minOccurs (String) optional
+ Minimum occurrences
+ maxOccurs (String) optional
+ Maximum occurrences
+ -->
+ <xsl:template name="PrintOccurs">
+ <xsl:param name="component"/>
+ <xsl:param name="minOccurs"/>
+ <xsl:param name="maxOccurs"/>
+
+ <!-- Get min occurs -->
+ <xsl:variable name="min">
+ <xsl:choose>
+ <xsl:when test="$component and local-name($component)='attribute'">
+ <xsl:choose>
+ <xsl:when test="normalize-space($component/@use)='required'">
+ <xsl:text>1</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>0</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="$component and $component/@minOccurs">
+ <xsl:value-of select="$component/@minOccurs"/>
+ </xsl:when>
+ <xsl:when test="$minOccurs != ''">
+ <xsl:value-of select="$minOccurs"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>1</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- Get max occurs -->
+ <xsl:variable name="max">
+ <xsl:choose>
+ <xsl:when test="$component and local-name($component)='attribute'">
+ <xsl:choose>
+ <xsl:when test="normalize-space($component/@use)='prohibited'">
+ <xsl:text>0</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>1</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="($component and normalize-space($component/@maxOccurs)='unbounded') or $maxOccurs='unbounded'">
+ <xsl:text>*</xsl:text>
+ </xsl:when>
+ <xsl:when test="$component and $component/@maxOccurs">
+ <xsl:value-of select="$component/@maxOccurs"/>
+ </xsl:when>
+ <xsl:when test="$maxOccurs != ''">
+ <xsl:value-of select="$maxOccurs"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>1</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <span class="occurs">
+ <xsl:choose>
+ <xsl:when test="number($min)=1 and number($max)=1">
+ <xsl:text>[1]</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$min"/>
+ <xsl:text>..</xsl:text>
+ <xsl:value-of select="$max"/>
+ <xsl:text>]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </span>
+ </xsl:template>
+
+ <!--
+ Translates occurrence of '#all' in 'block' value
+ of element declarations.
+ Param(s):
+ EBV (String) required
+ Value
+ -->
+ <xsl:template name="PrintBlockSet">
+ <xsl:param name="EBV"/>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space($EBV)='#all'">
+ <xsl:text>restriction, extension, substitution</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$EBV"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Translates occurrence of '#all' in 'final' value
+ of element declarations, and 'block' and 'final' values
+ in complex type definitions.
+ Param(s):
+ EBV (String) required
+ Value
+ -->
+ <xsl:template name="PrintDerivationSet">
+ <xsl:param name="EBV"/>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space($EBV)='#all'">
+ <xsl:text>restriction, extension</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$EBV"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Translates occurrence of '#all' in 'final' value
+ of simple type definitions.
+ Param(s):
+ EBV (String) required
+ Value
+ -->
+ <xsl:template name="PrintSimpleDerivationSet">
+ <xsl:param name="EBV"/>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space($EBV)='#all'">
+ <xsl:text>restriction, list, union</xsl:text>
+ </xsl:when>
+ <xsl:when test="normalize-space($EBV)!=''">
+ <xsl:value-of select="$EBV"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Print out a URI. If it starts with 'http', a link is provided.
+ Param(s):
+ uri (String) required
+ URI to be printed
+ -->
+ <xsl:template name="PrintURI">
+ <xsl:param name="uri"/>
+
+ <xsl:choose>
+ <xsl:when test="starts-with($uri, 'http')">
+ <a title="{$uri}" href="{$uri}">
+ <xsl:value-of select="$uri"/>
+ </a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$uri"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Print out a link to the documentation of schema document.
+ For this happen, the 'linksFile' variable must be provided,
+ it must point to an actual file, and in that file, there
+ must be a mapping from the schema file location to the
+ schema documentation file location.
+ Param(s):
+ uri (String) required
+ Location of schema file
+ -->
+ <xsl:template name="PrintSchemaLink">
+ <xsl:param name="uri"/>
+
+ <xsl:variable name="docFileLoc">
+ <xsl:call-template name="GetSchemaDocLocation">
+ <xsl:with-param name="uri" select="$uri"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$docFileLoc!=''">
+ <a title="Jump to schema documentation for '{$uri}'." href="{$docFileLoc}">
+ <xsl:value-of select="$uri"/>
+ </a>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$uri"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Tokenises a whitespace-separated list of values, and
+ displays them appropriately.
+ Param(s):
+ value (String) required
+ Whitespace-separated list
+ compType (String) optional
+ Specify schema component type if values in list are
+ schema components, so appropriate links can be provided.
+ isInList (boolean) optional
+ If true, place each value within 'li' tags.
+ Assumes that this template is called within an HTML
+ list element.
+ separator (String) optional
+ Character(s) to use to separate resulting values in list.
+ Only used if 'isInList' is false.
+ If none is provided, uses a space character, ' '.
+ isFirst (boolean) optional
+ If false, it's a recursive call from 'PrintWhitespaceList'.
+ schemaLoc (String) optional
+ Schema file containing this all model group;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintWhitespaceList">
+ <xsl:param name="value"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="isInList">false</xsl:param>
+ <xsl:param name="separator"/>
+ <xsl:param name="isFirst">true</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:variable name="token" select="normalize-space(substring-before($value, ' '))"/>
+ <xsl:choose>
+ <xsl:when test="$token!=''">
+ <!-- Whitespace found in value -->
+ <!-- Print out token -->
+ <xsl:call-template name="PrintWhitespaceListToken">
+ <xsl:with-param name="token" select="$token"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="isInList" select="$isInList"/>
+ <xsl:with-param name="separator" select="$separator"/>
+ <xsl:with-param name="isFirst" select="$isFirst"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ <!-- Continue parsing -->
+ <xsl:variable name="rest" select="normalize-space(substring-after($value, $token))"/>
+ <xsl:if test="$rest!=''">
+ <xsl:call-template name="PrintWhitespaceList">
+ <xsl:with-param name="value" select="$rest"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="isInList" select="$isInList"/>
+ <xsl:with-param name="separator" select="$separator"/>
+ <xsl:with-param name="isFirst">false</xsl:with-param>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- No more whitespaces -->
+ <!-- Print out one last token -->
+ <xsl:if test="normalize-space($value)!=''">
+ <xsl:call-template name="PrintWhitespaceListToken">
+ <xsl:with-param name="token" select="$value"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="isInList" select="$isInList"/>
+ <xsl:with-param name="separator" select="$separator"/>
+ <xsl:with-param name="isFirst" select="$isFirst"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Helper template for 'PrintWhitespaceList' template,
+ which prints out one token in the list.
+ Param(s):
+ token (String) required
+ Token to be printed
+ compType (String) optional
+ Schema component type of token, if applicable.
+ isInList (boolean) optional
+ If true, place token within 'li' tags.
+ separator (String) optional
+ Character(s) use to separate one token from another.
+ Only used if 'isInList' is false.
+ If none is provided, uses a space character, ' '.
+ isFirst (boolean) optional
+ If true, token is the first value in the list.
+ schemaLoc (String) optional
+ Schema file containing this all model group;
+ if in current schema, 'schemaLoc' is set to 'this'
+ -->
+ <xsl:template name="PrintWhitespaceListToken">
+ <xsl:param name="token"/>
+ <xsl:param name="compType"/>
+ <xsl:param name="isInList">false</xsl:param>
+ <xsl:param name="separator"/>
+ <xsl:param name="isFirst">true</xsl:param>
+ <xsl:param name="schemaLoc">this</xsl:param>
+
+ <xsl:variable name="displayValue">
+ <xsl:choose>
+ <xsl:when test="$compType!=''">
+ <xsl:call-template name="PrintCompRef">
+ <xsl:with-param name="ref" select="normalize-space($token)"/>
+ <xsl:with-param name="compType" select="$compType"/>
+ <xsl:with-param name="schemaLoc" select="$schemaLoc"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="normalize-space($token)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$isInList!='false'">
+ <li>
+ <xsl:copy-of select="$displayValue"/>
+ </li>
+ </xsl:when>
+ <xsl:when test="$isFirst!='true'">
+ <xsl:choose>
+ <xsl:when test="$separator!=''">
+ <xsl:value-of select="$separator"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:copy-of select="$displayValue"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$displayValue"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Print out a wildcard.
+ Param(s):
+ componentType (attribute|element) required
+ XML Schema component type
+ namespaces (String) required
+ Namespace attribute of wildcard
+ processContents (String) required
+ Process contents attribute of wildcard
+ namespaces (String) required
+ Namespace attribute of wildcard
+ -->
+ <xsl:template name="PrintWildcard">
+ <xsl:param name="componentType">element</xsl:param>
+ <xsl:param name="namespace"/>
+ <xsl:param name="processContents"/>
+ <xsl:param name="minOccurs"/>
+ <xsl:param name="maxOccurs"/>
+
+ <xsl:text>Allow any </xsl:text>
+ <xsl:value-of select="$componentType"/>
+ <xsl:text>s from </xsl:text>
+
+ <xsl:choose>
+ <!-- ##any -->
+ <xsl:when test="not($namespace) or normalize-space($namespace)='##any'">
+ <xsl:text>any namespace</xsl:text>
+ </xsl:when>
+ <!-- ##other -->
+ <xsl:when test="normalize-space($namespace)='##other'">
+ <xsl:text>a namespace other than this schema's namespace</xsl:text>
+ </xsl:when>
+ <!-- ##targetNamespace, ##local, specific namespaces -->
+ <xsl:otherwise>
+ <!-- ##targetNamespace -->
+ <xsl:variable name="hasTargetNS">
+ <xsl:if test="contains($namespace, '##targetNamespace')">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <!-- ##local -->
+ <xsl:variable name="hasLocalNS">
+ <xsl:if test="contains($namespace, '##local')">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <!-- Specific namespaces -->
+ <!-- Remove '##targetNamespace' string if any-->
+ <xsl:variable name="temp">
+ <xsl:choose>
+ <xsl:when test="$hasTargetNS='true'">
+ <xsl:value-of select="concat(substring-before($namespace, '##targetNamespace'), substring-after($namespace, '##targetNamespace'))"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$namespace"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- Remove '##local' string if any -->
+ <xsl:variable name="specificNS">
+ <xsl:choose>
+ <xsl:when test="$hasLocalNS='true'">
+ <xsl:value-of select="concat(substring-before($temp, '##local'), substring-after($temp, '##local'))"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$temp"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="hasSpecificNS">
+ <xsl:if test="normalize-space($specificNS)!=''">
+ <xsl:text>true</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:if test="$hasLocalNS='true'">
+ <xsl:text>no namespace</xsl:text>
+ </xsl:if>
+
+ <xsl:if test="$hasTargetNS='true'">
+ <xsl:choose>
+ <xsl:when test="$hasLocalNS='true' and $hasSpecificNS!='true'">
+ <xsl:text> and </xsl:text>
+ </xsl:when>
+ <xsl:when test="$hasLocalNS='true'">
+ <xsl:text>, </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:text>this schema's namespace</xsl:text>
+ </xsl:if>
+
+ <xsl:if test="$hasSpecificNS='true'">
+ <xsl:choose>
+ <xsl:when test="$hasTargetNS='true' and $hasLocalNS='true'">
+ <xsl:text>, and </xsl:text>
+ </xsl:when>
+ <xsl:when test="$hasTargetNS='true' or $hasLocalNS='true'">
+ <xsl:text> and </xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:text>the following namespace(s): </xsl:text>
+ <xsl:call-template name="PrintWhitespaceList">
+ <xsl:with-param name="value" select="normalize-space($specificNS)"/>
+ <xsl:with-param name="separator">,</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Process contents -->
+ <xsl:text> (</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$processContents">
+ <xsl:value-of select="normalize-space($processContents)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>strict</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> validation)</xsl:text>
+ <xsl:text>.</xsl:text>
+
+ <!-- Print min/max occurs -->
+ <xsl:if test="$componentType='element'">
+ <xsl:text> </xsl:text>
+ <xsl:call-template name="PrintOccurs">
+ <xsl:with-param name="minOccurs" select="$minOccurs"/>
+ <xsl:with-param name="maxOccurs" select="$maxOccurs"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Print out the pattern property for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintPatternFacet">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:if test="$simpleRestrict/xsd:pattern">
+ <em>pattern</em>
+ <xsl:text> = </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:pattern/@value"/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Print out the total digits property for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintTotalDigitsFacet">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:if test="$simpleRestrict/xsd:totalDigits">
+ <em>total no. of digits</em>
+ <xsl:text> = </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:totalDigits/@value"/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Print out the fraction digits property for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintFractionDigitsFacet">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:if test="$simpleRestrict/xsd:fractionDigits">
+ <em>
+ <xsl:text>no. of fraction digits</xsl:text>
+ </em>
+ <xsl:text> = </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:fractionDigits/@value"/>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Print out the enumeration list for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintEnumFacets">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:if test="$simpleRestrict/xsd:enumeration">
+ <em>value</em>
+ <xsl:text> comes from list: {</xsl:text>
+
+ <xsl:for-each select="$simpleRestrict/xsd:enumeration">
+ <xsl:if test="position()!=1">
+ <xsl:text>|</xsl:text>
+ </xsl:if>
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="@value"/>
+ <xsl:text>'</xsl:text>
+ </xsl:for-each>
+
+ <xsl:text>}</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <!--
+ Print out the length property for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintLengthFacets">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:choose>
+ <xsl:when test="$simpleRestrict/xsd:length">
+ <em>
+ <xsl:text>length</xsl:text>
+ </em>
+ <xsl:text> = </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:length/@value"/>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:minLength">
+ <em>
+ <xsl:text>length</xsl:text>
+ </em>
+ <xsl:text> >= </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:minLength/@value"/>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:maxLength">
+ <em>
+ <xsl:text>length</xsl:text>
+ </em>
+ <xsl:text> &lt;= </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:maxLength/@value"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Print out the whitespace property for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintWhitespaceFacet">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:variable name="facetValue" select="normalize-space(translate($simpleRestrict/xsd:whiteSpace/@value, 'ACELOPRSV', 'aceloprsv'))"/>
+
+ <xsl:choose>
+ <xsl:when test="$facetValue='preserve'">
+ <em>Whitespace policy: </em>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">PreserveWS</xsl:with-param>
+ <xsl:with-param name="term">preserve</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$facetValue='replace'">
+ <em>Whitespace policy: </em>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">ReplaceWS</xsl:with-param>
+ <xsl:with-param name="term">replace</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$facetValue='collapse'">
+ <em>Whitespace policy: </em>
+ <xsl:call-template name="PrintGlossaryTermRef">
+ <xsl:with-param name="code">CollapseWS</xsl:with-param>
+ <xsl:with-param name="term">collapse</xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Print out the value ranges for derivations by
+ restriction on simple content.
+ Param(s):
+ simpleRestrict (Node) required
+ 'restriction' element
+ -->
+ <xsl:template name="PrintRangeFacets">
+ <xsl:param name="simpleRestrict"/>
+
+ <xsl:choose>
+ <xsl:when test="($simpleRestrict/xsd:minInclusive or $simpleRestrict/xsd:minExclusive) and ($simpleRestrict/xsd:maxInclusive or $simpleRestrict/xsd:maxExclusive)">
+ <xsl:choose>
+ <xsl:when test="$simpleRestrict/xsd:minInclusive">
+ <xsl:value-of select="$simpleRestrict/xsd:minInclusive/@value"/>
+ <xsl:text> &lt;= </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$simpleRestrict/xsd:minExclusive/@value"/>
+ <xsl:text> &lt; </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <em>
+ <xsl:text>value</xsl:text>
+ </em>
+ <xsl:choose>
+ <xsl:when test="$simpleRestrict/xsd:maxInclusive">
+ <xsl:text> &lt;= </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:maxInclusive/@value"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> &lt; </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:maxExclusive/@value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:minInclusive">
+ <em>
+ <xsl:text>value</xsl:text>
+ </em>
+ <xsl:text> >= </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:minInclusive/@value"/>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:minExclusive">
+ <em>
+ <xsl:text>value</xsl:text>
+ </em>
+ <xsl:text> > </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:minExclusive/@value"/>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:maxInclusive">
+ <em>
+ <xsl:text>value</xsl:text>
+ </em>
+ <xsl:text> &lt;= </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:maxInclusive/@value"/>
+ </xsl:when>
+ <xsl:when test="$simpleRestrict/xsd:maxExclusive">
+ <em>
+ <xsl:text>value</xsl:text>
+ </em>
+ <xsl:text> &lt; </xsl:text>
+ <xsl:value-of select="$simpleRestrict/xsd:maxExclusive/@value"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--
+ Prints out JavaScript code.
+ NOTE: Javascript code is placed within comments to make it
+ work with current browsers. In strict XHTML, JavaScript code
+ should be placed within CDATA sections. However, most
+ browsers generate a syntax error if the page contains
+ CDATA sections. Placing Javascript code within comments
+ means that the code cannot contain two dashes.
+ Param(s):
+ code (Result Tree Fragment) required
+ Javascript code
+ -->
+ <xsl:template name="PrintJSCode">
+ <xsl:param name="code"/>
+
+ <script type="text/javascript">
+ <!-- If browsers start supporting CDATA sections,
+ uncomment the following piece of code. -->
+ <!-- <xsl:text disable-output-escaping="yes">
+&lt;![CDATA[
+</xsl:text> -->
+ <!-- If browsers start supporting CDATA sections,
+ remove the following piece of code. -->
+ <xsl:text disable-output-escaping="yes">
+&lt;!--
+</xsl:text>
+
+ <xsl:value-of select="$code" disable-output-escaping="yes"/>
+ <!-- If browsers start supporting CDATA sections,
+ remove the following piece of code. -->
+ <xsl:text disable-output-escaping="yes">
+// --&gt;
+</xsl:text>
+ <!-- If browsers start supporting CDATA sections,
+ uncomment the following piece of code. -->
+ <!-- <xsl:text disable-output-escaping="yes">
+]]&gt;
+</xsl:text> -->
+ </script>
+ </xsl:template>
+
+ <!--
+ Translates occurrences of a string
+ in a piece of text with another string.
+ Param(s):
+ value (String) required
+ Text to translate
+ strToReplace (String) required
+ String to be replaced
+ replacementStr (String) required
+ Replacement text
+ -->
+ <xsl:template name="TranslateStr">
+ <xsl:param name="value"/>
+ <xsl:param name="strToReplace"/>
+ <xsl:param name="replacementStr"/>
+
+ <xsl:if test="$value != ''">
+ <xsl:variable name="beforeText" select="substring-before($value, $strToReplace)"/>
+ <xsl:choose>
+ <xsl:when test="$beforeText != ''">
+ <xsl:value-of select="$beforeText"/>
+ <xsl:value-of select="$replacementStr"/>
+ <xsl:call-template name="TranslateStr">
+ <xsl:with-param name="value" select="substring-after($value, $strToReplace)"/>
+ <xsl:with-param name="strToReplace" select="$strToReplace"/>
+ <xsl:with-param name="replacementStr" select="$replacementStr"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="HandleError">
+ <xsl:param name="errorMsg"/>
+ <xsl:param name="isTerminating">false</xsl:param>
+
+ <xsl:choose>
+ <xsl:when test="$isTerminating='true'">
+ <xsl:message terminate="yes">
+ <xsl:text>XS3P ERROR: </xsl:text>
+ <xsl:value-of select="$errorMsg"/>
+ </xsl:message>
+ </xsl:when>
+ <xsl:otherwise>
+ <span style="font-weight: bold; color: red">
+ <xsl:text>ERROR: </xsl:text>
+ <xsl:value-of select="$errorMsg"/>
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet> \ No newline at end of file
diff --git a/setup.py b/setup.py
index 62da3b787..52ec93d10 100644
--- a/setup.py
+++ b/setup.py
@@ -115,7 +115,7 @@ except ImportError:
setup(cmdclass=cmdclass,
name="Bcfg2",
- version="1.2.0pre1",
+ version="1.2.0pre2",
description="Bcfg2 Server",
author="Narayan Desai",
author_email="desai@mcs.anl.gov",
@@ -126,6 +126,7 @@ setup(cmdclass=cmdclass,
"Bcfg2.Server.Admin",
"Bcfg2.Server.Hostbase",
"Bcfg2.Server.Hostbase.hostbase",
+ "Bcfg2.Server.Lint",
"Bcfg2.Server.Plugins",
"Bcfg2.Server.Reports",
"Bcfg2.Server.Reports.reports",
diff --git a/solaris/Makefile b/solaris/Makefile
index bd3d097d4..65ab6309e 100644
--- a/solaris/Makefile
+++ b/solaris/Makefile
@@ -1,7 +1,7 @@
#!/usr/sfw/bin/gmake
PYTHON="/opt/csw/bin/python"
-VERS=1.2.0pre1-1
+VERS=1.2.0pre2-1
PYVERSION := $(shell $(PYTHON) -c "import sys; print sys.version[0:3]")
default: clean package
diff --git a/solaris/pkginfo.bcfg2 b/solaris/pkginfo.bcfg2
index 8eeb29f62..cd8215741 100644
--- a/solaris/pkginfo.bcfg2
+++ b/solaris/pkginfo.bcfg2
@@ -1,7 +1,7 @@
PKG="SCbcfg2"
NAME="bcfg2"
ARCH="sparc"
-VERSION="1.2.0pre1"
+VERSION="1.2.0pre2"
CATEGORY="application"
VENDOR="Argonne National Labratory"
EMAIL="bcfg-dev@mcs.anl.gov"
diff --git a/solaris/pkginfo.bcfg2-server b/solaris/pkginfo.bcfg2-server
index 031d2a385..8bc069d08 100644
--- a/solaris/pkginfo.bcfg2-server
+++ b/solaris/pkginfo.bcfg2-server
@@ -1,7 +1,7 @@
PKG="SCbcfg2-server"
NAME="bcfg2-server"
ARCH="sparc"
-VERSION="1.2.0pre1"
+VERSION="1.2.0pre2"
CATEGORY="application"
VENDOR="Argonne National Labratory"
EMAIL="bcfg-dev@mcs.anl.gov"
diff --git a/src/lib/Bcfg2Py3Incompat.py b/src/lib/Bcfg2Py3Incompat.py
new file mode 100644
index 000000000..6b66e72b0
--- /dev/null
+++ b/src/lib/Bcfg2Py3Incompat.py
@@ -0,0 +1,2 @@
+def fprint(s, f):
+ print(s, file=f)
diff --git a/src/lib/Bcfg2Py3k.py b/src/lib/Bcfg2Py3k.py
new file mode 100644
index 000000000..c9e48a49b
--- /dev/null
+++ b/src/lib/Bcfg2Py3k.py
@@ -0,0 +1,81 @@
+import sys
+
+try:
+ from email.Utils import formatdate
+except ImportError:
+ from email.utils import formatdate
+
+# urllib imports
+try:
+ from urlparse import urljoin, urlparse
+ from urllib2 import HTTPBasicAuthHandler
+ from urllib2 import HTTPPasswordMgrWithDefaultRealm
+ from urllib2 import build_opener
+ from urllib2 import install_opener
+ from urllib import urlopen
+ from urllib2 import HTTPError
+except ImportError:
+ from urllib.parse import urljoin, urlparse
+ from urllib.request import HTTPBasicAuthHandler
+ from urllib.request import HTTPPasswordMgrWithDefaultRealm
+ from urllib.request import build_opener
+ from urllib.request import install_opener
+ from urllib.request import urlopen
+ from urllib.error import HTTPError
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+try:
+ import ConfigParser
+except ImportError:
+ import configparser as ConfigParser
+
+try:
+ import cPickle
+except ImportError:
+ import pickle as cPickle
+
+try:
+ from Queue import Queue, Empty, Full
+except ImportError:
+ from queue import Queue, Empty, Full
+
+# xmlrpc imports
+try:
+ import xmlrpclib, SimpleXMLRPCServer
+except ImportError:
+ import xmlrpc.client as xmlrpclib
+ import xmlrpc.server as SimpleXMLRPCServer
+
+# socketserver import
+try:
+ import SocketServer
+except ImportError:
+ import socketserver as SocketServer
+
+# httplib imports
+try:
+ import httplib
+except ImportError:
+ import http.client as httplib
+
+# print to file compatibility
+def u_str(string):
+ if sys.hexversion >= 0x03000000:
+ return string
+ else:
+ return unicode(string)
+
+"""
+In order to use the new syntax for printing to a file, we need to do
+a conditional import because there is a syntax incompatibility between
+the two versions of python.
+"""
+if sys.hexversion >= 0x03000000:
+ from Bcfg2.Bcfg2Py3Incompat import fprint
+else:
+ def fprint(s, f):
+ print >> f, s
diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py
index 545d4b584..60d158eb1 100644
--- a/src/lib/Client/Frame.py
+++ b/src/lib/Client/Frame.py
@@ -8,6 +8,7 @@ import logging
import time
import Bcfg2.Client.Tools
+
def cmpent(ent1, ent2):
"""Sort entries."""
if ent1.tag != ent2.tag:
@@ -15,6 +16,7 @@ def cmpent(ent1, ent2):
else:
return cmp(ent1.get('name'), ent2.get('name'))
+
def promptFilter(prompt, entries):
"""Filter a supplied list based on user input."""
ret = []
@@ -25,7 +27,12 @@ def promptFilter(prompt, entries):
else:
iprompt = prompt % (entry.tag, entry.get('name'))
try:
- if raw_input(iprompt) in ['y', 'Y']:
+ # py3k compatibility
+ try:
+ ans = raw_input(iprompt)
+ except NameError:
+ ans = input(iprompt)
+ if ans in ['y', 'Y']:
ret.append(entry)
except EOFError:
# python 2.4.3 on CentOS doesn't like ^C for some reason
@@ -35,6 +42,7 @@ def promptFilter(prompt, entries):
continue
return ret
+
def matches_entry(entryspec, entry):
# both are (tag, name)
if entryspec == entry:
@@ -52,11 +60,16 @@ def matches_entry(entryspec, entry):
return False
return True
+
def matches_white_list(entry, whitelist):
- return True in [matches_entry(we, (entry.tag, entry.get('name'))) for we in whitelist]
+ return True in [matches_entry(we, (entry.tag, entry.get('name')))
+ for we in whitelist]
+
def passes_black_list(entry, blacklist):
- return True not in [matches_entry(be, (entry.tag, entry.get('name'))) for be in blacklist]
+ return True not in [matches_entry(be, (entry.tag, entry.get('name')))
+ for be in blacklist]
+
class Frame:
"""Frame is the container for all Tool objects and state information."""
@@ -134,8 +147,10 @@ class Frame:
self.logger.error(["%s:%s:%s" % (entry.tag, entry.get('type'), \
entry.get('name')) for entry in problems])
self.logger.error("")
- entries = [(entry.tag, entry.get('name')) for struct in config for entry in struct]
- pkgs = [(entry.get('name'), entry.get('origin')) for struct in config for entry in struct if entry.tag == 'Package']
+ entries = [(entry.tag, entry.get('name'))
+ for struct in config for entry in struct]
+ pkgs = [(entry.get('name'), entry.get('origin'))
+ for struct in config for entry in struct if entry.tag == 'Package']
multi = []
for entry in entries[:]:
if entries.count(entry) > 1:
@@ -151,7 +166,6 @@ class Frame:
self.logger.debug("The following packages are prereqs added by Packages:")
self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 'Packages'])
-
def __getattr__(self, name):
if name in ['extra', 'handled', 'modified', '__important__']:
ret = []
@@ -186,10 +200,10 @@ class Frame:
if self.setup['remove']:
if self.setup['remove'] == 'all':
self.removal = self.extra
- elif self.setup['remove'] == 'services':
+ elif self.setup['remove'] in ['services', 'Services']:
self.removal = [entry for entry in self.extra \
if entry.tag == 'Service']
- elif self.setup['remove'] == 'packages':
+ elif self.setup['remove'] in ['packages', 'Packages']:
self.removal = [entry for entry in self.extra \
if entry.tag == 'Package']
@@ -268,7 +282,8 @@ class Frame:
if b_to_remv:
self.logger.info("Not installing entries from Bundle %s" % \
(bundle.get('name')))
- self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in b_to_remv])
+ self.logger.info(["%s:%s" % (e.tag, e.get('name'))
+ for e in b_to_remv])
[self.whitelist.remove(ent) for ent in b_to_remv]
if self.setup['interactive']:
diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Client/Tools/APT.py
index fe1ef6fdd..a838f5e27 100644
--- a/src/lib/Client/Tools/APT.py
+++ b/src/lib/Client/Tools/APT.py
@@ -69,7 +69,11 @@ class APT(Bcfg2.Client.Tools.Tool):
if self.setup['kevlar'] and not self.setup['dryrun']:
self.cmd.run("%s --force-confold --configure --pending" % DPKG)
self.cmd.run("%s clean" % APTGET)
- self.pkg_cache = apt.cache.Cache()
+ try:
+ self.pkg_cache = apt.cache.Cache()
+ except SystemError, e:
+ self.logger.info("Failed to initialize APT cache: %s" % e)
+ raise Bcfg2.Client.Tools.toolInstantiationError
self.pkg_cache.update()
self.pkg_cache = apt.cache.Cache()
diff --git a/src/lib/Client/Tools/Action.py b/src/lib/Client/Tools/Action.py
index 452788f94..bc57a0e27 100644
--- a/src/lib/Client/Tools/Action.py
+++ b/src/lib/Client/Tools/Action.py
@@ -31,7 +31,12 @@ class Action(Bcfg2.Client.Tools.Tool):
if self.setup['interactive']:
prompt = ('Run Action %s, %s: (y/N): ' %
(entry.get('name'), entry.get('command')))
- if raw_input(prompt) not in ['y', 'Y']:
+ # py3k compatibility
+ try:
+ ans = raw_input(prompt)
+ except NameError:
+ ans = input(prompt)
+ if ans not in ['y', 'Y']:
return False
if self.setup['servicemode'] == 'build':
if entry.get('build', 'true') == 'false':
diff --git a/src/lib/Client/Tools/Chkconfig.py b/src/lib/Client/Tools/Chkconfig.py
index bf2a2c1e1..0c78b0fb5 100644
--- a/src/lib/Client/Tools/Chkconfig.py
+++ b/src/lib/Client/Tools/Chkconfig.py
@@ -76,6 +76,11 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
def InstallService(self, entry):
"""Install Service entry."""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
rcmd = "/sbin/chkconfig %s %s"
self.cmd.run("/sbin/chkconfig --add %s"%(entry.attrib['name']))
self.logger.info("Installing Service %s" % (entry.get('name')))
diff --git a/src/lib/Client/Tools/DebInit.py b/src/lib/Client/Tools/DebInit.py
index 119036b32..d6ce16c52 100644
--- a/src/lib/Client/Tools/DebInit.py
+++ b/src/lib/Client/Tools/DebInit.py
@@ -21,6 +21,10 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
# implement entry (Verify|Install) ops
def VerifyService(self, entry, _):
"""Verify Service status for entry."""
+
+ if entry.get('status') == 'ignore':
+ return True
+
rawfiles = glob.glob("/etc/rc*.d/[SK]*%s" % (entry.get('name')))
files = []
@@ -71,6 +75,11 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
def InstallService(self, entry):
"""Install Service for entry."""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
self.logger.info("Installing Service %s" % (entry.get('name')))
try:
os.stat('/etc/init.d/%s' % entry.get('name'))
diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Client/Tools/POSIX.py
index c883fc17a..af3d1a473 100644
--- a/src/lib/Client/Tools/POSIX.py
+++ b/src/lib/Client/Tools/POSIX.py
@@ -137,14 +137,14 @@ class POSIX(Bcfg2.Client.Tools.Tool):
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-lint.' % (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')))
+ 'Try running bcfg2-lint.' % (entry.get('name')))
return False
try:
# check for file existence
@@ -167,7 +167,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
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')))
+ 'Try running bcfg2-lint.' % (entry.get('name')))
return False
major = int(entry.get('major'))
minor = int(entry.get('minor'))
@@ -218,7 +218,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
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')))
+ 'Try running bcfg2-lint.' % (entry.get('name')))
return False
major = int(entry.get('major'))
minor = int(entry.get('minor'))
@@ -240,7 +240,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
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-lint.' % (entry.get('name')))
return False
while len(entry.get('perms', '')) < 4:
entry.set('perms', '0' + entry.get('perms', ''))
@@ -348,7 +348,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
entry.get('owner') == None or \
entry.get('group') == None:
self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-repo-validate.' % \
+ 'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
self.logger.info("Installing directory %s" % (entry.get('name')))
@@ -542,7 +542,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
return False
# If we get here, then the parent directory should exist
- if (entry.get("paranoid", False) == 'true') and \
+ if (entry.get("paranoid", False) in ['true', 'True']) and \
self.setup.get("paranoid", False) and not \
(entry.get('current_exists', 'true') == 'false'):
bkupnam = entry.get('name').replace('/', '_')
@@ -550,7 +550,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
bkuplist = [f for f in os.listdir(self.ppath) if
f.startswith(bkupnam)]
bkuplist.sort()
- if len(bkuplist) == int(self.max_copies):
+ while len(bkuplist) >= int(self.max_copies):
# remove the oldest backup available
oldest = bkuplist.pop(0)
self.logger.info("Removing %s" % oldest)
@@ -563,7 +563,8 @@ class POSIX(Bcfg2.Client.Tools.Tool):
try:
# backup existing file
shutil.copy(entry.get('name'),
- "%s/%s_%s" % (self.ppath, bkupnam, datetime.now()))
+ "%s/%s_%s" % (self.ppath, bkupnam,
+ datetime.isoformat(datetime.now())))
self.logger.info("Backup of %s saved to %s" %
(entry.get('name'), self.ppath))
except IOError, e:
@@ -613,7 +614,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
"""Verify HardLink entry."""
if entry.get('to') == None:
self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-repo-validate.' % \
+ 'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
try:
@@ -636,7 +637,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
"""Install HardLink entry."""
if entry.get('to') == None:
self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-repo-validate.' % \
+ 'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
self.logger.info("Installing Hardlink %s" % (entry.get('name')))
@@ -712,7 +713,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
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-lint.' % (entry.get('name')))
return False
try:
os.chown(entry.get('name'), normUid(entry), normGid(entry))
@@ -727,7 +728,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
"""Verify Path type='symlink' entry."""
if entry.get('to') == None:
self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-repo-validate.' % \
+ 'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
try:
@@ -750,7 +751,7 @@ class POSIX(Bcfg2.Client.Tools.Tool):
"""Install Path type='symlink' entry."""
if entry.get('to') == None:
self.logger.error('Entry %s not completely specified. '
- 'Try running bcfg2-repo-validate.' % \
+ 'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
self.logger.info("Installing symlink %s" % (entry.get('name')))
diff --git a/src/lib/Client/Tools/Pacman.py b/src/lib/Client/Tools/Pacman.py
index be3fb0c94..082897934 100644
--- a/src/lib/Client/Tools/Pacman.py
+++ b/src/lib/Client/Tools/Pacman.py
@@ -73,7 +73,7 @@ class Pacman(Bcfg2.Client.Tools.PkgTool):
for pkg in packages:
pkgline += " " + pkg.get('name')
- print "packages : " + pkgline
+ self.logger.info("packages : " + pkgline)
try:
self.logger.debug("Running : %s -S %s" % (self.pkgtool, pkgline))
diff --git a/src/lib/Client/Tools/RcUpdate.py b/src/lib/Client/Tools/RcUpdate.py
index 159172b78..d832d98a8 100644
--- a/src/lib/Client/Tools/RcUpdate.py
+++ b/src/lib/Client/Tools/RcUpdate.py
@@ -57,6 +57,11 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
In supervised mode we also take care it's (not) running.
"""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
self.logger.info('Installing Service %s' % entry.get('name'))
if entry.get('status') == 'on':
# make sure it's running if in supervised mode
diff --git a/src/lib/Client/Tools/SMF.py b/src/lib/Client/Tools/SMF.py
index 96c7d9d28..944408326 100644
--- a/src/lib/Client/Tools/SMF.py
+++ b/src/lib/Client/Tools/SMF.py
@@ -74,6 +74,11 @@ class SMF(Bcfg2.Client.Tools.SvcTool):
def InstallService(self, entry):
"""Install SMF Service entry."""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
self.logger.info("Installing Service %s" % (entry.get('name')))
if entry.get('status') == 'off':
if entry.get("FMRI").startswith('lrc'):
diff --git a/src/lib/Client/Tools/Systemd.py b/src/lib/Client/Tools/Systemd.py
new file mode 100644
index 000000000..e3f6a4169
--- /dev/null
+++ b/src/lib/Client/Tools/Systemd.py
@@ -0,0 +1,59 @@
+# This is the bcfg2 support for systemd
+
+"""This is systemd support."""
+
+import Bcfg2.Client.Tools
+import Bcfg2.Client.XML
+
+class Systemd(Bcfg2.Client.Tools.SvcTool):
+ """Systemd support for Bcfg2."""
+ name = 'Systemd'
+ __execs__ = ['/bin/systemctl']
+ __handles__ = [('Service', 'systemd')]
+ __req__ = {'Service': ['name', 'status']}
+
+ def get_svc_command(self, service, action):
+ return "/bin/systemctl %s %s.service" % (action, service.get('name'))
+
+ def VerifyService(self, entry, _):
+ """Verify Service status for entry."""
+ cmd = "/bin/systemctl status %s.service " % (entry.get('name'))
+ raw = ''.join(self.cmd.run(cmd)[1])
+
+ if raw.find('Loaded: error') >= 0:
+ entry.set('current_status', 'off')
+ status = False
+
+ elif raw.find('Active: active') >= 0:
+ entry.set('current_status', 'on')
+ if entry.get('status') == 'off':
+ status = False
+ else:
+ status = True
+
+ else:
+ entry.set('current_status', 'off')
+ if entry.get('status') == 'on':
+ status = False
+ else:
+ status = True
+
+ return status
+
+ def InstallService(self, entry):
+ """Install Service entry."""
+ # don't take any actions for mode = 'manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return True
+
+ if entry.get('status') == 'on':
+ pstatus = self.cmd.run(self.get_svc_command(entry, 'enable'))[0]
+ pstatus = self.cmd.run(self.get_svc_command(entry, 'start'))[0]
+
+ else:
+ pstatus = self.cmd.run(self.get_svc_command(entry, 'stop'))[0]
+ pstatus = self.cmd.run(self.get_svc_command(entry, 'disable'))[0]
+
+ return not pstatus
diff --git a/src/lib/Client/Tools/Upstart.py b/src/lib/Client/Tools/Upstart.py
index b75b0927e..41a585c23 100644
--- a/src/lib/Client/Tools/Upstart.py
+++ b/src/lib/Client/Tools/Upstart.py
@@ -29,6 +29,10 @@ class Upstart(Bcfg2.Client.Tools.SvcTool):
/etc/init/servicename.conf. All we need to do is make sure
the service is running when it should be.
"""
+
+ if entry.get('status') == 'ignore':
+ return True
+
if entry.get('parameters'):
params = entry.get('parameters')
else:
@@ -66,6 +70,11 @@ class Upstart(Bcfg2.Client.Tools.SvcTool):
def InstallService(self, entry):
"""Install Service for entry."""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
if entry.get('status') == 'on':
pstatus = self.cmd.run(self.get_svc_command(entry, 'start'))[0]
elif entry.get('status') == 'off':
diff --git a/src/lib/Client/Tools/VCS.py b/src/lib/Client/Tools/VCS.py
new file mode 100644
index 000000000..fa7748574
--- /dev/null
+++ b/src/lib/Client/Tools/VCS.py
@@ -0,0 +1,137 @@
+"""VCS support."""
+
+# TODO:
+# * git_write_index
+# * add svn support
+# * integrate properly with reports
+missing = []
+
+import os
+import sys
+# python-dulwich git imports
+try:
+ import dulwich
+ import dulwich.index
+ from dulwich.errors import NotGitRepository
+except:
+ missing.append('git')
+# subversion import
+try:
+ import pysvn
+except:
+ missing.append('svn')
+
+import Bcfg2.Client.Tools
+
+
+class VCS(Bcfg2.Client.Tools.Tool):
+ """VCS support."""
+ name = 'VCS'
+ __handles__ = [('Path', 'vcs')]
+ __req__ = {'Path': ['name',
+ 'type',
+ 'vcstype',
+ 'sourceurl',
+ 'revision']}
+
+ def __init__(self, logger, cfg, setup):
+ Bcfg2.Client.Tools.Tool.__init__(self, logger, cfg, setup)
+ self.cfg = cfg
+
+ def git_write_index(self, entry):
+ """Write the git index"""
+ pass
+
+ def Verifygit(self, entry, _):
+ """Verify git repositories"""
+ try:
+ repo = dulwich.repo.Repo(entry.get('name'))
+ except NotGitRepository:
+ self.logger.info("Repository %s does not exist" %
+ entry.get('name'))
+ return False
+ cur_rev = repo.head()
+
+ if cur_rev != entry.get('revision'):
+ self.logger.info("At revision %s need to go to revision %s" %
+ (cur_rev, entry.get('revision')))
+ return False
+
+ return True
+
+ def Installgit(self, entry):
+ """Checkout contents from a git repository"""
+ destname = entry.get('name')
+ destr = dulwich.repo.Repo.init(destname, mkdir=True)
+ cl, host_path = dulwich.client.get_transport_and_path(entry.get('sourceurl'))
+ remote_refs = cl.fetch(host_path,
+ destr,
+ determine_wants=destr.object_store.determine_wants_all,
+ progress=sys.stdout.write)
+ destr.refs['refs/heads/master'] = entry.get('revision')
+ dtree = destr[entry.get('revision')].tree
+ obj_store = destr.object_store
+ for fname, mode, sha in obj_store.iter_tree_contents(dtree):
+ fullpath = os.path.join(destname, fname)
+ try:
+ f = open(os.path.join(destname, fname), 'wb')
+ except IOError:
+ dir = os.path.split(fullpath)[0]
+ os.makedirs(dir)
+ f = open(os.path.join(destname, fname), 'wb')
+ f.write(destr[sha].data)
+ f.close()
+ os.chmod(os.path.join(destname, fname), mode)
+ return True
+ # FIXME: figure out how to write the git index properly
+ #iname = "%s/.git/index" % entry.get('name')
+ #f = open(iname, 'w+')
+ #entries = obj_store[sha].iteritems()
+ #try:
+ # dulwich.index.write_index(f, entries)
+ #finally:
+ # f.close()
+
+ def Verifysvn(self, entry, _):
+ """Verify svn repositories"""
+ client = pysvn.Client()
+ try:
+ cur_rev = str(client.info(entry.get('name')).revision.number)
+ except:
+ self.logger.info("Repository %s does not exist" % entry.get('name'))
+ return False
+
+ if cur_rev != entry.get('revision'):
+ self.logger.info("At revision %s need to go to revision %s" %
+ (cur_rev, entry.get('revision')))
+ return False
+
+ return True
+
+ def Installsvn(self, entry):
+ """Checkout contents from a svn repository"""
+ try:
+ client = pysvn.Client.update(entry.get('name'), recurse=True)
+ except:
+ self.logger.error("Failed to update repository", exc_info=1)
+ return False
+
+ return True
+
+ def VerifyPath(self, entry, _):
+ vcs = entry.get('vcstype')
+ if vcs in missing:
+ self.logger.error("Missing %s python libraries. Cannot verify" %
+ vcs)
+ return False
+ ret = getattr(self, 'Verify%s' % vcs)
+ return ret(entry, _)
+
+ def InstallPath(self, entry):
+ vcs = entry.get('vcstype')
+ if vcs in missing:
+ self.logger.error("Missing %s python libraries. "
+ "Unable to install" % vcs)
+ return False
+ ret = getattr(self, 'Install%s' % vcs)
+ return ret(entry)
diff --git a/src/lib/Client/Tools/YUM24.py b/src/lib/Client/Tools/YUM24.py
index efe92a059..04d9f5c07 100644
--- a/src/lib/Client/Tools/YUM24.py
+++ b/src/lib/Client/Tools/YUM24.py
@@ -30,6 +30,7 @@ except:
if not hasattr(Bcfg2.Client.Tools.RPMng, 'RPMng'):
raise ImportError
+
def build_yname(pkgname, inst):
"""Build yum appropriate package name."""
ypname = pkgname
@@ -45,6 +46,7 @@ def build_yname(pkgname, inst):
ypname += ".%s" % (inst.get('arch'))
return ypname
+
class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
"""Support for Yum packages."""
pkgtype = 'yum'
@@ -59,7 +61,8 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
__ireq__ = {'Package': ['name']}
#__ireq__ = {'Package': ['name', 'version']}
- __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']}
+ __new_req__ = {'Package': ['name'],
+ 'Instance': ['version', 'release', 'arch']}
__new_ireq__ = {'Package': ['name'], \
'Instance': []}
#__new_ireq__ = {'Package': ['name', 'uri'], \
@@ -68,8 +71,10 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
__gpg_req__ = {'Package': ['name', 'version']}
__gpg_ireq__ = {'Package': ['name', 'version']}
- __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']}
- __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']}
+ __new_gpg_req__ = {'Package': ['name'],
+ 'Instance': ['version', 'release']}
+ __new_gpg_ireq__ = {'Package': ['name'],
+ 'Instance': ['version', 'release']}
conflicts = ['YUMng', 'RPMng']
@@ -101,10 +106,14 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
data = {pkg.arch: (pkg.epoch, pkg.version, pkg.release)}
else:
pname = pkg[0]
- if pkg[1] is None: a = 'noarch'
- else: a = pkg[1]
- if pkg[2] is None: e = '0'
- else: e = pkg[2]
+ if pkg[1] is None:
+ a = 'noarch'
+ else:
+ a = pkg[1]
+ if pkg[2] is None:
+ e = '0'
+ else:
+ e = pkg[2]
data = {a: (e, pkg[3], pkg[4])}
if pname in dest:
dest[pname].update(data)
@@ -137,24 +146,24 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
if entry.get('type', False) == 'yum':
# Check for virtual provides or packages. If we don't have
# this package use Yum to resolve it to a real package name
- knownPkgs = self.yum_installed.keys() + self.yum_avail.keys()
+ knownPkgs = list(self.yum_installed.keys()) + list(self.yum_avail.keys())
if entry.get('name') not in knownPkgs:
# If the package name matches something installed
# or available the that's the correct package.
try:
- pkgDict = dict( [ (i.name, i) for i in \
- self.yb.returnPackagesByDep(entry.get('name')) ] )
+ pkgDict = dict([(i.name, i) for i in \
+ self.yb.returnPackagesByDep(entry.get('name'))])
except yum.Errors.YumBaseError, e:
self.logger.error('Yum Error Depsolving for %s: %s' % \
(entry.get('name'), str(e)))
pkgDict = {}
if len(pkgDict) > 1:
- # What do we do with multiple packages?
+ # What do we do with multiple packages?
s = "YUMng: returnPackagesByDep(%s) returned many packages"
self.logger.info(s % entry.get('name'))
s = "YUMng: matching packages: %s"
- self.logger.info(s % str(pkgDict.keys()))
+ self.logger.info(s % str(list(pkgDict.keys())))
pkgs = set(pkgDict.keys()) & set(self.yum_installed.keys())
if len(pkgs) > 0:
# Virtual packages matches an installed real package
@@ -166,7 +175,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
# and Yum should Do The Right Thing on package install
pkg = None
elif len(pkgDict) == 1:
- pkg = pkgDict.values()[0]
+ pkg = list(pkgDict.values())[0]
else: # len(pkgDict) == 0
s = "YUMng: returnPackagesByDep(%s) returned no results"
self.logger.info(s % entry.get('name'))
@@ -252,16 +261,16 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
self.logger.error("GPG key has no simplefile attribute")
continue
key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \
- inst.get('simplefile'))
+ inst.get('simplefile'))
cmdrc, output = self.cmd.run("rpm --import %s" % key_arg)
if cmdrc != 0:
self.logger.debug("Unable to install %s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- self.str_evra(inst)))
+ (self.instance_status[inst].get('pkg').get('name'), \
+ self.str_evra(inst)))
else:
self.logger.debug("Installed %s-%s-%s" % \
- (self.instance_status[inst].get('pkg').get('name'), \
- inst.get('version'), inst.get('release')))
+ (self.instance_status[inst].get('pkg').get('name'), \
+ inst.get('version'), inst.get('release')))
self.RefreshPackages()
self.gpg_keyids = self.getinstalledgpg()
pkg = self.instance_status[gpg_keys[0]].get('pkg')
@@ -374,9 +383,9 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
pkg_arg = pkg_arg + '.' + inst.get('arch')
erase_args.append(pkg_arg)
else:
- pkgspec = { 'name':pkg.get('name'),
- 'version':inst.get('version'),
- 'release':inst.get('release')}
+ pkgspec = {'name': pkg.get('name'),
+ 'version': inst.get('version'),
+ 'release': inst.get('release')}
self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\
% (pkgspec.get('name'), self.str_evra(pkgspec)))
self.logger.info(" This package will be deleted in a future version of the RPMng driver.")
@@ -395,7 +404,7 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
for inst in pkg:
if pkg.get('name') != 'gpg-pubkey':
pkg_arg = pkg.get('name') + '-'
- if inst.attrib.has_key('epoch'):
+ if 'epoch' in inst.attrib:
pkg_arg = pkg_arg + inst.get('epoch') + ':'
pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release')
if 'arch' in inst.attrib:
@@ -416,6 +425,5 @@ class YUM24(Bcfg2.Client.Tools.RPMng.RPMng):
if pkg_modified == True:
self.modified.append(pkg)
-
self.RefreshPackages()
self.extra = self.FindExtraPackages()
diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Client/Tools/YUMng.py
index 44d56ff9f..c9e7aa15e 100644
--- a/src/lib/Client/Tools/YUMng.py
+++ b/src/lib/Client/Tools/YUMng.py
@@ -300,7 +300,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
# 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
+ ver=po.version, rel=po.release) # find all arches of pkg
if len(packages) == 1:
return results # No mathcing multilib packages
@@ -319,13 +319,13 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
v = verify(p)
self.verifyCache[k] = v
- for fn, probs in v.items():
+ for fn, probs in list(v.items()):
# file problems must exist in ALL multilib packages to be real
if fn in files:
common[fn] = common.get(fn, 0) + 1
flag = len(packages) - 1
- for fn, i in common.items():
+ for fn, i in list(common.items()):
if i == flag:
# this fn had verify problems in all but one of the multilib
# packages. That means its correct in the package that's
@@ -463,6 +463,14 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
self.logger.debug(" Not checking version for virtual package")
_POs = [po for po in POs] # Make a copy
elif entry.get('name') == 'gpg-pubkey':
+ if 'version' not in nevra:
+ m = "Skipping verify: gpg-pubkey without an RPM version."
+ self.logger.warning(m)
+ continue
+ if 'release' not in nevra:
+ m = "Skipping verify: gpg-pubkey without an RPM release."
+ self.logger.warning(m)
+ continue
_POs = [p for p in POs if p.version == nevra['version'] \
and p.release == nevra['release']]
else:
@@ -504,7 +512,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \
[ig.get('name') for ig in inst.findall('Ignore')] + \
self.ignores
- for fn, probs in vResult.items():
+ for fn, probs in list(vResult.items()):
if fn in modlist:
self.logger.debug(" %s in modlist, skipping" % fn)
continue
@@ -529,7 +537,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
"these files, revert the changes, or ignore "
"false failures:")
self.logger.debug(" Verify Problems:")
- for fn, probs in stat['verify'].items():
+ for fn, probs in list(stat['verify'].items()):
self.logger.debug(" %s" % fn)
for p in probs:
self.logger.debug(" %s: %s" % p)
@@ -569,7 +577,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
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 = []
@@ -580,7 +588,7 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
flag = True
if len(pkgs) > 0:
if pkgs[0] in checked:
- continue # We've already taken care of this Instance
+ continue # We've already taken care of this Instance
else:
checked.append(pkgs[0])
_POs.remove(pkgs[0])
@@ -601,16 +609,17 @@ class YUMng(Bcfg2.Client.Tools.PkgTool):
packages = [e.get('name') for e in self.getSupportedEntries()]
extras = []
- for p in self.installed.keys():
+ for p in list(self.installed.keys()):
if p not in packages:
entry = Bcfg2.Client.XML.Element('Package', name=p,
type=self.pkgtype)
for i in self.installed[p]:
- inst = Bcfg2.Client.XML.SubElement(entry, 'Instance', \
- epoch = i['epoch'],
- version = i['version'],
- release = i['release'],
- arch = i['arch'])
+ inst = Bcfg2.Client.XML.SubElement(entry,
+ 'Instance',
+ epoch=i['epoch'],
+ version=i['version'],
+ release=i['release'],
+ arch=i['arch'])
extras.append(entry)
diff --git a/src/lib/Client/Tools/__init__.py b/src/lib/Client/Tools/__init__.py
index b5120db71..88609c2f6 100644
--- a/src/lib/Client/Tools/__init__.py
+++ b/src/lib/Client/Tools/__init__.py
@@ -4,9 +4,9 @@ import warnings
warnings.filterwarnings("ignore", "The popen2 module is deprecated.*",
DeprecationWarning)
import os
-import popen2
import stat
import sys
+from subprocess import Popen, PIPE
import time
import Bcfg2.Client.XML
@@ -25,26 +25,6 @@ class toolInstantiationError(Exception):
pass
-class readonlypipe(popen2.Popen4):
- """This pipe sets up stdin --> /dev/null."""
-
- def __init__(self, cmd, bufsize=-1):
- popen2._cleanup()
- c2pread, c2pwrite = os.pipe()
- null = open('/dev/null', 'w+')
- self.pid = os.fork()
- if self.pid == 0:
- # Child
- os.dup2(null.fileno(), sys.__stdin__.fileno())
- #os.dup2(p2cread, 0)
- os.dup2(c2pwrite, 1)
- os.dup2(c2pwrite, 2)
- self._run_child(cmd)
- os.close(c2pwrite)
- self.fromchild = os.fdopen(c2pread, 'r', bufsize)
- popen2._active.append(self)
-
-
class executor:
"""This class runs stuff for us"""
@@ -53,30 +33,12 @@ class executor:
def run(self, command):
"""Run a command in a pipe dealing with stdout buffer overloads."""
- self.logger.debug('> %s' % command)
-
- runpipe = readonlypipe(command, bufsize=16384)
- output = []
- try:#macosx doesn't like this
- runpipe.fromchild.flush()
- except IOError:
- pass
- line = runpipe.fromchild.readline()
- cmdstat = -1
- while cmdstat == -1:
- while line:
- if len(line) > 0:
- self.logger.debug('< %s' % line[:-1])
- output.append(line[:-1])
- line = runpipe.fromchild.readline()
- time.sleep(0.1)
- cmdstat = runpipe.poll()
- output += [line[:-1] for line in runpipe.fromchild.readlines() \
- if line]
- # The exit code from the program is in the upper byte of the
- # value returned by cmdstat. Shift it down for tools looking at
- # the value.
- return ((cmdstat >> 8), output)
+ p = Popen(command, shell=True, bufsize=16384,
+ stdin=PIPE, stdout=PIPE, close_fds=True)
+ output = p.communicate()[0]
+ for line in output.splitlines():
+ self.logger.debug('< %s' % line)
+ return (p.returncode, output.splitlines())
class Tool:
@@ -185,7 +147,9 @@ class Tool:
if 'failure' in entry.attrib:
self.logger.error("Entry %s:%s reports bind failure: %s" % \
- (entry.tag, entry.get('name'), entry.get('failure')))
+ (entry.tag,
+ entry.get('name'),
+ entry.get('failure')))
return False
missing = [attr for attr in self.__req__[entry.tag] \
@@ -198,7 +162,8 @@ class Tool:
try:
self.gatherCurrentData(entry)
except:
- self.logger.error("Unexpected error in gatherCurrentData", exc_info=1)
+ self.logger.error("Unexpected error in gatherCurrentData",
+ exc_info=1)
return False
return True
@@ -255,7 +220,8 @@ class PkgTool(Tool):
self.logger.info("Trying single pass package install for pkgtype %s" % \
self.pkgtype)
- data = [tuple([pkg.get(field) for field in self.pkgtool[1][1]]) for pkg in packages]
+ data = [tuple([pkg.get(field) for field in self.pkgtool[1][1]])
+ for pkg in packages]
pkgargs = " ".join([self.pkgtool[1][0] % datum for datum in data])
self.logger.debug("Installing packages: :%s:" % pkgargs)
@@ -348,7 +314,9 @@ class SvcTool(Tool):
return
for entry in [ent for ent in bundle if self.handlesEntry(ent)]:
- if entry.get('mode', 'default') == 'manual':
+ mode = entry.get('mode', 'default')
+ if mode == 'manual' or \
+ (mode == 'interactive_only' and not self.setup['interactive']):
continue
# need to handle servicemode = (build|default)
# need to handle mode = (default|supervised)
@@ -358,7 +326,12 @@ class SvcTool(Tool):
else:
if self.setup['interactive']:
prompt = 'Restart service %s?: (y/N): ' % entry.get('name')
- if raw_input(prompt) not in ['y', 'Y']:
+ # py3k compatibility
+ try:
+ ans = raw_input(prompt)
+ except NameError:
+ ans = input(prompt)
+ if ans not in ['y', 'Y']:
continue
rc = self.restart_service(entry)
else:
diff --git a/src/lib/Client/Tools/launchd.py b/src/lib/Client/Tools/launchd.py
index db6d94c1b..03dd97e71 100644
--- a/src/lib/Client/Tools/launchd.py
+++ b/src/lib/Client/Tools/launchd.py
@@ -82,6 +82,11 @@ class launchd(Bcfg2.Client.Tools.Tool):
def InstallService(self, entry):
"""Enable or disable launchd item."""
+ # don't take any actions for mode='manual'
+ if entry.get('mode', 'default') == 'manual':
+ self.logger.info("Service %s mode set to manual. Skipping "
+ "installation." % (entry.get('name')))
+ return False
name = entry.get('name')
if entry.get('status') == 'on':
self.logger.error("Installing service %s" % name)
diff --git a/src/lib/Component.py b/src/lib/Component.py
index 33b1c9289..88dce906e 100644
--- a/src/lib/Component.py
+++ b/src/lib/Component.py
@@ -11,12 +11,12 @@ import pydoc
import sys
import time
import threading
-import urlparse
-import xmlrpclib
import Bcfg2.Logger
from Bcfg2.Statistics import Statistics
from Bcfg2.SSLServer import XMLRPCServer
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import xmlrpclib, urlparse, fprint
logger = logging.getLogger()
@@ -56,11 +56,11 @@ def run_component(component_cls, location, daemon, pidfile_name, to_file,
os.chdir(os.sep)
pidfile = open(pidfile_name or "/dev/null", "w")
- print >> pidfile, os.getpid()
+ fprint(os.getpid(), pidfile)
pidfile.close()
component = component_cls(cfile=cfile, **cls_kwargs)
- up = urlparse.urlparse(location)
+ up = urlparse(location)
port = tuple(up[1].split(':'))
port = (port[0], int(port[1]))
try:
@@ -153,30 +153,31 @@ class Component (object):
automatic == True.
"""
- for name, func in inspect.getmembers(self, callable):
- if getattr(func, "automatic", False):
- need_to_lock = not getattr(func, 'locking', False)
- if (time.time() - func.automatic_ts) > \
- func.automatic_period:
- if need_to_lock:
- t1 = time.time()
- self.lock.acquire()
- t2 = time.time()
- self.instance_statistics.add_value('component_lock', t2-t1)
- try:
- mt1 = time.time()
+ for name, func in inspect.getmembers(self):
+ if name == '__call__':
+ if getattr(func, "automatic", False):
+ need_to_lock = not getattr(func, 'locking', False)
+ if (time.time() - func.automatic_ts) > \
+ func.automatic_period:
+ if need_to_lock:
+ t1 = time.time()
+ self.lock.acquire()
+ t2 = time.time()
+ self.instance_statistics.add_value('component_lock', t2-t1)
try:
- func()
- except:
- self.logger.error("Automatic method %s failed" \
- % (name), exc_info=1)
- finally:
- mt2 = time.time()
-
- if need_to_lock:
- self.lock.release()
- self.instance_statistics.add_value(name, mt2-mt1)
- func.__dict__['automatic_ts'] = time.time()
+ mt1 = time.time()
+ try:
+ func()
+ except:
+ self.logger.error("Automatic method %s failed" \
+ % (name), exc_info=1)
+ finally:
+ mt2 = time.time()
+
+ if need_to_lock:
+ self.lock.release()
+ self.instance_statistics.add_value(name, mt2-mt1)
+ func.__dict__['automatic_ts'] = time.time()
def _resolve_exposed_method(self, method_name):
"""Resolve an exposed method.
@@ -209,7 +210,8 @@ class Component (object):
except NoExposedMethod:
self.logger.error("Unknown method %s" % (method))
raise xmlrpclib.Fault(7, "Unknown method %s" % method)
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
if getattr(e, "log", True):
self.logger.error(e, exc_info=True)
raise xmlrpclib.Fault(getattr(e, "fault_code", 1), str(e))
@@ -233,7 +235,8 @@ class Component (object):
self.instance_statistics.add_value(method, method_done - method_start)
except xmlrpclib.Fault:
raise
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
if getattr(e, "log", True):
self.logger.error(e, exc_info=True)
raise xmlrpclib.Fault(getattr(e, "fault_code", 1), str(e))
diff --git a/src/lib/Logger.py b/src/lib/Logger.py
index e8cdd492d..9fe81f47e 100644
--- a/src/lib/Logger.py
+++ b/src/lib/Logger.py
@@ -10,18 +10,25 @@ import socket
import struct
import sys
import termios
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import fprint
logging.raiseExceptions = 0
+
def print_attributes(attrib):
"""Add the attributes for an element."""
return ' '.join(['%s="%s"' % data for data in list(attrib.items())])
+
def print_text(text):
"""Add text to the output (which will need normalising."""
- charmap = {'<':'&lt;', '>':'&gt;', '&':'&amp;'}
+ charmap = {'<': '&lt;',
+ '>': '&gt;',
+ '&': '&amp;'}
return ''.join([charmap.get(char, char) for char in text]) + '\n'
+
def xml_print(element, running_indent=0, indent=4):
"""Add an element and its children to the return string."""
if (len(element.getchildren()) == 0) and (not element.text):
@@ -32,7 +39,7 @@ def xml_print(element, running_indent=0, indent=4):
ret = (' ' * running_indent)
ret += '<%s%s>\n' % (element.tag, print_attributes(element))
if element.text:
- ret += (' '* child_indent) + print_text(element.text)
+ ret += (' ' * child_indent) + print_text(element.text)
for child in element.getchildren():
ret += xml_print(child, child_indent, indent)
ret += (' ' * running_indent) + '</%s>\n' % (element.tag)
@@ -40,16 +47,21 @@ def xml_print(element, running_indent=0, indent=4):
ret += (' ' * child_indent) + print_text(element.tail)
return ret
+
class TermiosFormatter(logging.Formatter):
- """The termios formatter displays output in a terminal-sensitive fashion."""
+ """The termios formatter displays output
+ in a terminal-sensitive fashion.
+ """
def __init__(self, fmt=None, datefmt=None):
logging.Formatter.__init__(self, fmt, datefmt)
if sys.stdout.isatty():
# now get termios info
try:
- self.width = struct.unpack('hhhh', fcntl.ioctl(0, termios.TIOCGWINSZ,
- "\000"*8))[1]
+ self.width = struct.unpack('hhhh',
+ fcntl.ioctl(0,
+ termios.TIOCGWINSZ,
+ "\000" * 8))[1]
if self.width == 0:
self.width = 80
except:
@@ -67,16 +79,16 @@ class TermiosFormatter(logging.Formatter):
if len(line) <= line_len:
returns.append(line)
else:
- inner_lines = int(math.floor(float(len(line)) / line_len))+1
+ inner_lines = int(math.floor(float(len(line)) / line_len)) + 1
for i in range(inner_lines):
- returns.append("%s" % (line[i*line_len:(i+1)*line_len]))
+ returns.append("%s" % (line[i * line_len:(i + 1) * line_len]))
elif isinstance(record.msg, list):
if not record.msg:
return ''
record.msg.sort()
msgwidth = self.width
columnWidth = max([len(item) for item in record.msg])
- columns = int(math.floor(float(msgwidth) / (columnWidth+2)))
+ columns = int(math.floor(float(msgwidth) / (columnWidth + 2)))
lines = int(math.ceil(float(len(record.msg)) / columns))
for lineNumber in range(lines):
indices = [idx for idx in [(colNum * lines) + lineNumber
@@ -91,6 +103,7 @@ class TermiosFormatter(logging.Formatter):
returns.append(self.formatException(record.exc_info))
return '\n'.join(returns)
+
class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
"""
This handler fragments messages into
@@ -105,7 +118,7 @@ class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
def emit(self, record):
"""Chunk and deliver records."""
record.name = self.procname
- if str(record.msg) > 250:
+ if isinstance(record.msg, str):
msgs = []
error = record.exc_info
record.exc_info = None
@@ -120,14 +133,16 @@ class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
msgs = [record]
for newrec in msgs:
msg = self.log_format_string % (self.encodePriority(self.facility,
- newrec.levelname.lower()), self.format(newrec))
+ newrec.levelname.lower()),
+ self.format(newrec))
try:
- self.socket.send(msg)
+ self.socket.send(msg.encode('ascii'))
except socket.error:
- for i in xrange(10):
+ for i in range(10):
try:
if isinstance(self.address, tuple):
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.socket = socket.socket(socket.AF_INET,
+ socket.SOCK_DGRAM)
self.socket.connect(self.address)
else:
self._connect_unixsocket(self.address)
@@ -144,23 +159,34 @@ class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
"""
pass
-def setup_logging(procname, to_console=True, to_syslog=True, syslog_facility='daemon', level=0, to_file=None):
+
+def setup_logging(procname, to_console=True, to_syslog=True,
+ syslog_facility='daemon', level=0, to_file=None):
"""Setup logging for Bcfg2 software."""
if hasattr(logging, 'already_setup'):
return
+
# add the handler to the root logger
if to_console:
console = logging.StreamHandler(sys.stdout)
- console.setLevel(logging.DEBUG)
+ if to_console is True:
+ console.setLevel(logging.DEBUG)
+ else:
+ console.setLevel(to_console)
# tell the handler to use this format
console.setFormatter(TermiosFormatter())
logging.root.addHandler(console)
+
if to_syslog:
try:
try:
- syslog = FragmentingSysLogHandler(procname, '/dev/log', syslog_facility)
+ syslog = FragmentingSysLogHandler(procname,
+ '/dev/log',
+ syslog_facility)
except socket.error:
- syslog = FragmentingSysLogHandler(procname, ('localhost', 514), syslog_facility)
+ syslog = FragmentingSysLogHandler(procname,
+ ('localhost', 514),
+ syslog_facility)
syslog.setLevel(logging.DEBUG)
syslog.setFormatter(logging.Formatter('%(name)s[%(process)d]: %(message)s'))
logging.root.addHandler(syslog)
@@ -168,14 +194,17 @@ def setup_logging(procname, to_console=True, to_syslog=True, syslog_facility='da
logging.root.error("failed to activate syslogging")
except:
print("Failed to activate syslogging")
+
if not to_file == None:
filelog = logging.FileHandler(to_file)
filelog.setLevel(logging.DEBUG)
filelog.setFormatter(logging.Formatter('%(asctime)s %(name)s[%(process)d]: %(message)s'))
logging.root.addHandler(filelog)
+
logging.root.setLevel(level)
logging.already_setup = True
+
def trace_process(**kwargs):
"""Literally log every line of python code as it runs.
@@ -202,11 +231,12 @@ def trace_process(**kwargs):
filename = filename[:-1]
name = frame.f_globals["__name__"]
line = linecache.getline(filename, lineno)
- print >> log_file, "%s:%s: %s" % (name, lineno, line.rstrip())
+ fprint("%s:%s: %s" % (name, lineno, line.rstrip()), log_file)
return traceit
sys.settrace(traceit)
+
def log_to_stderr(logger_name, level=logging.INFO):
"""Set up console logging."""
try:
@@ -214,11 +244,12 @@ def log_to_stderr(logger_name, level=logging.INFO):
except:
# assume logger_name is already a logger
logger = logger_name
- handler = logging.StreamHandler() # sys.stderr is the default stream
+ handler = logging.StreamHandler() # sys.stderr is the default stream
handler.setLevel(level)
- handler.setFormatter(TermiosFormatter()) # investigate this formatter
+ handler.setFormatter(TermiosFormatter()) # investigate this formatter
logger.addHandler(handler)
+
def log_to_syslog(logger_name, level=logging.INFO, format='%(name)s[%(process)d]: %(message)s'):
"""Set up syslog logging."""
try:
@@ -227,7 +258,7 @@ def log_to_syslog(logger_name, level=logging.INFO, format='%(name)s[%(process)d]
# assume logger_name is already a logger
logger = logger_name
# anticipate an exception somewhere below
- handler = logging.handlers.SysLogHandler() # investigate FragmentingSysLogHandler
+ handler = logging.handlers.SysLogHandler() # investigate FragmentingSysLogHandler
handler.setLevel(level)
handler.setFormatter(logging.Formatter(format))
logger.addHandler(handler)
diff --git a/src/lib/Options.py b/src/lib/Options.py
index 4041ccf78..d5304e696 100644
--- a/src/lib/Options.py
+++ b/src/lib/Options.py
@@ -1,11 +1,12 @@
"""Option parsing library for utilities."""
__revision__ = '$Revision$'
-import ConfigParser
import getopt
import os
import sys
import Bcfg2.Client.Tools
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import ConfigParser
def bool_cook(x):
if x:
@@ -146,7 +147,8 @@ class OptionSet(dict):
try:
opts, args = getopt.getopt(argv, self.buildGetopt(),
self.buildLongGetopt())
- except getopt.GetoptError, err:
+ except getopt.GetoptError:
+ err = sys.exc_info()[1]
self.helpExit(err)
if '-h' in argv:
self.helpExit('', 0)
@@ -201,6 +203,14 @@ PARANOID_MAX_COPIES = Option('Specify the number of paranoid copies you want',
OMIT_LOCK_CHECK = Option('Omit lock check', default=False, cmd='-O')
CORE_PROFILE = Option('profile',
default=False, cmd='-p', )
+FILES_ON_STDIN = Option('Operate on a list of files supplied on stdin',
+ cmd='--stdin', default=False, long_arg=True)
+SCHEMA_PATH = Option('Path to XML Schema files', cmd='--schema',
+ odesc='<schema path>',
+ default="%s/share/bcfg2/schemas" % DEFAULT_INSTALL_PREFIX,
+ long_arg=True)
+REQUIRE_SCHEMA = Option("Require property files to have matching schema files",
+ cmd="--require-schema", default=False, long_arg=True)
# Metadata options
MDATA_OWNER = Option('Default Path owner',
diff --git a/src/lib/Proxy.py b/src/lib/Proxy.py
index 275405faf..8a1ad683e 100644
--- a/src/lib/Proxy.py
+++ b/src/lib/Proxy.py
@@ -11,9 +11,6 @@ load_config -- read configuration files
__revision__ = '$Revision: $'
-from xmlrpclib import _Method
-
-import httplib
import logging
import re
import socket
@@ -25,50 +22,59 @@ import socket
try:
import ssl
SSL_LIB = 'py26_ssl'
-except ImportError, e:
+except ImportError:
from M2Crypto import SSL
import M2Crypto.SSL.Checker
SSL_LIB = 'm2crypto'
-import string
import sys
import time
-import urlparse
-import xmlrpclib
+
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import httplib, xmlrpclib, urlparse
version = sys.version_info[:2]
-has_py23 = map(int, version) >= [2, 3]
-has_py26 = map(int, version) >= [2, 6]
+has_py23 = version >= (2, 3)
+has_py26 = version >= (2, 6)
+
+__all__ = ["ComponentProxy",
+ "RetryMethod",
+ "SSLHTTPConnection",
+ "XMLRPCTransport"]
-__all__ = ["ComponentProxy", "RetryMethod", "SSLHTTPConnection", "XMLRPCTransport"]
class CertificateError(Exception):
def __init__(self, commonName):
self.commonName = commonName
-class RetryMethod(_Method):
+
+class RetryMethod(xmlrpclib._Method):
"""Method with error handling and retries built in."""
log = logging.getLogger('xmlrpc')
max_retries = 4
+
def __call__(self, *args):
for retry in range(self.max_retries):
try:
- return _Method.__call__(self, *args)
- except xmlrpclib.ProtocolError, err:
+ return xmlrpclib._Method.__call__(self, *args)
+ except xmlrpclib.ProtocolError:
+ err = sys.exc_info()[1]
self.log.error("Server failure: Protocol Error: %s %s" % \
(err.errcode, err.errmsg))
raise xmlrpclib.Fault(20, "Server Failure")
except xmlrpclib.Fault:
raise
- except socket.error, err:
+ except socket.error:
+ err = sys.exc_info()[1]
if hasattr(err, 'errno') and err.errno == 336265218:
self.log.error("SSL Key error")
break
if retry == 3:
self.log.error("Server failure: %s" % err)
raise xmlrpclib.Fault(20, err)
- except CertificateError, ce:
+ except CertificateError:
+ ce = sys.exc_info()[1]
self.log.error("Got unallowed commonName %s from server" \
% ce.commonName)
break
@@ -82,10 +88,13 @@ class RetryMethod(_Method):
raise xmlrpclib.Fault(20, "Server Failure")
# sorry jon
-xmlrpclib._Method = RetryMethod
+_Method = RetryMethod
+
class SSLHTTPConnection(httplib.HTTPConnection):
- """Extension of HTTPConnection that implements SSL and related behaviors."""
+ """Extension of HTTPConnection that
+ implements SSL and related behaviors.
+ """
logger = logging.getLogger('Bcfg2.Proxy.SSLHTTPConnection')
@@ -154,8 +163,7 @@ class SSLHTTPConnection(httplib.HTTPConnection):
elif SSL_LIB == 'm2crypto':
self._connect_m2crypto()
else:
- raise Exception, "No SSL module support"
-
+ raise Exception("No SSL module support")
def _connect_py26ssl(self):
"""Initiates a connection using the ssl module."""
@@ -166,7 +174,7 @@ class SSLHTTPConnection(httplib.HTTPConnection):
ssl_protocol_ver = ssl.PROTOCOL_TLSv1
else:
self.logger.error("Unknown protocol %s" % (self.protocol))
- raise Exception, "unknown protocol %s" % self.protocol
+ raise Exception("unknown protocol %s" % self.protocol)
if self.ca:
other_side_required = ssl.CERT_REQUIRED
else:
@@ -190,7 +198,7 @@ class SSLHTTPConnection(httplib.HTTPConnection):
if peer_cert and self.scns:
scn = [x[0][1] for x in peer_cert['subject'] if x[0][0] == 'commonName'][0]
if scn not in self.scns:
- raise CertificateError, scn
+ raise CertificateError(scn)
self.sock.closeSocket = True
def _connect_m2crypto(self):
@@ -202,7 +210,7 @@ class SSLHTTPConnection(httplib.HTTPConnection):
ctx = SSL.Context('tlsv1')
else:
self.logger.error("Unknown protocol %s" % (self.protocol))
- raise Exception, "unknown protocol %s" % self.protocol
+ raise Exception("unknown protocol %s" % self.protocol)
if self.ca:
# Use the certificate authority to validate the cert
@@ -235,12 +243,14 @@ class SSLHTTPConnection(httplib.HTTPConnection):
try:
self.sock.connect((hostname, self.port))
# automatically checks cert matches host
- except M2Crypto.SSL.Checker.WrongHost, wr:
- raise CertificateError, wr
+ except M2Crypto.SSL.Checker.WrongHost:
+ wr = sys.exc_info()[1]
+ raise CertificateError(wr)
class XMLRPCTransport(xmlrpclib.Transport):
- def __init__(self, key=None, cert=None, ca=None, scns=None, use_datetime=0, timeout=90):
+ def __init__(self, key=None, cert=None, ca=None,
+ scns=None, use_datetime=0, timeout=90):
if hasattr(xmlrpclib.Transport, '__init__'):
xmlrpclib.Transport.__init__(self, use_datetime)
self.key = key
@@ -250,9 +260,13 @@ class XMLRPCTransport(xmlrpclib.Transport):
self.timeout = timeout
def make_connection(self, host):
- host = self.get_host_info(host)[0]
- http = SSLHTTPConnection(host, key=self.key, cert=self.cert, ca=self.ca,
- scns=self.scns, timeout=self.timeout)
+ host, self._extra_headers = self.get_host_info(host)[0:2]
+ http = SSLHTTPConnection(host,
+ key=self.key,
+ cert=self.cert,
+ ca=self.ca,
+ scns=self.scns,
+ timeout=self.timeout)
https = httplib.HTTP()
https._setup(http)
return https
@@ -268,7 +282,10 @@ class XMLRPCTransport(xmlrpclib.Transport):
errcode, errmsg, headers = h.getreply()
if errcode != 200:
- raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, headers)
+ raise xmlrpclib.ProtocolError(host + handler,
+ errcode,
+ errmsg,
+ headers)
self.verbose = verbose
msglen = int(headers.dict['content-length'])
@@ -287,7 +304,7 @@ class XMLRPCTransport(xmlrpclib.Transport):
if not response:
break
if self.verbose:
- print "body:", repr(response), len(response)
+ print("body:", repr(response), len(response))
p.feed(response)
fd.close()
@@ -295,7 +312,9 @@ class XMLRPCTransport(xmlrpclib.Transport):
return u.close()
-def ComponentProxy(url, user=None, password=None, key=None, cert=None, ca=None,
+
+def ComponentProxy(url, user=None, password=None,
+ key=None, cert=None, ca=None,
allowedServerCNs=None, timeout=90):
"""Constructs proxies to components.
@@ -308,9 +327,10 @@ def ComponentProxy(url, user=None, password=None, key=None, cert=None, ca=None,
"""
if user and password:
- method, path = urlparse.urlparse(url)[:2]
+ method, path = urlparse(url)[:2]
newurl = "%s://%s:%s@%s" % (method, user, password, path)
else:
newurl = url
- ssl_trans = XMLRPCTransport(key, cert, ca, allowedServerCNs, timeout=timeout)
+ ssl_trans = XMLRPCTransport(key, cert, ca,
+ allowedServerCNs, timeout=timeout)
return xmlrpclib.ServerProxy(newurl, allow_none=True, transport=ssl_trans)
diff --git a/src/lib/SSLServer.py b/src/lib/SSLServer.py
index 1f4c1c8e4..a89beabbb 100644
--- a/src/lib/SSLServer.py
+++ b/src/lib/SSLServer.py
@@ -8,10 +8,7 @@ __all__ = [
import os
import sys
-import xmlrpclib
import socket
-import SocketServer
-import SimpleXMLRPCServer
import base64
import select
import signal
@@ -19,12 +16,17 @@ import logging
import ssl
import threading
import time
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import xmlrpclib, SimpleXMLRPCServer, SocketServer
+
class ForkedChild(Exception):
pass
+
class XMLRPCDispatcher (SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
logger = logging.getLogger("Cobalt.Server.XMLRPCDispatcher")
+
def __init__(self, allow_none, encoding):
try:
SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
@@ -48,7 +50,8 @@ class XMLRPCDispatcher (SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
raw_response = xmlrpclib.dumps(response, methodresponse=1,
allow_none=self.allow_none,
encoding=self.encoding)
- except xmlrpclib.Fault, fault:
+ except xmlrpclib.Fault:
+ fault = sys.exc_info()[1]
raw_response = xmlrpclib.dumps(fault,
allow_none=self.allow_none,
encoding=self.encoding)
@@ -60,6 +63,7 @@ class XMLRPCDispatcher (SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
allow_none=self.allow_none, encoding=self.encoding)
return raw_response
+
class SSLServer (SocketServer.TCPServer, object):
"""TCP server supporting SSL encryption.
@@ -76,7 +80,8 @@ class SSLServer (SocketServer.TCPServer, object):
logger = logging.getLogger("Cobalt.Server.TCPServer")
def __init__(self, server_address, RequestHandlerClass, keyfile=None,
- certfile=None, reqCert=False, ca=None, timeout=None, protocol='xmlrpc/ssl'):
+ certfile=None, reqCert=False, ca=None, timeout=None,
+ protocol='xmlrpc/ssl'):
"""Initialize the SSL-TCP server.
@@ -106,17 +111,17 @@ class SSLServer (SocketServer.TCPServer, object):
if keyfile != None:
if keyfile == False or not os.path.exists(keyfile):
self.logger.error("Keyfile %s does not exist" % keyfile)
- raise Exception, "keyfile doesn't exist"
+ raise Exception("keyfile doesn't exist")
self.certfile = certfile
if certfile != None:
if certfile == False or not os.path.exists(certfile):
self.logger.error("Certfile %s does not exist" % certfile)
- raise Exception, "certfile doesn't exist"
+ raise Exception("certfile doesn't exist")
self.ca = ca
if ca != None:
if ca == False or not os.path.exists(ca):
self.logger.error("CA %s does not exist" % ca)
- raise Exception, "ca doesn't exist"
+ raise Exception("ca doesn't exist")
self.reqCert = reqCert
if ca and certfile:
self.mode = ssl.CERT_OPTIONAL
@@ -128,14 +133,18 @@ class SSLServer (SocketServer.TCPServer, object):
self.ssl_protocol = ssl.PROTOCOL_TLSv1
else:
self.logger.error("Unknown protocol %s" % (protocol))
- raise Exception, "unknown protocol %s" % protocol
+ raise Exception("unknown protocol %s" % protocol)
def get_request(self):
(sock, sockinfo) = self.socket.accept()
sock.settimeout(self.timeout)
- sslsock = ssl.wrap_socket(sock, server_side=True, certfile=self.certfile,
- keyfile=self.keyfile, cert_reqs=self.mode,
- ca_certs=self.ca, ssl_version=self.ssl_protocol)
+ sslsock = ssl.wrap_socket(sock,
+ server_side=True,
+ certfile=self.certfile,
+ keyfile=self.keyfile,
+ cert_reqs=self.mode,
+ ca_certs=self.ca,
+ ssl_version=self.ssl_protocol)
return sslsock, sockinfo
def close_request(self, request):
@@ -212,20 +221,21 @@ class XMLRPCRequestHandler (SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
### need to override do_POST here
def do_POST(self):
try:
- max_chunk_size = 10*1024*1024
+ max_chunk_size = 10 * 1024 * 1024
size_remaining = int(self.headers["content-length"])
L = []
while size_remaining:
try:
select.select([self.rfile.fileno()], [], [], 3)
except select.error:
- print "got select timeout"
+ print("got select timeout")
raise
chunk_size = min(size_remaining, max_chunk_size)
L.append(self.rfile.read(chunk_size))
size_remaining -= len(L[-1])
data = ''.join(L)
- response = self.server._marshaled_dispatch(self.client_address, data)
+ response = self.server._marshaled_dispatch(self.client_address,
+ data)
except:
try:
self.send_response(500)
@@ -233,7 +243,7 @@ class XMLRPCRequestHandler (SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
except:
(type, msg) = sys.exc_info()[:2]
self.logger.error("Error sending 500 response (%s): %s" % \
- (type, msg))
+ (type, msg))
raise
else:
# got a valid XML RPC response
@@ -248,7 +258,8 @@ class XMLRPCRequestHandler (SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
# If we hit SSL3_WRITE_PENDING here try to resend.
self.wfile.write(response)
break
- except ssl.SSLError, e:
+ except ssl.SSLError:
+ e = sys.exc_info()[1]
if str(e).find("SSL3_WRITE_PENDING") < 0:
raise
self.logger.error("SSL3_WRITE_PENDING")
@@ -267,7 +278,7 @@ class XMLRPCRequestHandler (SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
(self.client_address[0], msg))
else:
self.logger.error("Error sending response (%s): %s" % \
- (type, msg))
+ (type, msg))
def finish(self):
# shut down the connection
@@ -276,6 +287,7 @@ class XMLRPCRequestHandler (SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
self.wfile.close()
self.rfile.close()
+
class XMLRPCServer (SocketServer.ThreadingMixIn, SSLServer,
XMLRPCDispatcher, object):
@@ -355,6 +367,7 @@ class XMLRPCServer (SocketServer.ThreadingMixIn, SSLServer,
def _get_require_auth(self):
return getattr(self.RequestHandlerClass, "require_auth", False)
+
def _set_require_auth(self, value):
self.RequestHandlerClass.require_auth = value
require_auth = property(_get_require_auth, _set_require_auth)
@@ -364,6 +377,7 @@ class XMLRPCServer (SocketServer.ThreadingMixIn, SSLServer,
return self.RequestHandlerClass.credentials
except AttributeError:
return dict()
+
def _set_credentials(self, value):
self.RequestHandlerClass.credentials = value
credentials = property(_get_credentials, _set_credentials)
@@ -375,7 +389,7 @@ class XMLRPCServer (SocketServer.ThreadingMixIn, SSLServer,
except AttributeError:
name = "unknown"
if hasattr(instance, 'plugins'):
- for pname, pinst in instance.plugins.iteritems():
+ for pname, pinst in list(instance.plugins.items()):
for mname in pinst.__rmi__:
xmname = "%s.%s" % (pname, mname)
fn = getattr(pinst, mname)
diff --git a/src/lib/Server/Admin/Backup.py b/src/lib/Server/Admin/Backup.py
index fefc9fc9e..9bd644ff9 100644
--- a/src/lib/Server/Admin/Backup.py
+++ b/src/lib/Server/Admin/Backup.py
@@ -5,6 +5,7 @@ import tarfile
import Bcfg2.Server.Admin
import Bcfg2.Options
+
class Backup(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Make a backup of the Bcfg2 repository"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin backup\n")
@@ -29,4 +30,4 @@ class Backup(Bcfg2.Server.Admin.MetadataCore):
out = tarfile.open(self.datastore + '/' + filename, mode=mode)
out.add(self.datastore, os.path.basename(self.datastore))
out.close()
- print "Archive %s was stored under %s" % (filename, self.datastore)
+ print("Archive %s was stored under %s" % (filename, self.datastore))
diff --git a/src/lib/Server/Admin/Bundle.py b/src/lib/Server/Admin/Bundle.py
index 96a7ba59d..9b2a71783 100644
--- a/src/lib/Server/Admin/Bundle.py
+++ b/src/lib/Server/Admin/Bundle.py
@@ -6,11 +6,11 @@ import Bcfg2.Server.Admin
import Bcfg2.Options
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Bundle(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create or delete bundle entries"
- __longhelp__ = (__shorthelp__ + #"\n\nbcfg2-admin bundle add <bundle> "
- #"\n\nbcfg2-admin bundle del <bundle>"
- "\n\nbcfg2-admin bundle list-xml"
+ # TODO: add/del functions
+ __longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin bundle list-xml"
"\nbcfg2-admin bundle list-genshi"
"\nbcfg2-admin bundle show\n")
__usage__ = ("bcfg2-admin bundle [options] [add|del] [group]")
@@ -21,7 +21,7 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
def __call__(self, args):
Bcfg2.Server.Admin.MetadataCore.__call__(self, args)
- reg='((?:[a-z][a-z\\.\\d\\-]+)\\.(?:[a-z][a-z\\-]+))(?![\\w\\.])'
+ reg = '((?:[a-z][a-z\\.\\d\\-]+)\\.(?:[a-z][a-z\\-]+))(?![\\w\\.])'
# Get all bundles out of the Bundle/ directory
opts = {'repo': Bcfg2.Options.SERVER_REPOSITORY}
@@ -38,31 +38,31 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
# try:
# self.metadata.add_bundle(args[1])
# except MetadataConsistencyError:
-# print "Error in adding bundle."
+# print("Error in adding bundle.")
# raise SystemExit(1)
# elif args[0] in ['delete', 'remove', 'del', 'rm']:
# try:
# self.metadata.remove_bundle(args[1])
# except MetadataConsistencyError:
-# print "Error in deleting bundle."
+# print("Error in deleting bundle.")
# raise SystemExit(1)
# Lists all available xml bundles
elif args[0] in ['list-xml', 'ls-xml']:
bundle_name = []
for bundle_path in xml_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
for bundle in bundle_name:
- print bundle.split('.')[0]
+ print(bundle.split('.')[0])
# Lists all available genshi bundles
elif args[0] in ['list-genshi', 'ls-gen']:
bundle_name = []
for bundle_path in genshi_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
for bundle in bundle_name:
- print bundle.split('.')[0]
- # Shows a list of all available bundles and prints bundle
+ print(bundle.split('.')[0])
+ # Shows a list of all available bundles and prints bundle
# details after the user choose one bundle.
# FIXME: Add support for detailed output of genshi bundles
# FIXME: This functionality is almost identical with
@@ -71,32 +71,34 @@ class Bundle(Bcfg2.Server.Admin.MetadataCore):
bundle_name = []
bundle_list = xml_list + genshi_list
for bundle_path in bundle_list:
- rg = re.compile(reg,re.IGNORECASE|re.DOTALL)
+ rg = re.compile(reg, re.IGNORECASE | re.DOTALL)
bundle_name.append(rg.search(bundle_path).group(1))
text = "Available bundles (Number of bundles: %s)" % \
(len(bundle_list))
- print text
- print "%s" % (len(text) * "-")
+ print(text)
+ print("%s" % (len(text) * "-"))
for i in range(len(bundle_list)):
- print "[%i]\t%s" % (i, bundle_name[i])
- print "Enter the line number of a bundle for details:",
- lineno = raw_input()
+ print("[%i]\t%s" % (i, bundle_name[i]))
+ try:
+ lineno = raw_input("Enter the line number of a bundle for details: ")
+ except NameError:
+ lineno = input("Enter the line number of a bundle for details: ")
if int(lineno) >= int(len(bundle_list)):
- print "No line with this number."
+ print("No line with this number.")
else:
if '%s/Bundler/%s' % \
(repo, bundle_name[int(lineno)]) in genshi_list:
- print "Detailed output for *.genshi bundles is not supported."
+ print("Detailed output for *.genshi bundles is not supported.")
else:
- print 'Details for the "%s" bundle:' % \
- (bundle_name[int(lineno)].split('.')[0])
+ print('Details for the "%s" bundle:' % \
+ (bundle_name[int(lineno)].split('.')[0]))
tree = lxml.etree.parse(bundle_list[int(lineno)])
#Prints bundle content
- #print lxml.etree.tostring(tree)
+ #print(lxml.etree.tostring(tree))
names = ['Action', 'Package', 'Path', 'Service']
for name in names:
for node in tree.findall("//" + name):
- print "%s:\t%s" % (name, node.attrib["name"])
+ print("%s:\t%s" % (name, node.attrib["name"]))
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Client.py b/src/lib/Server/Admin/Client.py
index 08bd34151..3af25b15a 100644
--- a/src/lib/Server/Admin/Client.py
+++ b/src/lib/Server/Admin/Client.py
@@ -2,6 +2,7 @@ import lxml.etree
import Bcfg2.Server.Admin
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Client(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create, delete, or modify client entries"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin client add <client> "
@@ -27,13 +28,13 @@ class Client(Bcfg2.Server.Admin.MetadataCore):
attr, val = i.split('=', 1)
if attr not in ['profile', 'uuid', 'password',
'location', 'secure', 'address']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.add_client(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in adding client"
+ print("Error in adding client")
raise SystemExit(1)
elif args[0] in ['update', 'up']:
attr_d = {}
@@ -41,24 +42,24 @@ class Client(Bcfg2.Server.Admin.MetadataCore):
attr, val = i.split('=', 1)
if attr not in ['profile', 'uuid', 'password',
'location', 'secure', 'address']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.update_client(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in updating client"
+ print("Error in updating client")
raise SystemExit(1)
elif args[0] in ['delete', 'remove', 'del', 'rm']:
try:
self.metadata.remove_client(args[1])
except MetadataConsistencyError:
- print "Error in deleting client"
+ print("Error in deleting client")
raise SystemExit(1)
elif args[0] in ['list', 'ls']:
tree = lxml.etree.parse(self.metadata.data + "/clients.xml")
for node in tree.findall("//Client"):
- print node.attrib["name"]
+ print(node.attrib["name"])
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Compare.py b/src/lib/Server/Admin/Compare.py
index f97233b0e..4c751b55a 100644
--- a/src/lib/Server/Admin/Compare.py
+++ b/src/lib/Server/Admin/Compare.py
@@ -1,6 +1,9 @@
-import lxml.etree, os
+import lxml.etree
+import os
+
import Bcfg2.Server.Admin
+
class Compare(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("Determine differences between files or "
"directories of client specification instances")
@@ -11,30 +14,30 @@ class Compare(Bcfg2.Server.Admin.Mode):
def __init__(self, configfile):
Bcfg2.Server.Admin.Mode.__init__(self, configfile)
- self.important = {'Package':['name', 'version'],
- 'Service':['name', 'status'],
- 'Directory':['name', 'owner', 'group', 'perms'],
- 'SymLink':['name', 'to'],
- 'ConfigFile':['name', 'owner', 'group', 'perms'],
- 'Permissions':['name', 'perms'],
- 'PostInstall':['name']}
+ self.important = {'Package': ['name', 'version'],
+ 'Service': ['name', 'status'],
+ 'Directory': ['name', 'owner', 'group', 'perms'],
+ 'SymLink': ['name', 'to'],
+ 'ConfigFile': ['name', 'owner', 'group', 'perms'],
+ 'Permissions': ['name', 'perms'],
+ 'PostInstall': ['name']}
def compareStructures(self, new, old):
for child in new.getchildren():
equiv = old.xpath('%s[@name="%s"]' %
(child.tag, child.get('name')))
if child.tag in self.important:
- print "tag type %s not handled" % (child.tag)
+ print("tag type %s not handled" % (child.tag))
continue
if len(equiv) == 0:
- print ("didn't find matching %s %s" %
- (child.tag, child.get('name')))
+ print("didn't find matching %s %s" %
+ (child.tag, child.get('name')))
continue
elif len(equiv) >= 1:
if child.tag == 'ConfigFile':
if child.text != equiv[0].text:
- print " %s %s contents differ" \
- % (child.tag, child.get('name'))
+ print(" %s %s contents differ" \
+ % (child.tag, child.get('name')))
continue
noattrmatch = [field for field in self.important[child.tag] if \
child.get(field) != equiv[0].get(field)]
@@ -42,8 +45,8 @@ class Compare(Bcfg2.Server.Admin.Mode):
new.remove(child)
old.remove(equiv[0])
else:
- print " %s %s attributes %s do not match" % \
- (child.tag, child.get('name'), noattrmatch)
+ print(" %s %s attributes %s do not match" % \
+ (child.tag, child.get('name'), noattrmatch))
if len(old.getchildren()) == 0 and len(new.getchildren()) == 0:
return True
if new.tag == 'Independent':
@@ -59,24 +62,26 @@ class Compare(Bcfg2.Server.Admin.Mode):
newl.remove(entry)
oldl.remove(entry)
for entry in both:
- print " %s differs (in bundle %s)" % (entry, name)
+ print(" %s differs (in bundle %s)" % (entry, name))
for entry in oldl:
- print " %s only in old configuration (in bundle %s)" % (entry, name)
+ print(" %s only in old configuration (in bundle %s)" % (entry,
+ name))
for entry in newl:
- print " %s only in new configuration (in bundle %s)" % (entry, name)
+ print(" %s only in new configuration (in bundle %s)" % (entry,
+ name))
return False
def compareSpecifications(self, path1, path2):
try:
new = lxml.etree.parse(path1).getroot()
except IOError:
- print "Failed to read %s" % (path1)
+ print("Failed to read %s" % (path1))
raise SystemExit(1)
try:
old = lxml.etree.parse(path2).getroot()
except IOError:
- print "Failed to read %s" % (path2)
+ print("Failed to read %s" % (path2))
raise SystemExit(1)
for src in [new, old]:
@@ -88,7 +93,7 @@ class Compare(Bcfg2.Server.Admin.Mode):
for bundle in new.findall('./Bundle'):
equiv = old.xpath('Bundle[@name="%s"]' % (bundle.get('name')))
if len(equiv) == 0:
- print "couldnt find matching bundle for %s" % bundle.get('name')
+ print("couldnt find matching bundle for %s" % bundle.get('name'))
continue
if len(equiv) == 1:
if self.compareStructures(bundle, equiv[0]):
@@ -98,7 +103,7 @@ class Compare(Bcfg2.Server.Admin.Mode):
else:
rcs.append(False)
else:
- print "Unmatched bundle %s" % (bundle.get('name'))
+ print("Unmatched bundle %s" % (bundle.get('name')))
rcs.append(False)
i1 = new.find('./Independent')
i2 = old.find('./Independent')
@@ -120,18 +125,18 @@ class Compare(Bcfg2.Server.Admin.Mode):
(oldd, newd) = args
(old, new) = [os.listdir(spot) for spot in args]
for item in old:
- print "Entry:", item
+ print("Entry:", item)
state = self.__call__([oldd + '/' + item, newd + '/' + item])
new.remove(item)
if state:
- print "Entry:", item, "good"
+ print("Entry:", item, "good")
else:
- print "Entry:", item, "bad"
+ print("Entry:", item, "bad")
if new:
- print "new has extra entries", new
+ print("new has extra entries", new)
return
try:
(old, new) = args
except IndexError:
- print self.__call__.__doc__
+ print(self.__call__.__doc__)
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Group.py b/src/lib/Server/Admin/Group.py
index 4b2db28ec..1c5d0c12f 100644
--- a/src/lib/Server/Admin/Group.py
+++ b/src/lib/Server/Admin/Group.py
@@ -2,6 +2,7 @@ import lxml.etree
import Bcfg2.Server.Admin
from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError
+
class Group(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Create, delete, or modify group entries"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin group add <group> "
@@ -28,13 +29,13 @@ class Group(Bcfg2.Server.Admin.MetadataCore):
if attr not in ['profile', 'public', 'default',
'name', 'auth', 'toolset', 'category',
'comment']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.add_group(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in adding group"
+ print("Error in adding group")
raise SystemExit(1)
elif args[0] in ['update', 'up']:
attr_d = {}
@@ -43,24 +44,24 @@ class Group(Bcfg2.Server.Admin.MetadataCore):
if attr not in ['profile', 'public', 'default',
'name', 'auth', 'toolset', 'category',
'comment']:
- print "Attribute %s unknown" % attr
+ print("Attribute %s unknown" % attr)
raise SystemExit(1)
attr_d[attr] = val
try:
self.metadata.update_group(args[1], attr_d)
except MetadataConsistencyError:
- print "Error in updating group"
+ print("Error in updating group")
raise SystemExit(1)
elif args[0] in ['delete', 'remove', 'del', 'rm']:
try:
self.metadata.remove_group(args[1])
except MetadataConsistencyError:
- print "Error in deleting group"
+ print("Error in deleting group")
raise SystemExit(1)
elif args[0] in ['list', 'ls']:
tree = lxml.etree.parse(self.metadata.data + "/groups.xml")
for node in tree.findall("//Group"):
- print node.attrib["name"]
+ print(node.attrib["name"])
else:
- print "No command specified"
+ print("No command specified")
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Init.py b/src/lib/Server/Admin/Init.py
index 8f54b836e..fff8bcd1c 100644
--- a/src/lib/Server/Admin/Init.py
+++ b/src/lib/Server/Admin/Init.py
@@ -2,7 +2,9 @@ import getpass
import os
import random
import socket
+import stat
import string
+import sys
import subprocess
import Bcfg2.Server.Admin
import Bcfg2.Server.Plugin
@@ -137,21 +139,27 @@ def create_key(hostname, keypath, certpath, country, state, location):
keypath,
certpath))
subprocess.call((ccstr), shell=True)
- os.chmod(keypath, 0600)
+ os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600
-def create_conf(confpath, confdata):
+def create_conf(confpath, confdata, keypath):
# Don't overwrite existing bcfg2.conf file
if os.path.exists(confpath):
- result = raw_input("\nWarning: %s already exists. "
- "Overwrite? [y/N]: " % confpath)
+ # py3k compatibility
+ try:
+ result = raw_input("\nWarning: %s already exists. "
+ "Overwrite? [y/N]: " % confpath)
+ except NameError:
+ result = input("\nWarning: %s already exists. "
+ "Overwrite? [y/N]: " % confpath)
if result not in ['Y', 'y']:
print("Leaving %s unchanged" % confpath)
return
try:
open(confpath, "w").write(confdata)
- os.chmod(confpath, 0600)
- except Exception, e:
+ os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600
+ except Exception:
+ e = sys.exc_info()[1]
print("Error %s occured while trying to write configuration "
"file to '%s'.\n" %
(e, confpath))
@@ -204,7 +212,12 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_hostname(self):
"""Ask for the server hostname."""
- data = raw_input("What is the server's hostname [%s]: " %
+ # py3k compatibility
+ try:
+ data = raw_input("What is the server's hostname [%s]: " %
+ socket.getfqdn())
+ except NameError:
+ data = input("What is the server's hostname [%s]: " %
socket.getfqdn())
if data != '':
self.shostname = data
@@ -213,21 +226,36 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_config(self):
"""Ask for the configuration file path."""
- newconfig = raw_input("Store Bcfg2 configuration in [%s]: " %
- self.configfile)
+ # py3k compatibility
+ try:
+ newconfig = raw_input("Store Bcfg2 configuration in [%s]: " %
+ self.configfile)
+ except NameError:
+ newconfig = input("Store Bcfg2 configuration in [%s]: " %
+ self.configfile)
if newconfig != '':
self.configfile = newconfig
def _prompt_repopath(self):
"""Ask for the repository path."""
while True:
- newrepo = raw_input("Location of Bcfg2 repository [%s]: " %
- self.repopath)
+ # py3k compatibility
+ try:
+ newrepo = raw_input("Location of Bcfg2 repository [%s]: " %
+ self.repopath)
+ except NameError:
+ newrepo = input("Location of Bcfg2 repository [%s]: " %
+ self.repopath)
if newrepo != '':
self.repopath = newrepo
if os.path.isdir(self.repopath):
- response = raw_input("Directory %s exists. Overwrite? [y/N]:" \
- % self.repopath)
+ # py3k compatibility
+ try:
+ response = raw_input("Directory %s exists. Overwrite? [y/N]:" \
+ % self.repopath)
+ except NameError:
+ response = input("Directory %s exists. Overwrite? [y/N]:" \
+ % self.repopath)
if response.lower().strip() == 'y':
break
else:
@@ -243,8 +271,13 @@ class Init(Bcfg2.Server.Admin.Mode):
def _prompt_server(self):
"""Ask for the server name."""
- newserver = raw_input("Input the server location [%s]: " %
- self.server_uri)
+ # py3k compatibility
+ try:
+ newserver = raw_input("Input the server location [%s]: " %
+ self.server_uri)
+ except NameError:
+ newserver = input("Input the server location [%s]: " %
+ self.server_uri)
if newserver != '':
self.server_uri = newserver
@@ -256,51 +289,81 @@ class Init(Bcfg2.Server.Admin.Mode):
prompt += ': '
while True:
try:
- self.os_sel = os_list[int(raw_input(prompt))-1][1]
+ # py3k compatibility
+ try:
+ osidx = int(raw_input(prompt))
+ except NameError:
+ osidx = int(input(prompt))
+ self.os_sel = os_list[osidx - 1][1]
break
except ValueError:
continue
def _prompt_plugins(self):
- default = raw_input("Use default plugins? (%s) [Y/n]: " %
+ # py3k compatibility
+ try:
+ default = raw_input("Use default plugins? (%s) [Y/n]: " %
+ ''.join(default_plugins)).lower()
+ except NameError:
+ default = input("Use default plugins? (%s) [Y/n]: " %
''.join(default_plugins)).lower()
if default != 'y' or default != '':
while True:
plugins_are_valid = True
- plug_str = raw_input("Specify plugins: ")
+ # py3k compatibility
+ try:
+ plug_str = raw_input("Specify plugins: ")
+ except NameError:
+ plug_str = input("Specify plugins: ")
plugins = plug_str.split(',')
for plugin in plugins:
plugin = plugin.strip()
if not plugin in plugin_list:
plugins_are_valid = False
- print "ERROR: Plugin %s not recognized" % plugin
+ print("ERROR: Plugin %s not recognized" % plugin)
if plugins_are_valid:
break
def _prompt_certificate(self):
"""Ask for the key details (country, state, and location)."""
- print "The following questions affect SSL certificate generation."
- print "If no data is provided, the default values are used."
- newcountry = raw_input("Country name (2 letter code) for certificate: ")
+ print("The following questions affect SSL certificate generation.")
+ print("If no data is provided, the default values are used.")
+ # py3k compatibility
+ try:
+ newcountry = raw_input("Country name (2 letter code) for certificate: ")
+ except NameError:
+ newcountry = input("Country name (2 letter code) for certificate: ")
if newcountry != '':
if len(newcountry) == 2:
self.country = newcountry
else:
while len(newcountry) != 2:
- newcountry = raw_input("2 letter country code (eg. US): ")
+ # py3k compatibility
+ try:
+ newcountry = raw_input("2 letter country code (eg. US): ")
+ except NameError:
+ newcountry = input("2 letter country code (eg. US): ")
if len(newcountry) == 2:
self.country = newcountry
break
else:
self.country = 'US'
- newstate = raw_input("State or Province Name (full name) for certificate: ")
+ # py3k compatibility
+ try:
+ newstate = raw_input("State or Province Name (full name) for certificate: ")
+ except NameError:
+ newstate = input("State or Province Name (full name) for certificate: ")
if newstate != '':
self.state = newstate
else:
self.state = 'Illinois'
- newlocation = raw_input("Locality Name (eg, city) for certificate: ")
+ # py3k compatibility
+ try:
+ newlocation = raw_input("Locality Name (eg, city) for certificate: ")
+ except NameError:
+ newlocation = input("Locality Name (eg, city) for certificate: ")
if newlocation != '':
self.location = newlocation
else:
@@ -320,7 +383,8 @@ class Init(Bcfg2.Server.Admin.Mode):
'', ["Bcfg2.Server.Plugins"])
cls = getattr(module, plugin)
cls.init_repo(self.repopath)
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
print("Plugin setup for %s failed: %s\n"
"Check that dependencies are installed?" % (plugin, e))
@@ -338,7 +402,7 @@ class Init(Bcfg2.Server.Admin.Mode):
self.server_uri)
# Create the configuration file and SSL key
- create_conf(self.configfile, confdata)
+ create_conf(self.configfile, confdata, keypath)
kpath = keypath + '/bcfg2.key'
cpath = keypath + '/bcfg2.crt'
create_key(self.shostname, kpath, cpath, self.country,
@@ -349,6 +413,6 @@ class Init(Bcfg2.Server.Admin.Mode):
try:
os.makedirs(path)
self._init_plugins()
- print "Repository created successfuly in %s" % (self.repopath)
+ print("Repository created successfuly in %s" % (self.repopath))
except OSError:
print("Failed to create %s." % path)
diff --git a/src/lib/Server/Admin/Perf.py b/src/lib/Server/Admin/Perf.py
index 095180592..af1c83072 100644
--- a/src/lib/Server/Admin/Perf.py
+++ b/src/lib/Server/Admin/Perf.py
@@ -1,8 +1,9 @@
+import sys
+
import Bcfg2.Options
import Bcfg2.Proxy
import Bcfg2.Server.Admin
-import sys
class Perf(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("Query server for performance data")
@@ -27,11 +28,11 @@ class Perf(Bcfg2.Server.Admin.Mode):
proxy = Bcfg2.Proxy.ComponentProxy(setup['server'],
setup['user'],
setup['password'],
- key = setup['key'],
- cert = setup['certificate'],
- ca = setup['ca'])
+ key=setup['key'],
+ cert=setup['certificate'],
+ ca=setup['ca'])
data = proxy.get_statistics()
- for key, value in data.iteritems():
+ for key, value in list(data.items()):
data = tuple(["%.06f" % (item) for item in value[:-1]] + [value[-1]])
output.append((key, ) + data)
self.print_table(output)
diff --git a/src/lib/Server/Admin/Pull.py b/src/lib/Server/Admin/Pull.py
index 926eda1b3..47a8be253 100644
--- a/src/lib/Server/Admin/Pull.py
+++ b/src/lib/Server/Admin/Pull.py
@@ -1,7 +1,9 @@
import getopt
import sys
+
import Bcfg2.Server.Admin
+
class Pull(Bcfg2.Server.Admin.MetadataCore):
"""Pull mode retrieves entries from clients and
integrates the information into the repository.
@@ -38,7 +40,7 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
try:
opts, gargs = getopt.getopt(args, 'vfIs')
except:
- print self.__shorthelp__
+ print(self.__shorthelp__)
raise SystemExit(1)
for opt in opts:
if opt[0] == '-v':
@@ -55,18 +57,20 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
try:
self.PullEntry(*line.split(None, 3))
except SystemExit:
- print " for %s" % line
+ print(" for %s" % line)
except:
- print "Bad entry: %s" % line.strip()
+ print("Bad entry: %s" % line.strip())
elif len(gargs) < 3:
- print self.__longhelp__
+ print(self.__longhelp__)
raise SystemExit(1)
else:
self.PullEntry(gargs[0], gargs[1], gargs[2])
def BuildNewEntry(self, client, etype, ename):
- """Construct a new full entry for given client/entry from statistics."""
- new_entry = {'type':etype, 'name':ename}
+ """Construct a new full entry for
+ given client/entry from statistics.
+ """
+ new_entry = {'type': etype, 'name': ename}
for plugin in self.bcore.pull_sources:
try:
(owner, group, perms, contents) = \
@@ -74,16 +78,19 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
break
except Bcfg2.Server.Plugin.PluginExecutionError:
if plugin == self.bcore.pull_sources[-1]:
- print "Pull Source failure; could not fetch current state"
+ print("Pull Source failure; could not fetch current state")
raise SystemExit(1)
try:
- data = {'owner':owner, 'group':group, 'perms':perms, 'text':contents}
+ data = {'owner': owner,
+ 'group': group,
+ 'perms': perms,
+ 'text': contents}
except UnboundLocalError:
print("Unable to build entry. "
"Do you have a statistics plugin enabled?")
raise SystemExit(1)
- for k, v in data.iteritems():
+ for k, v in list(data.items()):
if v:
new_entry[k] = v
#print new_entry
@@ -93,17 +100,22 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
"""Determine where to put pull data."""
if self.mode == 'interactive':
for choice in choices:
- print "Plugin returned choice:"
+ print("Plugin returned choice:")
if id(choice) == id(choices[0]):
- print "(current entry)",
+ print("(current entry) ")
if choice.all:
- print " => global entry"
+ print(" => global entry")
elif choice.group:
- print (" => group entry: %s (prio %d)" %
- (choice.group, choice.prio))
+ print(" => group entry: %s (prio %d)" %
+ (choice.group, choice.prio))
else:
- print " => host entry: %s" % (choice.hostname)
- if raw_input("Use this entry? [yN]: ") in ['y', 'Y']:
+ print(" => host entry: %s" % (choice.hostname))
+ # py3k compatibility
+ try:
+ ans = raw_input("Use this entry? [yN]: ") in ['y', 'Y']
+ except NameError:
+ ans = input("Use this entry? [yN]: ") in ['y', 'Y']
+ if ans:
return choice
return False
else:
@@ -136,7 +148,7 @@ class Pull(Bcfg2.Server.Admin.MetadataCore):
self.errExit("Configuration upload not supported by plugin %s" \
% (plugin.name))
# Commit if running under a VCS
- for vcsplugin in self.bcore.plugins.values():
+ for vcsplugin in list(self.bcore.plugins.values()):
if isinstance(vcsplugin, Bcfg2.Server.Plugin.Version):
files = "%s/%s" % (plugin.data, ename)
comment = 'file "%s" pulled from host %s' % (files, client)
diff --git a/src/lib/Server/Admin/Query.py b/src/lib/Server/Admin/Query.py
index b5af9bad2..9e1d7cc88 100644
--- a/src/lib/Server/Admin/Query.py
+++ b/src/lib/Server/Admin/Query.py
@@ -2,6 +2,7 @@ import logging
import Bcfg2.Logger
import Bcfg2.Server.Admin
+
class Query(Bcfg2.Server.Admin.Mode):
__shorthelp__ = "Query clients"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin query [-n] [-c] "
@@ -25,14 +26,15 @@ class Query(Bcfg2.Server.Admin.Mode):
self.bcore = Bcfg2.Server.Core.Core(self.get_repo_path(),
['Metadata', 'Probes'],
'foo', False, 'UTF-8')
- except Bcfg2.Server.Core.CoreInitError, msg:
+ except Bcfg2.Server.Core.CoreInitError:
+ msg = sys.exc_info()[1]
self.errExit("Core load failed because %s" % msg)
self.bcore.fam.handle_events_in_interval(1)
self.meta = self.bcore.metadata
def __call__(self, args):
Bcfg2.Server.Admin.Mode.__call__(self, args)
- clients = self.meta.clients.keys()
+ clients = list(self.meta.clients.keys())
filename_arg = False
filename = None
for arg in args:
@@ -48,7 +50,7 @@ class Query(Bcfg2.Server.Admin.Mode):
try:
k, v = arg.split('=')
except:
- print "Unknown argument %s" % arg
+ print("Unknown argument %s" % arg)
continue
if k == 'p':
nc = self.meta.get_client_names_by_profiles(v.split(','))
@@ -57,22 +59,22 @@ class Query(Bcfg2.Server.Admin.Mode):
# add probed groups (if present)
for conn in self.bcore.connectors:
if isinstance(conn, Bcfg2.Server.Plugins.Probes.Probes):
- for c, glist in conn.cgroups.items():
+ for c, glist in list(conn.cgroups.items()):
for g in glist:
if g in v.split(','):
nc.append(c)
else:
- print "One of g= or p= must be specified"
+ print("One of g= or p= must be specified")
raise SystemExit(1)
clients = [c for c in clients if c in nc]
if '-n' in args:
for client in clients:
- print client
+ print(client)
else:
- print ','.join(clients)
+ print(','.join(clients))
if '-f' in args:
f = open(filename, "w")
for client in clients:
f.write(client + "\n")
f.close()
- print "Wrote results to %s" % (filename)
+ print("Wrote results to %s" % (filename))
diff --git a/src/lib/Server/Admin/Reports.py b/src/lib/Server/Admin/Reports.py
index a4dd19064..942477a49 100644
--- a/src/lib/Server/Admin/Reports.py
+++ b/src/lib/Server/Admin/Reports.py
@@ -1,7 +1,6 @@
'''Admin interface for dynamic reports'''
import Bcfg2.Logger
import Bcfg2.Server.Admin
-import ConfigParser
import datetime
import os
import logging
@@ -14,6 +13,9 @@ from Bcfg2.Server.Reports.updatefix import update_database
from Bcfg2.Server.Reports.utils import *
from lxml.etree import XML, XMLSyntaxError
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
+
# FIXME: Remove when server python dep is 2.5 or greater
if sys.version_info >= (2, 5):
from hashlib import md5
@@ -26,7 +28,8 @@ import django.core.management
# FIXME - settings file uses a hardcoded path for /etc/bcfg2.conf
try:
import Bcfg2.Server.Reports.settings
-except Exception, e:
+except Exception:
+ e = sys.exc_info()[1]
sys.stderr.write("Failed to load configuration settings. %s\n" % e)
sys.exit(1)
@@ -42,7 +45,8 @@ from django.db import connection, transaction
from Bcfg2.Server.Reports.reports.models import Client, Interaction, Entries, \
Entries_interactions, Performance, \
- Reason, Ping, TYPE_CHOICES, InternalDatabaseVersion
+ Reason, Ping
+
def printStats(fn):
"""
@@ -50,24 +54,29 @@ def printStats(fn):
Decorator for purging. Prints database statistics after a run.
"""
- def print_stats(*data):
+ def print_stats(self, *data):
start_client = Client.objects.count()
start_i = Interaction.objects.count()
start_ei = Entries_interactions.objects.count()
start_perf = Performance.objects.count()
start_ping = Ping.objects.count()
- fn(*data)
+ fn(self, *data)
- print "Clients removed: %s" % (start_client - Client.objects.count())
- print "Interactions removed: %s" % (start_i - Interaction.objects.count())
- print "Interactions->Entries removed: %s" % \
- (start_ei - Entries_interactions.objects.count())
- print "Metrics removed: %s" % (start_perf - Performance.objects.count())
- print "Ping metrics removed: %s" % (start_ping - Ping.objects.count())
+ self.log.info("Clients removed: %s" %
+ (start_client - Client.objects.count()))
+ self.log.info("Interactions removed: %s" %
+ (start_i - Interaction.objects.count()))
+ self.log.info("Interactions->Entries removed: %s" %
+ (start_ei - Entries_interactions.objects.count()))
+ self.log.info("Metrics removed: %s" %
+ (start_perf - Performance.objects.count()))
+ self.log.info("Ping metrics removed: %s" %
+ (start_ping - Ping.objects.count()))
return print_stats
+
class Reports(Bcfg2.Server.Admin.Mode):
'''Admin interface for dynamic reports'''
__shorthelp__ = "Manage dynamic reports"
@@ -93,7 +102,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
def __init__(self, cfile):
Bcfg2.Server.Admin.Mode.__init__(self, cfile)
self.log.setLevel(logging.INFO)
- self.django_commands = [ 'syncdb', 'sqlall', 'validate' ]
+ self.django_commands = ['syncdb', 'sqlall', 'validate']
self.__usage__ = self.__usage__ + " Django commands:\n " + \
"\n ".join(self.django_commands)
@@ -123,54 +132,54 @@ class Reports(Bcfg2.Server.Admin.Mode):
update_database()
elif args[0] == 'load_stats':
quick = '-O3' in args
- stats_file=None
- clients_file=None
- i=1
+ stats_file = None
+ clients_file = None
+ i = 1
while i < len(args):
if args[i] == '-s' or args[i] == '--stats':
- stats_file = args[i+1]
+ stats_file = args[i + 1]
if stats_file[0] == '-':
self.errExit("Invalid statistics file: %s" % stats_file)
elif args[i] == '-c' or args[i] == '--clients-file':
- clients_file = args[i+1]
+ clients_file = args[i + 1]
if clients_file[0] == '-':
self.errExit("Invalid clients file: %s" % clients_file)
i = i + 1
self.load_stats(stats_file, clients_file, verb, quick)
elif args[0] == 'purge':
- expired=False
- client=None
- maxdate=None
- state=None
- i=1
+ expired = False
+ client = None
+ maxdate = None
+ state = None
+ i = 1
while i < len(args):
if args[i] == '-c' or args[i] == '--client':
if client:
self.errExit("Only one client per run")
- client = args[i+1]
- print client
+ client = args[i + 1]
+ print(client)
i = i + 1
elif args[i] == '--days':
if maxdate:
self.errExit("Max date specified multiple times")
try:
- maxdate = datetime.datetime.now() - datetime.timedelta(days=int(args[i+1]))
+ 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.log.error("Invalid number of days: %s" % args[i + 1])
+ raise SystemExit(-1)
i = i + 1
elif args[i] == '--expired':
- expired=True
+ expired = True
i = i + 1
if expired:
if state:
self.log.error("--state is not valid with --expired")
- raise SystemExit, -1
+ raise SystemExit(-1)
self.purge_expired(maxdate)
else:
self.purge(client, maxdate, state)
else:
- print "Unknown command: %s" % args[0]
+ print("Unknown command: %s" % args[0])
@transaction.commit_on_success
def scrub(self):
@@ -179,11 +188,12 @@ class Reports(Bcfg2.Server.Admin.Mode):
# Currently only reasons are a problem
try:
start_count = Reason.objects.count()
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
self.log.error("Failed to load reason objects: %s" % e)
return
dup_reasons = []
-
+
cmp_reasons = dict()
batch_update = []
for reason in BatchFetch(Reason.objects):
@@ -192,7 +202,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
comparisons '''
id = reason.id
reason.id = None
- key=md5(pickle.dumps(reason)).hexdigest()
+ key = md5(pickle.dumps(reason)).hexdigest()
reason.id = id
if key in cmp_reasons:
@@ -203,14 +213,15 @@ class Reports(Bcfg2.Server.Admin.Mode):
else:
cmp_reasons[key] = reason.id
self.log.debug("key %d" % reason.id)
-
+
self.log.debug("Done with updates, deleting dupes")
try:
cursor = connection.cursor()
cursor.executemany('update reports_entries_interactions set reason_id=%s where reason_id=%s', batch_update)
cursor.executemany('delete from reports_reason where id = %s', dup_reasons)
transaction.set_dirty()
- except Exception, ex:
+ except Exception:
+ ex = sys.exc_info()[1]
self.log.error("Failed to delete reasons: %s" % ex)
raise
@@ -244,7 +255,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
try:
statsdata = XML(open(stats_file).read())
except (IOError, XMLSyntaxError):
- self.errExit("StatReports: Failed to parse %s"%(stats_file))
+ self.errExit("StatReports: Failed to parse %s" % (stats_file))
if not clientspath:
try:
@@ -255,10 +266,15 @@ class Reports(Bcfg2.Server.Admin.Mode):
try:
clientsdata = XML(open(clientspath).read())
except (IOError, XMLSyntaxError):
- self.errExit("StatReports: Failed to parse %s"%(clientspath))
+ self.errExit("StatReports: Failed to parse %s" % (clientspath))
try:
- load_stats(clientsdata, statsdata, verb, self.log, quick=quick, location=platform.node())
+ load_stats(clientsdata,
+ statsdata,
+ verb,
+ self.log,
+ quick=quick,
+ location=platform.node())
except:
pass
@@ -266,7 +282,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
def purge(self, client=None, maxdate=None, state=None):
'''Purge historical data from the database'''
- filtered = False # indicates whether or not a client should be deleted
+ filtered = False # indicates whether or not a client should be deleted
if not client and not maxdate and not state:
self.errExit("Reports.prune: Refusing to prune all data")
@@ -278,13 +294,13 @@ class Reports(Bcfg2.Server.Admin.Mode):
ipurge = ipurge.filter(client=cobj)
except Client.DoesNotExist:
self.log.error("Client %s not in database" % client)
- raise SystemExit, -1
+ raise SystemExit(-1)
self.log.debug("Filtering by client: %s" % client)
if maxdate:
filtered = True
if not isinstance(maxdate, datetime.datetime):
- raise TypeError, "maxdate is not a DateTime object"
+ raise TypeError("maxdate is not a DateTime object")
self.log.debug("Filtering by maxdate: %s" % maxdate)
ipurge = ipurge.filter(timestamp__lt=maxdate)
@@ -296,9 +312,9 @@ class Reports(Bcfg2.Server.Admin.Mode):
if state:
filtered = True
- if state not in ('dirty','clean','modified'):
- raise TypeError, "state is not one of the following values " + \
- "('dirty','clean','modified')"
+ if state not in ('dirty', 'clean', 'modified'):
+ raise TypeError("state is not one of the following values " + \
+ "('dirty','clean','modified')")
self.log.debug("Filtering by state: %s" % state)
ipurge = ipurge.filter(state=state)
@@ -323,6 +339,8 @@ class Reports(Bcfg2.Server.Admin.Mode):
# bulk operations bypass the Interaction.delete method
self.log.debug("Pruning orphan Performance objects")
Performance.prune_orphans()
+ self.log.debug("Pruning orphan Reason objects")
+ Reason.prune_orphans()
if client and not filtered:
'''Delete the client, ping data is automatic'''
@@ -342,7 +360,7 @@ class Reports(Bcfg2.Server.Admin.Mode):
if maxdate:
if not isinstance(maxdate, datetime.datetime):
- raise TypeError, "maxdate is not a DateTime object"
+ raise TypeError("maxdate is not a DateTime object")
self.log.debug("Filtering by maxdate: %s" % maxdate)
clients = Client.objects.filter(expiration__lt=maxdate)
else:
@@ -354,4 +372,3 @@ class Reports(Bcfg2.Server.Admin.Mode):
client.delete()
self.log.debug("Pruning orphan Performance objects")
Performance.prune_orphans()
-
diff --git a/src/lib/Server/Admin/Snapshots.py b/src/lib/Server/Admin/Snapshots.py
index d58873174..052545b61 100644
--- a/src/lib/Server/Admin/Snapshots.py
+++ b/src/lib/Server/Admin/Snapshots.py
@@ -8,6 +8,8 @@ import Bcfg2.Server.Snapshots
import Bcfg2.Server.Snapshots.model
from Bcfg2.Server.Snapshots.model import Snapshot, Client, Metadata, Base, \
File, Group, Package, Service
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import u_str
class Snapshots(Bcfg2.Server.Admin.Mode):
__shorthelp__ = "Interact with the Snapshots system"
@@ -71,7 +73,7 @@ class Snapshots(Bcfg2.Server.Admin.Mode):
session.commit()
elif args[0] == 'dump':
client = args[1]
- snap = Snapshot.get_current(self.session, unicode(client))
+ snap = Snapshot.get_current(self.session, u_str(client))
if not snap:
print("Current snapshot for %s not found" % client)
sys.exit(1)
@@ -105,7 +107,7 @@ class Snapshots(Bcfg2.Server.Admin.Mode):
print("Usage: bcfg2-admin snapshots -b <client>")
return
client = args[2]
- snap = Snapshot.get_current(self.session, unicode(client))
+ snap = Snapshot.get_current(self.session, u_str(client))
if not snap:
print("Current snapshot for %s not found" % client)
sys.exit(1)
@@ -128,7 +130,7 @@ class Snapshots(Bcfg2.Server.Admin.Mode):
elif '-e' in args[1:]:
# Query a single host for extra entries
client = args[2]
- snap = Snapshot.get_current(self.session, unicode(client))
+ snap = Snapshot.get_current(self.session, u_str(client))
if not snap:
print("Current snapshot for %s not found" % client)
sys.exit(1)
diff --git a/src/lib/Server/Admin/Tidy.py b/src/lib/Server/Admin/Tidy.py
index cc8ab4f5e..f79991fd9 100644
--- a/src/lib/Server/Admin/Tidy.py
+++ b/src/lib/Server/Admin/Tidy.py
@@ -4,6 +4,7 @@ import socket
import Bcfg2.Server.Admin
+
class Tidy(Bcfg2.Server.Admin.Mode):
__shorthelp__ = "Clean up useless files in the repo"
__longhelp__ = __shorthelp__ + "\n\nbcfg2-admin tidy [-f] [-I]\n"
@@ -24,17 +25,21 @@ class Tidy(Bcfg2.Server.Admin.Mode):
if '-f' in args or '-I' in args:
if '-I' in args:
for name in badfiles[:]:
- answer = raw_input("Unlink file %s? [yN] " % name)
+ # py3k compatibility
+ try:
+ answer = raw_input("Unlink file %s? [yN] " % name)
+ except NameError:
+ answer = input("Unlink file %s? [yN] " % name)
if answer not in ['y', 'Y']:
badfiles.remove(name)
for name in badfiles:
try:
os.unlink(name)
except IOError:
- print "Failed to unlink %s" % name
+ print("Failed to unlink %s" % name)
else:
for name in badfiles:
- print name
+ print(name)
def buildTidyList(self):
"""Clean up unused or unusable files from the repository."""
@@ -56,7 +61,8 @@ class Tidy(Bcfg2.Server.Admin.Mode):
bad.append(hostname)
for name in os.listdir("%s/SSHbase" % (self.get_repo_path())):
if not hostmatcher.match(name):
- to_remove.append("%s/SSHbase/%s" % (self.get_repo_path(), name))
+ to_remove.append("%s/SSHbase/%s" % (self.get_repo_path(),
+ name))
else:
if hostmatcher.match(name).group(1) in bad:
to_remove.append("%s/SSHbase/%s" %
diff --git a/src/lib/Server/Admin/Viz.py b/src/lib/Server/Admin/Viz.py
index e3daea84b..f39e6d7a8 100644
--- a/src/lib/Server/Admin/Viz.py
+++ b/src/lib/Server/Admin/Viz.py
@@ -1,7 +1,9 @@
import getopt
from subprocess import Popen, PIPE
+
import Bcfg2.Server.Admin
+
class Viz(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Produce graphviz diagrams of metadata structures"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin viz [--includehosts] "
@@ -27,7 +29,8 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
plugin_blacklist = ['DBStats', 'Snapshots', 'Cfg', 'Pkgmgr', 'Packages',
'Rules', 'Account', 'Decisions', 'Deps', 'Git', 'Svn',
- 'Fossil', 'Bzr', 'Bundler', 'TGenshi', 'SGenshi', 'Base']
+ 'Fossil', 'Bzr', 'Bundler', 'TGenshi', 'SGenshi',
+ 'Base']
def __init__(self, cfile):
@@ -42,8 +45,9 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
opts, args = getopt.getopt(args, 'Hbko:',
['includehosts', 'includebundles',
'includekey', 'outfile='])
- except getopt.GetoptError, msg:
- print msg
+ except getopt.GetoptError:
+ msg = sys.exc_info()[1]
+ print(msg)
#FIXME: is this for --raw?
#rset = False
@@ -63,8 +67,8 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
data = self.Visualize(self.get_repo_path(), hset, bset,
kset, outputfile)
- print data
- raise SystemExit, 0
+ print(data)
+ raise SystemExit(0)
def Visualize(self, repopath, hosts=False,
bundles=False, key=False, output=False):
@@ -82,7 +86,7 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
try:
dotpipe.stdin.write("digraph groups {\n")
except:
- print "write to dot process failed. Is graphviz installed?"
+ print("write to dot process failed. Is graphviz installed?")
raise SystemExit(1)
dotpipe.stdin.write('\trankdir="LR";\n')
dotpipe.stdin.write(self.metadata.viz(hosts, bundles,
diff --git a/src/lib/Server/Admin/Xcmd.py b/src/lib/Server/Admin/Xcmd.py
index 8ea98b79c..fd5794f88 100644
--- a/src/lib/Server/Admin/Xcmd.py
+++ b/src/lib/Server/Admin/Xcmd.py
@@ -1,9 +1,12 @@
+import sys
+
import Bcfg2.Options
import Bcfg2.Proxy
import Bcfg2.Server.Admin
-import sys
-import xmlrpclib
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import xmlrpclib
+
class Xcmd(Bcfg2.Server.Admin.Mode):
__shorthelp__ = ("XML-RPC Command Interface")
@@ -16,8 +19,8 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
'user': Bcfg2.Options.CLIENT_USER,
'password': Bcfg2.Options.SERVER_PASSWORD,
'key': Bcfg2.Options.SERVER_KEY,
- 'certificate' : Bcfg2.Options.CLIENT_CERT,
- 'ca' : Bcfg2.Options.CLIENT_CA
+ 'certificate': Bcfg2.Options.CLIENT_CERT,
+ 'ca': Bcfg2.Options.CLIENT_CA
}
setup = Bcfg2.Options.OptionParser(optinfo)
setup.parse(sys.argv[2:])
@@ -25,9 +28,10 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
proxy = Bcfg2.Proxy.ComponentProxy(setup['server'],
setup['user'],
setup['password'],
- key = setup['key'],
- cert = setup['certificate'],
- ca = setup['ca'], timeout=180)
+ key=setup['key'],
+ cert=setup['certificate'],
+ ca=setup['ca'],
+ timeout=180)
if len(setup['args']) == 0:
print("Usage: xcmd <xmlrpc method> <optional arguments>")
return
@@ -36,8 +40,9 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
if len(setup['args']) > 1:
args = tuple(setup['args'][1:])
try:
- data = apply(getattr(proxy, cmd), args)
- except xmlrpclib.Fault, flt:
+ data = getattr(proxy, cmd)(*args)
+ except xmlrpclib.Fault:
+ flt = sys.exc_info()[1]
if flt.faultCode == 7:
print("Unknown method %s" % cmd)
return
@@ -46,4 +51,4 @@ class Xcmd(Bcfg2.Server.Admin.Mode):
else:
raise
if data != None:
- print data
+ print(data)
diff --git a/src/lib/Server/Admin/__init__.py b/src/lib/Server/Admin/__init__.py
index dc3dc8c01..8915492a3 100644
--- a/src/lib/Server/Admin/__init__.py
+++ b/src/lib/Server/Admin/__init__.py
@@ -19,22 +19,26 @@ __all__ = [
'Xcmd'
]
-import ConfigParser
import logging
import lxml.etree
import sys
import Bcfg2.Server.Core
import Bcfg2.Options
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
+
class ModeOperationError(Exception):
pass
+
class Mode(object):
"""Help message has not yet been added for mode."""
__shorthelp__ = 'Shorthelp not defined yet'
__longhelp__ = 'Longhelp not defined yet'
__args__ = []
+
def __init__(self, configfile):
self.configfile = configfile
self.__cfp = False
@@ -50,11 +54,11 @@ class Mode(object):
def __call__(self, args):
if len(args) > 0 and args[0] == 'help':
- print self.__longhelp__
+ print(self.__longhelp__)
raise SystemExit(0)
def errExit(self, emsg):
- print emsg
+ print(emsg)
raise SystemExit(1)
def get_repo_path(self):
@@ -80,9 +84,9 @@ class Mode(object):
"""
hdelim = "="
- justify = {'left':str.ljust,
- 'center':str.center,
- 'right':str.rjust}[justify.lower()]
+ justify = {'left': str.ljust,
+ 'center': str.center,
+ 'right': str.rjust}[justify.lower()]
"""
Calculate column widths (longest item in each column
@@ -90,9 +94,9 @@ class Mode(object):
"""
cols = list(zip(*rows))
- colWidths = [max([len(str(item))+2*padding for \
+ colWidths = [max([len(str(item)) + 2 * padding for \
item in col]) for col in cols]
- borderline = vdelim.join([w*hdelim for w in colWidths])
+ borderline = vdelim.join([w * hdelim for w in colWidths])
# Print out the table
print(borderline)
@@ -103,6 +107,7 @@ class Mode(object):
print(borderline)
hdr = False
+
class MetadataCore(Mode):
"""Base class for admin-modes that handle metadata."""
def __init__(self, configfile, usage, pwhitelist=None, pblacklist=None):
@@ -113,17 +118,21 @@ class MetadataCore(Mode):
setup.hm = usage
setup.parse(sys.argv[1:])
if pwhitelist is not None:
- setup['plugins'] = [x for x in setup['plugins'] if x in pwhitelist]
+ setup['plugins'] = [x for x in setup['plugins']
+ if x in pwhitelist]
elif pblacklist is not None:
- setup['plugins'] = [x for x in setup['plugins'] if x not in pblacklist]
+ setup['plugins'] = [x for x in setup['plugins']
+ if x not in pblacklist]
try:
self.bcore = Bcfg2.Server.Core.Core(self.get_repo_path(),
setup['plugins'],
'foo', 'UTF-8')
- except Bcfg2.Server.Core.CoreInitError, msg:
+ except Bcfg2.Server.Core.CoreInitError:
+ msg = sys.exc_info()[1]
self.errExit("Core load failed because %s" % msg)
self.bcore.fam.handle_events_in_interval(5)
self.metadata = self.bcore.metadata
+
class StructureMode(MetadataCore):
pass
diff --git a/src/lib/Server/Core.py b/src/lib/Server/Core.py
index ac67b8a69..8f9d3e746 100644
--- a/src/lib/Server/Core.py
+++ b/src/lib/Server/Core.py
@@ -3,19 +3,28 @@ __revision__ = '$Revision$'
import atexit
import logging
-import lxml.etree
import select
+import sys
import threading
import time
-import xmlrpclib
+try:
+ import lxml.etree
+except ImportError:
+ print("Failed to import lxml dependency. Shutting down server.")
+ raise SystemExit(1)
from Bcfg2.Component import Component, exposed
from Bcfg2.Server.Plugin import PluginInitError, PluginExecutionError
import Bcfg2.Server.FileMonitor
import Bcfg2.Server.Plugins.Metadata
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import xmlrpclib
+if sys.hexversion >= 0x03000000:
+ from functools import reduce
logger = logging.getLogger('Bcfg2.Server.Core')
+
def critical_error(operation):
"""Log and err, traceback and return an xmlrpc fault to client."""
logger.error(operation, exc_info=1)
@@ -27,12 +36,16 @@ try:
except:
pass
+
class CoreInitError(Exception):
"""This error is raised when the core cannot be initialized."""
pass
+
class Core(Component):
- """The Core object is the container for all Bcfg2 Server logic and modules."""
+ """The Core object is the container for all
+ Bcfg2 Server logic and modules.
+ """
name = 'bcfg2-server'
implementation = 'bcfg2-server'
@@ -42,15 +55,16 @@ class Core(Component):
Component.__init__(self)
self.datastore = repo
if filemonitor not in Bcfg2.Server.FileMonitor.available:
- logger.error("File monitor driver %s not available; forcing to default" % filemonitor)
+ logger.error("File monitor driver %s not available; "
+ "forcing to default" % filemonitor)
filemonitor = 'default'
try:
self.fam = Bcfg2.Server.FileMonitor.available[filemonitor]()
except IOError:
logger.error("Failed to instantiate fam driver %s" % filemonitor,
exc_info=1)
- raise CoreInitError, "failed to instantiate fam driver (used %s)" % \
- filemonitor
+ raise CoreInitError("failed to instantiate fam driver (used %s)" % \
+ filemonitor)
self.pubspace = {}
self.cfile = cfile
self.cron = {}
@@ -70,44 +84,43 @@ class Core(Component):
if not plugin in self.plugins:
self.init_plugins(plugin)
# Remove blacklisted plugins
- for p, bl in self.plugin_blacklist.items():
+ for p, bl in list(self.plugin_blacklist.items()):
if len(bl) > 0:
logger.error("The following plugins conflict with %s;"
"Unloading %s" % (p, bl))
for plug in bl:
del self.plugins[plug]
# This section loads the experimental plugins
- expl = [plug for (name, plug) in self.plugins.iteritems()
+ expl = [plug for (name, plug) in list(self.plugins.items())
if plug.experimental]
if expl:
logger.info("Loading experimental plugin(s): %s" % \
(" ".join([x.name for x in expl])))
logger.info("NOTE: Interfaces subject to change")
- depr = [plug for (name, plug) in self.plugins.iteritems()
+ depr = [plug for (name, plug) in list(self.plugins.items())
if plug.deprecated]
# This section loads the deprecated plugins
if depr:
logger.info("Loading deprecated plugin(s): %s" % \
(" ".join([x.name for x in depr])))
-
- mlist = [p for p in self.plugins.values() if \
+ mlist = [p for p in list(self.plugins.values()) if \
isinstance(p, Bcfg2.Server.Plugin.Metadata)]
if len(mlist) == 1:
self.metadata = mlist[0]
else:
logger.error("No Metadata Plugin loaded; failed to instantiate Core")
- raise CoreInitError, "No Metadata Plugin"
- self.statistics = [plugin for plugin in self.plugins.values() if \
- isinstance(plugin, Bcfg2.Server.Plugin.Statistics)]
- self.pull_sources = [plugin for plugin in self.statistics if \
- isinstance(plugin, Bcfg2.Server.Plugin.PullSource)]
- self.generators = [plugin for plugin in self.plugins.values() if \
- isinstance(plugin, Bcfg2.Server.Plugin.Generator)]
- self.structures = [plugin for plugin in self.plugins.values() if \
- isinstance(plugin, Bcfg2.Server.Plugin.Structure)]
- self.connectors = [plugin for plugin in self.plugins.values() if \
- isinstance(plugin, Bcfg2.Server.Plugin.Connector)]
+ raise CoreInitError("No Metadata Plugin")
+ self.statistics = [plugin for plugin in list(self.plugins.values())
+ if isinstance(plugin, Bcfg2.Server.Plugin.Statistics)]
+ self.pull_sources = [plugin for plugin in self.statistics
+ if isinstance(plugin, Bcfg2.Server.Plugin.PullSource)]
+ self.generators = [plugin for plugin in list(self.plugins.values())
+ if isinstance(plugin, Bcfg2.Server.Plugin.Generator)]
+ self.structures = [plugin for plugin in list(self.plugins.values())
+ if isinstance(plugin, Bcfg2.Server.Plugin.Structure)]
+ self.connectors = [plugin for plugin in list(self.plugins.values())
+ if isinstance(plugin, Bcfg2.Server.Plugin.Connector)]
self.ca = ca
self.fam_thread = threading.Thread(target=self._file_monitor_thread)
if start_fam_thread:
@@ -128,7 +141,7 @@ class Core(Component):
except:
continue
# VCS plugin periodic updates
- for plugin in self.plugins.values():
+ for plugin in list(self.plugins.values()):
if isinstance(plugin, Bcfg2.Server.Plugin.Version):
self.revision = plugin.get_revision()
@@ -137,7 +150,7 @@ class Core(Component):
try:
mod = getattr(__import__("Bcfg2.Server.Plugins.%s" %
(plugin)).Server.Plugins, plugin)
- except ImportError, e:
+ except ImportError:
try:
mod = __import__(plugin)
except:
@@ -157,22 +170,23 @@ class Core(Component):
(plugin), exc_info=1)
def shutdown(self):
- """Shuting down the plugins."""
+ """Shutting down the plugins."""
if not self.terminate.isSet():
self.terminate.set()
- for plugin in self.plugins.values():
+ for plugin in list(self.plugins.values()):
plugin.shutdown()
def validate_data(self, metadata, data, base_cls):
"""Checks the data structure."""
- for plugin in self.plugins.values():
+ for plugin in list(self.plugins.values()):
if isinstance(plugin, base_cls):
try:
if base_cls == Bcfg2.Server.Plugin.StructureValidator:
plugin.validate_structures(metadata, data)
elif base_cls == Bcfg2.Server.Plugin.GoalValidator:
plugin.validate_goals(metadata, data)
- except Bcfg2.Server.Plugin.ValidationError, err:
+ except Bcfg2.Server.Plugin.ValidationError:
+ err = sys.exc_info()[1]
logger.error("Plugin %s structure validation failed: %s" \
% (plugin.name, err.message))
raise
@@ -182,7 +196,7 @@ class Core(Component):
def GetStructures(self, metadata):
"""Get all structures for client specified by metadata."""
- structures = reduce(lambda x, y:x+y,
+ structures = reduce(lambda x, y: x + y,
[struct.BuildStructures(metadata) for struct \
in self.structures], [])
sbundles = [b.get('name') for b in structures if b.tag == 'Bundle']
@@ -232,7 +246,8 @@ class Core(Component):
glist = [gen for gen in self.generators if
entry.get('name') in gen.Entries.get(entry.tag, {})]
if len(glist) == 1:
- return glist[0].Entries[entry.tag][entry.get('name')](entry, metadata)
+ return glist[0].Entries[entry.tag][entry.get('name')](entry,
+ metadata)
elif len(glist) > 1:
generators = ", ".join([gen.name for gen in glist])
logger.error("%s %s served by multiple generators: %s" % \
@@ -242,7 +257,7 @@ class Core(Component):
if len(g2list) == 1:
return g2list[0].HandleEntry(entry, metadata)
entry.set('failure', 'no matching generator')
- raise PluginExecutionError, (entry.tag, entry.get('name'))
+ raise PluginExecutionError(entry.tag, entry.get('name'))
def BuildConfiguration(self, client):
"""Build configuration for clients."""
@@ -290,7 +305,7 @@ class Core(Component):
def GetDecisions(self, metadata, mode):
"""Get data for the decision list."""
result = []
- for plugin in self.plugins.values():
+ for plugin in list(self.plugins.values()):
try:
if isinstance(plugin, Bcfg2.Server.Plugin.Decision):
result += plugin.GetDecisions(metadata, mode)
@@ -300,7 +315,7 @@ class Core(Component):
return result
def build_metadata(self, client_name):
- """Build the metadata structure."""
+ """Build the metadata structure."""
if not hasattr(self, 'metadata'):
# some threads start before metadata is even loaded
raise Bcfg2.Server.Plugins.Metadata.MetadataRuntimeError
diff --git a/src/lib/Server/FileMonitor.py b/src/lib/Server/FileMonitor.py
index 0f09f7751..d6b313e6b 100644
--- a/src/lib/Server/FileMonitor.py
+++ b/src/lib/Server/FileMonitor.py
@@ -7,6 +7,7 @@ from time import sleep, time
logger = logging.getLogger('Bcfg2.Server.FileMonitor')
+
def ShouldIgnore(event):
"""Test if the event should be suppresed."""
# FIXME should move event suppression out of the core
@@ -18,6 +19,7 @@ def ShouldIgnore(event):
return True
return False
+
class Event(object):
def __init__(self, request_id, filename, code):
self.requestID = request_id
@@ -29,6 +31,8 @@ class Event(object):
return self.action
available = {}
+
+
class FileMonitor(object):
"""File Monitor baseclass."""
def __init__(self, debug=False):
@@ -78,7 +82,7 @@ class FileMonitor(object):
if lock:
lock.release()
end = time()
- logger.info("Handled %d events in %.03fs" % (count, (end-start)))
+ logger.info("Handled %d events in %.03fs" % (count, (end - start)))
def handle_events_in_interval(self, interval):
end = time() + interval
@@ -91,7 +95,9 @@ class FileMonitor(object):
class FamFam(object):
- """The fam object is a set of callbacks for file alteration events (FAM support)."""
+ """The fam object is a set of callbacks for
+ file alteration events (FAM support).
+ """
def __init__(self):
object.__init__(self)
@@ -164,7 +170,6 @@ class FamFam(object):
return count
-
class Fam(FileMonitor):
"""
The fam object is a set of callbacks for
@@ -195,6 +200,7 @@ class Fam(FileMonitor):
def get_event(self):
return self.fm.nextEvent()
+
class Pseudo(FileMonitor):
"""
The fam object is a set of callbacks for
@@ -213,14 +219,16 @@ class Pseudo(FileMonitor):
def AddMonitor(self, path, obj):
"""add a monitor to path, installing a callback to obj.HandleEvent"""
- handleID = len(self.handles.keys())
+ handleID = len(list(self.handles.keys()))
mode = os.stat(path)[stat.ST_MODE]
handle = Event(handleID, path, 'exists')
if stat.S_ISDIR(mode):
dirList = os.listdir(path)
self.pending_events.append(handle)
for includedFile in dirList:
- self.pending_events.append(Event(handleID, includedFile, 'exists'))
+ self.pending_events.append(Event(handleID,
+ includedFile,
+ 'exists'))
self.pending_events.append(Event(handleID, path, 'endExist'))
else:
self.pending_events.append(Event(handleID, path, 'exists'))
diff --git a/src/lib/Server/Hostbase/ldapauth.py b/src/lib/Server/Hostbase/ldapauth.py
index f2148181f..21b462c86 100644
--- a/src/lib/Server/Hostbase/ldapauth.py
+++ b/src/lib/Server/Hostbase/ldapauth.py
@@ -1,16 +1,18 @@
-"""Checks with LDAP (ActiveDirectory) to see if the current user is an LDAP(AD) user,
-and returns a subset of the user's profile that is needed by Argonne/CIS to
-to set user level privleges in Django"""
-
-__revision__ = '$Revision: 2456 $'
+"""
+Checks with LDAP (ActiveDirectory) to see if the current user is an LDAP(AD)
+user, and returns a subset of the user's profile that is needed by Argonne/CIS
+to set user level privleges in Django
+"""
import os
import ldap
+
class LDAPAUTHError(Exception):
"""LDAPAUTHError is raised when somehting goes boom."""
pass
+
class ldapauth(object):
group_test = False
check_member_of = os.environ['LDAP_CHECK_MBR_OF_GRP']
@@ -20,35 +22,35 @@ class ldapauth(object):
telephoneNumber = None
title = None
memberOf = None
- department = None #this will be a list
+ department = None # this will be a list
mail = None
- extensionAttribute1 = None #badgenumber
+ extensionAttribute1 = None # badgenumber
badge_no = None
- def __init__(self,login,passwd):
+ def __init__(self, login, passwd):
"""get username (if using ldap as auth the
apache env var REMOTE_USER should be used)
from username get user profile from AD/LDAP
"""
#p = self.user_profile(login,passwd)
- d = self.user_dn(login) #success, distname
- print d[1]
+ d = self.user_dn(login) # success, distname
+ print(d[1])
if d[0] == 'success':
pass
- p = self.user_bind(d[1],passwd)
+ p = self.user_bind(d[1], passwd)
if p[0] == 'success':
#parse results
parsed = self.parse_results(p[2])
- print self.department
+ print(self.department)
self.group_test = self.member_of()
securitylevel = self.security_level()
- print "ACCESS LEVEL: " + str(securitylevel)
+ print("ACCESS LEVEL: " + str(securitylevel))
else:
raise LDAPAUTHError(p[2])
else:
raise LDAPAUTHError(p[2])
- def user_profile(self,login,passwd=None):
+ def user_profile(self, login, passwd=None):
"""NOT USED RIGHT NOW"""
ldap_login = "CN=%s" % login
svc_acct = os.environ['LDAP_SVC_ACCT_NAME']
@@ -60,33 +62,35 @@ class ldapauth(object):
try:
conn = ldap.initialize(os.environ['LDAP_URI'])
- conn.bind(svc_acct,svc_pass,ldap.AUTH_SIMPLE)
+ conn.bind(svc_acct, svc_pass, ldap.AUTH_SIMPLE)
result_id = conn.search(search_pth,
- ldap.SCOPE_SUBTREE,
- ldap_login,None)
- result_type,result_data = conn.result(result_id,0)
- return ('success','User profile found',result_data,)
- except ldap.LDAPError,e:
+ ldap.SCOPE_SUBTREE,
+ ldap_login,
+ None)
+ result_type, result_data = conn.result(result_id, 0)
+ return ('success', 'User profile found', result_data,)
+ except ldap.LDAPError, e:
#connection failed
- return ('error','LDAP connect failed',e,)
+ return ('error', 'LDAP connect failed', e,)
- def user_bind(self,distinguishedName,passwd):
+ def user_bind(self, distinguishedName, passwd):
"""Binds to LDAP Server"""
search_pth = os.environ['LDAP_SEARCH_PTH']
try:
conn = ldap.initialize(os.environ['LDAP_URI'])
- conn.bind(distinguishedName,passwd,ldap.AUTH_SIMPLE)
+ conn.bind(distinguishedName, passwd, ldap.AUTH_SIMPLE)
cn = distinguishedName.split(",")
result_id = conn.search(search_pth,
- ldap.SCOPE_SUBTREE,
- cn[0],None)
- result_type,result_data = conn.result(result_id,0)
- return ('success','User profile found',result_data,)
- except ldap.LDAPError,e:
+ ldap.SCOPE_SUBTREE,
+ cn[0],
+ None)
+ result_type, result_data = conn.result(result_id, 0)
+ return ('success', 'User profile found', result_data,)
+ except ldap.LDAPError, e:
#connection failed
- return ('error','LDAP connect failed',e,)
+ return ('error', 'LDAP connect failed', e,)
- def user_dn(self,cn):
+ def user_dn(self, cn):
"""Uses Service Account to get distinguishedName"""
ldap_login = "CN=%s" % cn
svc_acct = os.environ['LDAP_SVC_ACCT_NAME']
@@ -95,19 +99,20 @@ class ldapauth(object):
try:
conn = ldap.initialize(os.environ['LDAP_URI'])
- conn.bind(svc_acct,svc_pass,ldap.AUTH_SIMPLE)
+ conn.bind(svc_acct, svc_pass, ldap.AUTH_SIMPLE)
result_id = conn.search(search_pth,
- ldap.SCOPE_SUBTREE,
- ldap_login,None)
- result_type,result_data = conn.result(result_id,0)
+ ldap.SCOPE_SUBTREE,
+ ldap_login,
+ None)
+ result_type, result_data = conn.result(result_id, 0)
raw_obj = result_data[0][1]
distinguishedName = raw_obj['distinguishedName']
- return ('success',distinguishedName[0],)
- except ldap.LDAPError,e:
+ return ('success', distinguishedName[0],)
+ except ldap.LDAPError, e:
#connection failed
- return ('error','LDAP connect failed',e,)
+ return ('error', 'LDAP connect failed', e,)
- def parse_results(self,user_obj):
+ def parse_results(self, user_obj):
"""Clean up the huge ugly object handed to us in the LDAP query"""
#user_obj is a list formatted like this:
#[('LDAP_DN',{user_dict},),]
@@ -169,4 +174,3 @@ class ldapauth(object):
level = 4
return level
-
diff --git a/src/lib/Server/Hostbase/media/base.css b/src/lib/Server/Hostbase/media/base.css
index 9196c7d51..ddbf02165 100644
--- a/src/lib/Server/Hostbase/media/base.css
+++ b/src/lib/Server/Hostbase/media/base.css
@@ -1,5 +1,5 @@
-
-/* Import other styles */
-@import url('global.css');
-@import url('layout.css');
-@import url('boxypastel.css');
+
+/* Import other styles */
+@import url('global.css');
+@import url('layout.css');
+@import url('boxypastel.css');
diff --git a/src/lib/Server/Hostbase/media/global.css b/src/lib/Server/Hostbase/media/global.css
index 92d7ce0a3..73451e1bc 100644
--- a/src/lib/Server/Hostbase/media/global.css
+++ b/src/lib/Server/Hostbase/media/global.css
@@ -1,8 +1,8 @@
-body {
- margin:0;
- padding:0;
- font-size:12px;
- font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif;
- color:#000;
- background:#fff;
- }
+body {
+ margin:0;
+ padding:0;
+ font-size:12px;
+ font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif;
+ color:#000;
+ background:#fff;
+ }
diff --git a/src/lib/Server/Hostbase/media/layout.css b/src/lib/Server/Hostbase/media/layout.css
index 99f61da8f..9085cc220 100644
--- a/src/lib/Server/Hostbase/media/layout.css
+++ b/src/lib/Server/Hostbase/media/layout.css
@@ -1,62 +1,62 @@
-/* Page Structure */
-#container { position:absolute; top: 3em; margin-left:1em; margin-right:2em; padding:0; margin-top:1.5em; min-width:
- 650px; }
-#header { width:100%; }
-#content-main { float:left; }
-
-/* HEADER */
-#header {
-background:#000;
-color:#ffc;
-position:absolute;
-}
-#header a:link, #header a:visited { color:white; }
-#header a:hover { text-decoration:underline; }
-#branding h1 { padding:0 10px; font-size:18px; margin:8px 0; font-weight:normal; color:#f4f379; }
-#branding h2 { padding:0 10px; font-size:14px; margin:-8px 0 8px 0; font-weight:normal; color:#ffc; }
-#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
-
-/*SIDEBAR*/
-#sidebar {
- float:left;
- position: relative;
- width: auto;
- height: 100%;
- margin-top: 3em;
- padding-right: 1.5em;
- padding-left: 1.5em;
- padding-top: 1em;
- padding-bottom:3em;
- background: #000;
- color:ffc;
-}
-
-a.sidebar:link {color: #fff;}
-a.sidebar:active {color: #fff;}
-a.sidebar:visited {color: #fff;}
-a.sidebar:hover {color: #fff;}
-
-ul.sidebar {
- color: #ffc;
- text-decoration: none;
- list-style-type: none;
- text-indent: -1em;
-}
-ul.sidebar-level2 {
- text-indent: -2em;
- list-style-type: none;
- font-size: 11px;
-}
-
-/* ALIGNED FIELDSETS */
-.aligned label { display:block; padding:0 1em 3px 0; float:left; width:8em; }
-.aligned label.inline { display:inline; float:none; }
-.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { width:350px; }
-form .aligned p, form .aligned ul { margin-left:7em; padding-left:30px; }
-form .aligned table p { margin-left:0; padding-left:0; }
-form .aligned p.help { padding-left:38px; }
-.aligned .vCheckboxLabel { float:none !important; display:inline; padding-left:4px; }
-.colM .aligned .vLargeTextField, colM .aligned .vXMLLargeTextField { width:610px; }
-.checkbox-row p.help { margin-left:0; padding-left:0 !important; }
-
-
+/* Page Structure */
+#container { position:absolute; top: 3em; margin-left:1em; margin-right:2em; padding:0; margin-top:1.5em; min-width:
+ 650px; }
+#header { width:100%; }
+#content-main { float:left; }
+
+/* HEADER */
+#header {
+background:#000;
+color:#ffc;
+position:absolute;
+}
+#header a:link, #header a:visited { color:white; }
+#header a:hover { text-decoration:underline; }
+#branding h1 { padding:0 10px; font-size:18px; margin:8px 0; font-weight:normal; color:#f4f379; }
+#branding h2 { padding:0 10px; font-size:14px; margin:-8px 0 8px 0; font-weight:normal; color:#ffc; }
+#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
+
+/*SIDEBAR*/
+#sidebar {
+ float:left;
+ position: relative;
+ width: auto;
+ height: 100%;
+ margin-top: 3em;
+ padding-right: 1.5em;
+ padding-left: 1.5em;
+ padding-top: 1em;
+ padding-bottom:3em;
+ background: #000;
+ color:ffc;
+}
+
+a.sidebar:link {color: #fff;}
+a.sidebar:active {color: #fff;}
+a.sidebar:visited {color: #fff;}
+a.sidebar:hover {color: #fff;}
+
+ul.sidebar {
+ color: #ffc;
+ text-decoration: none;
+ list-style-type: none;
+ text-indent: -1em;
+}
+ul.sidebar-level2 {
+ text-indent: -2em;
+ list-style-type: none;
+ font-size: 11px;
+}
+
+/* ALIGNED FIELDSETS */
+.aligned label { display:block; padding:0 1em 3px 0; float:left; width:8em; }
+.aligned label.inline { display:inline; float:none; }
+.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { width:350px; }
+form .aligned p, form .aligned ul { margin-left:7em; padding-left:30px; }
+form .aligned table p { margin-left:0; padding-left:0; }
+form .aligned p.help { padding-left:38px; }
+.aligned .vCheckboxLabel { float:none !important; display:inline; padding-left:4px; }
+.colM .aligned .vLargeTextField, colM .aligned .vXMLLargeTextField { width:610px; }
+.checkbox-row p.help { margin-left:0; padding-left:0 !important; }
+
+
diff --git a/src/lib/Server/Hostbase/settings.py b/src/lib/Server/Hostbase/settings.py
index a42fd5b2e..c44c7bf16 100644
--- a/src/lib/Server/Hostbase/settings.py
+++ b/src/lib/Server/Hostbase/settings.py
@@ -27,7 +27,7 @@ else:
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
- # ('Your Name', 'your_email@domain.com'),
+ ('Root', 'root'),
)
MANAGERS = ADMINS
diff --git a/src/lib/Server/Lint/Bundles.py b/src/lib/Server/Lint/Bundles.py
new file mode 100644
index 000000000..e90159f7c
--- /dev/null
+++ b/src/lib/Server/Lint/Bundles.py
@@ -0,0 +1,64 @@
+import lxml.etree
+import Bcfg2.Server.Lint
+
+class Bundles(Bcfg2.Server.Lint.ServerPlugin):
+ """ Perform various bundle checks """
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ """ run plugin """
+ if 'Bundler' in self.core.plugins:
+ self.missing_bundles()
+ for bundle in self.core.plugins['Bundler'].entries.values():
+ if self.HandlesFile(bundle.name):
+ if (Bcfg2.Server.Plugins.Bundler.have_genshi and
+ type(bundle) is
+ Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile):
+ self.sgenshi_groups(bundle)
+ else:
+ self.bundle_names(bundle)
+
+ def missing_bundles(self):
+ """ find bundles listed in Metadata but not implemented in Bundler """
+ if self.files is None:
+ # when given a list of files on stdin, this check is
+ # useless, so skip it
+ groupdata = self.metadata.groups_xml.xdata
+ ref_bundles = set([b.get("name")
+ for b in groupdata.findall("//Bundle")])
+
+ allbundles = self.core.plugins['Bundler'].entries.keys()
+ for bundle in ref_bundles:
+ xmlbundle = "%s.xml" % bundle
+ genshibundle = "%s.genshi" % bundle
+ if (xmlbundle not in allbundles and
+ genshibundle not in allbundles):
+ self.LintError("bundle-not-found",
+ "Bundle %s referenced, but does not exist" %
+ bundle)
+
+ def bundle_names(self, bundle):
+ """ verify bundle name attribute matches filename """
+ try:
+ xdata = lxml.etree.XML(bundle.data)
+ except AttributeError:
+ # genshi template
+ xdata = lxml.etree.parse(bundle.template.filepath).getroot()
+
+ fname = bundle.name.split('Bundler/')[1].split('.')[0]
+ bname = xdata.get('name')
+ if fname != bname:
+ self.LintError("inconsistent-bundle-name",
+ "Inconsistent bundle name: filename is %s, bundle name is %s" %
+ (fname, bname))
+
+ def sgenshi_groups(self, bundle):
+ """ ensure that Genshi Bundles do not include <Group> tags,
+ which are not supported """
+ xdata = lxml.etree.parse(bundle.name)
+ groups = [self.RenderXML(g)
+ for g in xdata.getroottree().findall("//Group")]
+ if groups:
+ self.LintError("group-tag-not-allowed",
+ "<Group> tag is not allowed in SGenshi Bundle:\n%s" %
+ "\n".join(groups))
diff --git a/src/lib/Server/Lint/Comments.py b/src/lib/Server/Lint/Comments.py
new file mode 100644
index 000000000..8e86cc564
--- /dev/null
+++ b/src/lib/Server/Lint/Comments.py
@@ -0,0 +1,188 @@
+import os.path
+import lxml.etree
+import Bcfg2.Server.Lint
+
+class Comments(Bcfg2.Server.Lint.ServerPlugin):
+ """ check files for various required headers """
+ def __init__(self, *args, **kwargs):
+ Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs)
+ self.config_cache = {}
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ self.check_bundles()
+ self.check_properties()
+ self.check_metadata()
+ self.check_cfg()
+ self.check_infoxml()
+ self.check_probes()
+
+ def required_keywords(self, rtype):
+ """ given a file type, fetch the list of required VCS keywords
+ from the bcfg2-lint config """
+ return self.required_items(rtype, "keyword")
+
+ def required_comments(self, rtype):
+ """ given a file type, fetch the list of required comments
+ from the bcfg2-lint config """
+ return self.required_items(rtype, "comment")
+
+ def required_items(self, rtype, itype):
+ """ given a file type and item type (comment or keyword),
+ fetch the list of required items from the bcfg2-lint config """
+ if itype not in self.config_cache:
+ self.config_cache[itype] = {}
+
+ if rtype not in self.config_cache[itype]:
+ rv = []
+ global_item = "global_%ss" % itype
+ if global_item in self.config:
+ rv.extend(self.config[global_item].split(","))
+
+ item = "%s_%ss" % (rtype.lower(), itype)
+ if item in self.config:
+ if self.config[item]:
+ rv.extend(self.config[item].split(","))
+ else:
+ # config explicitly specifies nothing
+ rv = []
+ self.config_cache[itype][rtype] = rv
+ return self.config_cache[itype][rtype]
+
+ def check_bundles(self):
+ """ check bundle files for required headers """
+ if 'Bundler' in self.core.plugins:
+ for bundle in self.core.plugins['Bundler'].entries.values():
+ xdata = None
+ rtype = ""
+ try:
+ xdata = lxml.etree.XML(bundle.data)
+ rtype = "bundler"
+ except AttributeError:
+ xdata = lxml.etree.parse(bundle.template.filepath).getroot()
+ rtype = "sgenshi"
+
+ self.check_xml(bundle.name, xdata, rtype)
+
+ def check_properties(self):
+ """ check properties files for required headers """
+ if 'Properties' in self.core.plugins:
+ props = self.core.plugins['Properties']
+ for propfile, pdata in props.store.entries.items():
+ if os.path.splitext(propfile)[1] == ".xml":
+ self.check_xml(pdata.name, pdata.data, 'properties')
+
+ def check_metadata(self):
+ """ check metadata files for required headers """
+ if self.has_all_xincludes("groups.xml"):
+ self.check_xml(os.path.join(self.metadata.data, "groups.xml"),
+ self.metadata.groups_xml.data,
+ "metadata")
+ if self.has_all_xincludes("clients.xml"):
+ self.check_xml(os.path.join(self.metadata.data, "clients.xml"),
+ self.metadata.clients_xml.data,
+ "metadata")
+
+ def check_cfg(self):
+ """ check Cfg files for required headers """
+ if 'Cfg' in self.core.plugins:
+ for entryset in self.core.plugins['Cfg'].entries.values():
+ for entry in entryset.entries.values():
+ if entry.name.endswith(".genshi"):
+ rtype = "tgenshi"
+ else:
+ rtype = "cfg"
+ self.check_plaintext(entry.name, entry.data, rtype)
+
+ def check_infoxml(self):
+ """ check info.xml files for required headers """
+ if 'Cfg' in self.core.plugins:
+ for entryset in self.core.plugins['Cfg'].entries.items():
+ if (hasattr(entryset, "infoxml") and
+ entryset.infoxml is not None):
+ self.check_xml(entryset.infoxml.name,
+ entryset.infoxml.pnode.data,
+ "infoxml")
+
+ def check_probes(self):
+ """ check probes for required headers """
+ if 'Probes' in self.core.plugins:
+ for probe in self.core.plugins['Probes'].probes.entries.values():
+ self.check_plaintext(probe.name, probe.data, "probes")
+
+ def check_xml(self, filename, xdata, rtype):
+ """ check generic XML files for required headers """
+ self.check_lines(filename,
+ [str(el)
+ for el in xdata.getiterator(lxml.etree.Comment)],
+ rtype)
+
+ def check_plaintext(self, filename, data, rtype):
+ """ check generic plaintex files for required headers """
+ self.check_lines(filename, data.splitlines(), rtype)
+
+ def check_lines(self, filename, lines, rtype):
+ """ generic header check for a set of lines """
+ if self.HandlesFile(filename):
+ # found is trivalent:
+ # False == not found
+ # None == found but not expanded
+ # True == found and expanded
+ found = dict((k, False) for k in self.required_keywords(rtype))
+
+ for line in lines:
+ # we check for both '$<keyword>:' and '$<keyword>$' to see
+ # if the keyword just hasn't been expanded
+ for (keyword, status) in found.items():
+ if not status:
+ if '$%s:' % keyword in line:
+ found[keyword] = True
+ elif '$%s$' % keyword in line:
+ found[keyword] = None
+
+ unexpanded = [keyword for (keyword, status) in found.items()
+ if status is None]
+ if unexpanded:
+ self.LintError("unexpanded-keywords",
+ "%s: Required keywords(s) found but not expanded: %s" %
+ (filename, ", ".join(unexpanded)))
+ missing = [keyword for (keyword, status) in found.items()
+ if status is False]
+ if missing:
+ self.LintError("keywords-not-found",
+ "%s: Required keywords(s) not found: $%s$" %
+ (filename, "$, $".join(missing)))
+
+ # next, check for required comments. found is just
+ # boolean
+ found = dict((k, False) for k in self.required_comments(rtype))
+
+ for line in lines:
+ for (comment, status) in found.items():
+ if not status:
+ found[comment] = comment in line
+
+ missing = [comment for (comment, status) in found.items()
+ if status is False]
+ if missing:
+ self.LintError("comments-not-found",
+ "%s: Required comments(s) not found: %s" %
+ (filename, ", ".join(missing)))
+
+ def has_all_xincludes(self, mfile):
+ """ return true if self.files includes all XIncludes listed in
+ the specified metadata type, false otherwise"""
+ if self.files is None:
+ return True
+ else:
+ path = os.path.join(self.metadata.data, mfile)
+ if path in self.files:
+ xdata = lxml.etree.parse(path)
+ for el in xdata.findall('./{http://www.w3.org/2001/XInclude}include'):
+ if not self.has_all_xincludes(el.get('href')):
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: could not include %s" % path)
+ return False
+
+ return True
+
diff --git a/src/lib/Server/Lint/Duplicates.py b/src/lib/Server/Lint/Duplicates.py
new file mode 100644
index 000000000..517f0dd7b
--- /dev/null
+++ b/src/lib/Server/Lint/Duplicates.py
@@ -0,0 +1,82 @@
+import os.path
+import lxml.etree
+import Bcfg2.Server.Lint
+
+class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
+ """ Find duplicate clients, groups, etc. """
+ def __init__(self, *args, **kwargs):
+ Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs)
+ self.groups_xdata = None
+ self.clients_xdata = None
+ self.load_xdata()
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ """ run plugin """
+ # only run this plugin if we were not given a list of files.
+ # not only is it marginally silly to run this plugin with a
+ # partial list of files, it turns out to be really freaking
+ # hard to get only a fragment of group or client metadata
+ if self.groups_xdata is not None:
+ self.duplicate_groups()
+ self.duplicate_defaults()
+ if self.clients_xdata is not None:
+ self.duplicate_clients()
+
+ def load_xdata(self):
+ """ attempt to load XML data for groups and clients. only
+ actually load data if all documents reference in XIncludes can
+ be found in self.files"""
+ if self.has_all_xincludes("groups.xml"):
+ self.groups_xdata = self.metadata.clients_xml.xdata
+ if self.has_all_xincludes("clients.xml"):
+ self.clients_xdata = self.metadata.clients_xml.xdata
+
+ def duplicate_groups(self):
+ """ find duplicate groups """
+ self.duplicate_entries(self.clients_xdata.xpath('//Groups/Group'),
+ 'group')
+
+ def duplicate_clients(self):
+ """ find duplicate clients """
+ self.duplicate_entries(self.clients_xdata.xpath('//Clients/Client'),
+ 'client')
+
+ def duplicate_entries(self, data, etype):
+ """ generic duplicate entry finder """
+ seen = {}
+ for el in data:
+ if el.get('name') not in seen:
+ seen[el.get('name')] = el
+ else:
+ self.LintError("duplicate-%s" % etype,
+ "Duplicate %s '%s':\n%s\n%s" %
+ (etype, el.get('name'),
+ self.RenderXML(seen[el.get('name')]),
+ self.RenderXML(el)))
+
+ def duplicate_defaults(self):
+ """ check for multiple default group definitions """
+ default_groups = [g for g in self.groups_xdata.findall('.//Group')
+ if g.get('default') == 'true']
+ if len(default_groups) > 1:
+ self.LintError("multiple-default-groups",
+ "Multiple default groups defined: %s" %
+ ",".join(default_groups))
+
+ def has_all_xincludes(self, mfile):
+ """ return true if self.files includes all XIncludes listed in
+ the specified metadata type, false otherwise"""
+ if self.files is None:
+ return True
+ else:
+ path = os.path.join(self.metadata.data, mfile)
+ if path in self.files:
+ xdata = lxml.etree.parse(path)
+ for el in xdata.findall('./{http://www.w3.org/2001/XInclude}include'):
+ if not self.has_all_xincludes(el.get('href')):
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: could not include %s" % path)
+ return False
+
+ return True
diff --git a/src/lib/Server/Lint/InfoXML.py b/src/lib/Server/Lint/InfoXML.py
new file mode 100644
index 000000000..7725ad748
--- /dev/null
+++ b/src/lib/Server/Lint/InfoXML.py
@@ -0,0 +1,43 @@
+import os.path
+import Bcfg2.Options
+import Bcfg2.Server.Lint
+
+class InfoXML(Bcfg2.Server.Lint.ServerPlugin):
+ """ ensure that all config files have an info.xml file"""
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ if 'Cfg' in self.core.plugins:
+ for filename, entryset in self.core.plugins['Cfg'].entries.items():
+ infoxml_fname = os.path.join(entryset.path, "info.xml")
+ if self.HandlesFile(infoxml_fname):
+ if (hasattr(entryset, "infoxml") and
+ entryset.infoxml is not None):
+ self.check_infoxml(entryset.infoxml.pnode.data)
+ else:
+ self.LintError("no-infoxml",
+ "No info.xml found for %s" % filename)
+
+ def check_infoxml(self, xdata):
+ for info in xdata.getroottree().findall("//Info"):
+ required = []
+ if "required_attrs" in self.config:
+ required = self.config["required_attrs"].split(",")
+
+ missing = [attr for attr in required if info.get(attr) is None]
+ if missing:
+ self.LintError("required-infoxml-attrs-missing",
+ "Required attribute(s) %s not found in %s:%s" %
+ (",".join(missing), infoxml_fname,
+ self.RenderXML(info)))
+
+ if ((Bcfg2.Options.MDATA_PARANOID.value and
+ info.get("paranoid") is not None and
+ info.get("paranoid").lower() == "false") or
+ (not Bcfg2.Options.MDATA_PARANOID.value and
+ (info.get("paranoid") is None or
+ info.get("paranoid").lower() != "true"))):
+ self.LintError("paranoid-false",
+ "Paranoid must be true in %s:%s" %
+ (infoxml_fname, self.RenderXML(info)))
+
diff --git a/src/lib/Server/Lint/Pkgmgr.py b/src/lib/Server/Lint/Pkgmgr.py
new file mode 100644
index 000000000..39c601617
--- /dev/null
+++ b/src/lib/Server/Lint/Pkgmgr.py
@@ -0,0 +1,38 @@
+import Bcfg2.Server.Lint
+
+class Pkgmgr(Bcfg2.Server.Lint.ServerPlugin):
+ """ find duplicate Pkgmgr entries with the same priority """
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ if 'Pkgmgr' not in self.core.plugins:
+ self.logger.info("Pkgmgr server plugin is not enabled, skipping Pkgmgr lint checks")
+ return
+
+ pset = set()
+ for plist in self.core.plugins['Pkgmgr'].entries.values():
+ if self.HandlesFile(plist.name):
+ xdata = plist.data
+ # get priority, type, group
+ priority = xdata.getroot().get('priority')
+ ptype = xdata.getroot().get('type')
+ for pkg in xdata.findall("//Package"):
+ if pkg.getparent().tag == 'Group':
+ grp = pkg.getparent().get('name')
+ if (type(grp) is not str and
+ grp.getparent().tag == 'Group'):
+ pgrp = grp.getparent().get('name')
+ else:
+ pgrp = 'none'
+ else:
+ grp = 'none'
+ pgrp = 'none'
+ ptuple = (pkg.get('name'), priority, ptype, grp, pgrp)
+ # check if package is already listed with same
+ # priority, type, grp
+ if ptuple in pset:
+ self.LintError("duplicate-package",
+ "Duplicate Package %s, priority:%s, type:%s" %
+ (pkg.get('name'), priority, ptype))
+ else:
+ pset.add(ptuple)
diff --git a/src/lib/Server/Lint/RequiredAttrs.py b/src/lib/Server/Lint/RequiredAttrs.py
new file mode 100644
index 000000000..cbb4395c4
--- /dev/null
+++ b/src/lib/Server/Lint/RequiredAttrs.py
@@ -0,0 +1,72 @@
+import os.path
+import lxml.etree
+import Bcfg2.Server.Lint
+
+class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
+ """ verify attributes for configuration entries (as defined in
+ doc/server/configurationentries) """
+
+ def __init__(self, *args, **kwargs):
+ Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs)
+ self.required_attrs = {
+ 'device': ['name', 'owner', 'group', 'dev_type'],
+ 'directory': ['name', 'owner', 'group', 'perms'],
+ 'file': ['name', 'owner', 'group', 'perms'],
+ 'hardlink': ['name', 'to'],
+ 'symlink': ['name', 'to'],
+ 'ignore': ['name'],
+ 'nonexistent': ['name'],
+ 'permissions': ['name', 'owner', 'group', 'perms']}
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ self.check_rules()
+ self.check_bundles()
+
+ def check_rules(self):
+ """ check Rules for Path entries with missing attrs """
+ if 'Rules' in self.core.plugins:
+ for rules in self.core.plugins['Rules'].entries.values():
+ xdata = rules.pnode.data
+ for path in xdata.xpath("//Path"):
+ self.check_entry(path, os.path.join(self.config['repo'],
+ rules.name))
+
+ def check_bundles(self):
+ """ check bundles for BoundPath entries with missing attrs """
+ if 'Bundler' in self.core.plugins:
+ for bundle in self.core.plugins['Bundler'].entries.values():
+ try:
+ xdata = lxml.etree.XML(bundle.data)
+ except AttributeError:
+ xdata = lxml.etree.parse(bundle.template.filepath).getroot()
+
+ for path in xdata.xpath("//BoundPath"):
+ self.check_entry(path, bundle.name)
+
+ def check_entry(self, entry, filename):
+ """ generic entry check """
+ if self.HandlesFile(filename):
+ pathname = entry.get('name')
+ pathtype = entry.get('type')
+ pathset = set(entry.attrib.keys())
+ try:
+ required_attrs = set(self.required_attrs[pathtype] + ['type'])
+ except KeyError:
+ self.LintError("unknown-path-type",
+ "Unknown path type %s: %s" %
+ (pathtype, self.RenderXML(entry)))
+
+ if 'dev_type' in required_attrs:
+ dev_type = entry.get('dev_type')
+ if dev_type in ['block', 'char']:
+ # check if major/minor are specified
+ required_attrs |= set(['major', 'minor'])
+ if not pathset.issuperset(required_attrs):
+ self.LintError("required-attrs-missing",
+ "The required attributes %s are missing for %s %sin %s:\n%s" %
+ (",".join([attr
+ for attr in
+ required_attrs.difference(pathset)]),
+ entry.tag, pathname, filename,
+ self.RenderXML(entry)))
diff --git a/src/lib/Server/Lint/Validate.py b/src/lib/Server/Lint/Validate.py
new file mode 100644
index 000000000..c87c55ee9
--- /dev/null
+++ b/src/lib/Server/Lint/Validate.py
@@ -0,0 +1,186 @@
+import glob
+import lxml.etree
+import os
+import fnmatch
+import Bcfg2.Options
+import Bcfg2.Server.Lint
+from subprocess import Popen, PIPE, STDOUT
+
+class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
+ """ Ensure that the repo validates """
+
+ def __init__(self, *args, **kwargs):
+ Bcfg2.Server.Lint.ServerlessPlugin.__init__(self, *args, **kwargs)
+ self.filesets = {"metadata:groups":"%s/metadata.xsd",
+ "metadata:clients":"%s/clients.xsd",
+ "info":"%s/info.xsd",
+ "%s/Bundler/*.{xml,genshi}":"%s/bundle.xsd",
+ "%s/Pkgmgr/*.xml":"%s/pkglist.xsd",
+ "%s/Base/*.xml":"%s/base.xsd",
+ "%s/Rules/*.xml":"%s/rules.xsd",
+ "%s/etc/report-configuration.xml":"%s/report-configuration.xsd",
+ "%s/Svcmgr/*.xml":"%s/services.xsd",
+ "%s/Deps/*.xml":"%s/deps.xsd",
+ "%s/Decisions/*.xml":"%s/decisions.xsd",
+ "%s/Packages/config.xml":"%s/packages.xsd",
+ "%s/GroupPatterns/config.xml":"%s/grouppatterns.xsd",
+ "%s/NagiosGen/config.xml":"%s/nagiosgen.xsd"}
+
+ self.filelists = {}
+ self.get_filelists()
+
+ @Bcfg2.Server.Lint.returnErrors
+ def Run(self):
+ self.schemadir = self.config['schema']
+
+ for schemaname, path in self.filesets.items():
+ try:
+ filelist = self.filelists[path]
+ except KeyError:
+ filelist = []
+
+ if filelist:
+ # avoid loading schemas for empty file lists
+ try:
+ schema = lxml.etree.XMLSchema(lxml.etree.parse(schemaname %
+ schemadir))
+ except:
+ self.LintError("schema-failed-to-parse",
+ "Failed to process schema %s",
+ schemaname % schemadir)
+ continue
+ for filename in filelist:
+ self.validate(filename, schemaname % schemadir,
+ schema=schema)
+
+ self.check_properties()
+
+ def check_properties(self):
+ """ check Properties files against their schemas """
+ for filename in self.filelists['props']:
+ schemafile = "%s.xsd" % os.path.splitext(filename)[0]
+ if os.path.exists(schemafile):
+ self.validate(filename, schemafile)
+ else:
+ self.LintError("properties-schema-not-found",
+ "No schema found for %s" % filename)
+
+ def validate(self, filename, schemafile, schema=None):
+ """validate a file against the given lxml.etree.Schema.
+ return True on success, False on failure """
+ if schema is None:
+ # if no schema object was provided, instantiate one
+ try:
+ schema = lxml.etree.XMLSchema(lxml.etree.parse(schemafile))
+ except:
+ self.LintError("schema-failed-to-parse",
+ "Failed to process schema %s" % schemafile)
+ return False
+
+ try:
+ datafile = lxml.etree.parse(filename)
+ except SyntaxError:
+ lint = Popen(["xmllint", filename], stdout=PIPE, stderr=STDOUT)
+ self.LintError("xml-failed-to-parse",
+ "%s fails to parse:\n%s" % (filename,
+ lint.communicate()[0]))
+ lint.wait()
+ return False
+ except IOError:
+ self.LintError("xml-failed-to-read",
+ "Failed to open file %s" % filename)
+ return False
+
+ if not schema.validate(datafile):
+ cmd = ["xmllint"]
+ if self.files is None:
+ cmd.append("--xinclude")
+ cmd.extend(["--noout", "--schema", schemafile, filename])
+ lint = Popen(cmd, stdout=PIPE, stderr=STDOUT)
+ output = lint.communicate()[0]
+ if lint.wait():
+ self.LintError("xml-failed-to-verify",
+ "%s fails to verify:\n%s" % (filename, output))
+ return False
+ return True
+
+ def get_filelists(self):
+ """ get lists of different kinds of files to validate """
+ if self.files is not None:
+ listfiles = lambda p: fnmatch.filter(self.files, p % "*")
+ else:
+ listfiles = lambda p: glob.glob(p % self.config['repo'])
+
+ for path in self.filesets.keys():
+ if path.startswith("metadata:"):
+ mtype = path.split(":")[1]
+ self.filelists[path] = self.get_metadata_list(mtype)
+ elif path == "info":
+ if self.files is not None:
+ self.filelists[path] = \
+ [f for f in self.files
+ if os.path.basename(f) == 'info.xml']
+ else: # self.files is None
+ self.filelists[path] = []
+ for infodir in ['Cfg', 'TGenshi', 'TCheetah']:
+ for root, dirs, files in os.walk('%s/%s' %
+ (self.config['repo'],
+ infodir)):
+ self.filelists[path].extend([os.path.join(root, f)
+ for f in files
+ if f == 'info.xml'])
+ else:
+ self.filelists[path] = listfiles(path)
+
+ self.filelists['props'] = listfiles("%s/Properties/*.xml")
+ all_metadata = listfiles("%s/Metadata/*.xml")
+
+ # if there are other files in Metadata that aren't xincluded
+ # from clients.xml or groups.xml, we can't verify them. warn
+ # about those.
+ for fname in all_metadata:
+ if (fname not in self.filelists['metadata:groups'] and
+ fname not in self.filelists['metadata:clients']):
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: Could not determine file type of %s" % fname)
+
+ def get_metadata_list(self, mtype):
+ """ get all metadata files for the specified type (clients or
+ group) """
+ if self.files is not None:
+ rv = fnmatch.filter(self.files, "*/Metadata/%s.xml" % mtype)
+ else:
+ rv = glob.glob("%s/Metadata/%s.xml" % (self.config['repo'], mtype))
+
+ # attempt to follow XIncludes. if the top-level files aren't
+ # listed in self.files, though, there's really nothing we can
+ # do to guess what a file in Metadata is
+ if rv:
+ rv.extend(self.follow_xinclude(rv[0]))
+
+ return rv
+
+ def follow_xinclude(self, xfile):
+ """ follow xincludes in the given file """
+ xdata = lxml.etree.parse(xfile)
+ included = set([ent.get('href') for ent in
+ xdata.findall('./{http://www.w3.org/2001/XInclude}include')])
+ rv = []
+
+ while included:
+ try:
+ filename = included.pop()
+ except KeyError:
+ continue
+
+ path = os.path.join(os.path.dirname(xfile), filename)
+ if self.HandlesFile(path):
+ rv.append(path)
+ groupdata = lxml.etree.parse(path)
+ [included.add(el.get('href'))
+ for el in
+ groupdata.findall('./{http://www.w3.org/2001/XInclude}include')]
+ included.discard(filename)
+
+ return rv
+
diff --git a/src/lib/Server/Lint/__init__.py b/src/lib/Server/Lint/__init__.py
new file mode 100644
index 000000000..3b89d1f9e
--- /dev/null
+++ b/src/lib/Server/Lint/__init__.py
@@ -0,0 +1,155 @@
+__revision__ = '$Revision$'
+
+__all__ = ['Bundles',
+ 'Comments',
+ 'Duplicates',
+ 'InfoXML',
+ 'Pkgmgr',
+ 'RequiredAttrs',
+ 'Validate']
+
+import logging
+import os.path
+from copy import copy
+import lxml.etree
+import Bcfg2.Logger
+
+def returnErrors(fn):
+ """ Decorator for Run method that returns error counts """
+ return fn
+
+class Plugin (object):
+ """ base class for ServerlessPlugin and ServerPlugin """
+
+ def __init__(self, config, errorhandler=None, files=None):
+ self.files = files
+ self.config = config
+ self.logger = logging.getLogger('bcfg2-lint')
+ if errorhandler is None:
+ self.errorHandler = ErrorHandler()
+ else:
+ self.errorHandler = errorhandler
+
+ def Run(self):
+ """ run the plugin. must be overloaded by child classes """
+ pass
+
+ def HandlesFile(self, fname):
+ """ returns true if the given file should be handled by the
+ plugin according to the files list, false otherwise """
+ return (self.files is None or
+ fname in self.files or
+ os.path.join(self.config['repo'], fname) in self.files or
+ os.path.abspath(fname) in self.files or
+ os.path.abspath(os.path.join(self.config['repo'],
+ fname)) in self.files)
+
+ def LintError(self, err, msg):
+ self.errorHandler.dispatch(err, msg)
+
+ def RenderXML(self, element):
+ """render an XML element for error output -- line number
+ prefixed, no children"""
+ xml = None
+ if len(element) or element.text:
+ el = copy(element)
+ if el.text:
+ el.text = '...'
+ [el.remove(c) for c in el.iterchildren()]
+ xml = lxml.etree.tostring(el).strip()
+ else:
+ xml = lxml.etree.tostring(element).strip()
+ return " line %s: %s" % (element.sourceline, xml)
+
+
+class ErrorHandler (object):
+ # how to handle different errors by default
+ _errors = {"no-infoxml":"warning",
+ "paranoid-false":"warning",
+ "bundle-not-found":"error",
+ "inconsistent-bundle-name":"warning",
+ "group-tag-not-allowed":"error",
+ "unexpanded-keywords":"warning",
+ "keywords-not-found":"warning",
+ "comments-not-found":"warning",
+ "broken-xinclude-chain":"warning",
+ "duplicate-client":"error",
+ "duplicate-group":"error",
+ "duplicate-package":"error",
+ "multiple-default-groups":"error",
+ "required-infoxml-attrs-missing":"error",
+ "unknown-path-type":"error",
+ "required-attrs-missing":"error",
+ "schema-failed-to-parse":"warning",
+ "properties-schema-not-found":"warning",
+ "xml-failed-to-parse":"error",
+ "xml-failed-to-read":"error",
+ "xml-failed-to-verify":"error",}
+
+ def __init__(self, config=None):
+ self.errors = 0
+ self.warnings = 0
+
+ self.logger = logging.getLogger('bcfg2-lint')
+
+ self._handlers = {}
+ if config is not None:
+ for err, action in config.items():
+ if "warn" in action:
+ self._handlers[err] = self.warn
+ elif "err" in action:
+ self._handlers[err] = self.error
+ else:
+ self._handlers[err] = self.debug
+
+ for err, action in self._errors.items():
+ if err not in self._handlers:
+ if "warn" in action:
+ self._handlers[err] = self.warn
+ elif "err" in action:
+ self._handlers[err] = self.error
+ else:
+ self._handlers[err] = self.debug
+
+ def dispatch(self, err, msg):
+ if err in self._handlers:
+ self._handlers[err](msg)
+ self.logger.debug(" (%s)" % err)
+ else:
+ self.logger.info("Unknown error %s" % err)
+
+ def error(self, msg):
+ """ log an error condition """
+ self.errors += 1
+ lines = msg.splitlines()
+ self.logger.error("ERROR: %s" % lines.pop())
+ [self.logger.error(" %s" % l) for l in lines]
+
+ def warn(self, msg):
+ """ log a warning condition """
+ self.warnings += 1
+ lines = msg.splitlines()
+ self.logger.warning("WARNING: %s" % lines.pop())
+ [self.logger.warning(" %s" % l) for l in lines]
+
+ def debug(self, msg):
+ """ log a silent/debug condition """
+ lines = msg.splitlines()
+ [self.logger.debug("%s" % l) for l in lines]
+
+
+class ServerlessPlugin (Plugin):
+ """ base class for plugins that are run before the server starts
+ up (i.e., plugins that check things that may prevent the server
+ from starting up) """
+ pass
+
+
+class ServerPlugin (Plugin):
+ """ base class for plugins that check things that require the
+ running Bcfg2 server """
+ def __init__(self, lintCore, config, **kwargs):
+ Plugin.__init__(self, config, **kwargs)
+ self.core = lintCore
+ self.logger = self.core.logger
+ self.metadata = self.core.metadata
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index 73d054409..cd2b63656 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -8,13 +8,23 @@ import os
import pickle
import posixpath
import re
-import Queue
+import sys
import threading
from lxml.etree import XML, XMLSyntaxError
import Bcfg2.Options
+# py3k compatibility
+if sys.hexversion >= 0x03000000:
+ from functools import reduce
+ from io import FileIO as BUILTIN_FILE_TYPE
+else:
+ BUILTIN_FILE_TYPE = file
+from Bcfg2.Bcfg2Py3k import Queue
+from Bcfg2.Bcfg2Py3k import Empty
+from Bcfg2.Bcfg2Py3k import Full
+
# grab default metadata info from bcfg2.conf
opts = {'owner': Bcfg2.Options.MDATA_OWNER,
'group': Bcfg2.Options.MDATA_GROUP,
@@ -38,14 +48,17 @@ info_regex = re.compile( \
'paranoid:(\s)*(?P<paranoid>\S+)|' +
'perms:(\s)*(?P<perms>\w+)|')
+
class PluginInitError(Exception):
"""Error raised in cases of Plugin initialization errors."""
pass
+
class PluginExecutionError(Exception):
"""Error raised in case of Plugin execution errors."""
pass
+
class Plugin(object):
"""This is the base class for all Bcfg2 Server plugins.
Several attributes must be defined in the subclass:
@@ -90,6 +103,7 @@ class Plugin(object):
def shutdown(self):
self.running = False
+
class Generator(object):
"""Generator plugins contribute to literal client configurations."""
def HandlesEntry(self, entry, metadata):
@@ -100,20 +114,24 @@ class Generator(object):
"""This is the slow-path handler for configuration entry binding."""
raise PluginExecutionError
+
class Structure(object):
"""Structure Plugins contribute to abstract client configurations."""
def BuildStructures(self, metadata):
"""Return a list of abstract goal structures for client."""
raise PluginExecutionError
+
class Metadata(object):
"""Signal metadata capabilities for this plugin"""
def add_client(self, client_name, attribs):
"""Add client."""
pass
+
def remove_client(self, client_name):
"""Remove client."""
pass
+
def viz(self, hosts, bundles, key, colors):
"""Create viz str for viz admin mode."""
pass
@@ -124,6 +142,7 @@ class Metadata(object):
def merge_additional_data(self, imd, source, groups, data):
raise PluginExecutionError
+
class Connector(object):
"""Connector Plugins augment client metadata instances."""
def get_additional_groups(self, metadata):
@@ -134,6 +153,7 @@ class Connector(object):
"""Determine additional data for metadata instances."""
return dict()
+
class Probing(object):
"""Signal probe capability for this plugin."""
def GetProbes(self, _):
@@ -144,11 +164,13 @@ class Probing(object):
"""Receive probe results pertaining to client."""
pass
+
class Statistics(object):
"""Signal statistics handling capability."""
def process_statistics(self, client, xdata):
pass
+
class ThreadedStatistics(Statistics,
threading.Thread):
"""Threaded statistics handling capability."""
@@ -157,7 +179,7 @@ class ThreadedStatistics(Statistics,
threading.Thread.__init__(self)
# Event from the core signaling an exit
self.terminate = core.terminate
- self.work_queue = Queue.Queue(100000)
+ self.work_queue = Queue(100000)
self.pending_file = "%s/etc/%s.pending" % (datastore, self.__class__.__name__)
self.daemon = True
self.start()
@@ -169,10 +191,10 @@ class ThreadedStatistics(Statistics,
while not self.work_queue.empty():
(metadata, data) = self.work_queue.get_nowait()
try:
- pending_data.append( ( metadata.hostname, lxml.etree.tostring(data) ) )
+ pending_data.append((metadata.hostname, lxml.etree.tostring(data)))
except:
self.logger.warning("Dropping interaction for %s" % metadata.hostname)
- except Queue.Empty:
+ except Empty:
pass
try:
@@ -192,7 +214,8 @@ class ThreadedStatistics(Statistics,
savefile = open(self.pending_file, 'r')
pending_data = pickle.load(savefile)
savefile.close()
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
self.logger.warning("Failed to load pending data: %s" % e)
for (pmetadata, pdata) in pending_data:
# check that shutdown wasnt called early
@@ -202,7 +225,7 @@ class ThreadedStatistics(Statistics,
try:
while True:
try:
- metadata = self.core.build_metadata(pmetadata)
+ metadata = self.core.build_metadata(pmetadata)
break
except Bcfg2.Server.Plugins.Metadata.MetadataRuntimeError:
pass
@@ -211,11 +234,12 @@ class ThreadedStatistics(Statistics,
if self.terminate.isSet():
return False
- self.work_queue.put_nowait( (metadata, lxml.etree.fromstring(pdata)) )
- except Queue.Full:
+ self.work_queue.put_nowait((metadata, lxml.etree.fromstring(pdata)))
+ except Full:
self.logger.warning("Queue.Full: Failed to load queue data")
break
- except lxml.etree.LxmlError, lxml_error:
+ except lxml.etree.LxmlError:
+ lxml_error = sys.exc_info()[1]
self.logger.error("Unable to load save interaction: %s" % lxml_error)
except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
self.logger.error("Unable to load metadata for save interaction: %s" % pmetadata)
@@ -232,9 +256,10 @@ class ThreadedStatistics(Statistics,
while not self.terminate.isSet():
try:
(xdata, client) = self.work_queue.get(block=True, timeout=2)
- except Queue.Empty:
+ except Empty:
continue
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
self.logger.error("ThreadedStatistics: %s" % e)
continue
self.handle_statistic(xdata, client)
@@ -246,7 +271,7 @@ class ThreadedStatistics(Statistics,
try:
self.work_queue.put_nowait((metadata, copy.deepcopy(data)))
warned = False
- except Queue.Full:
+ except Full:
if not warned:
self.logger.warning("%s: Queue is full. Dropping interactions." % self.__class__.__name__)
warned = True
@@ -255,6 +280,7 @@ class ThreadedStatistics(Statistics,
"""Handle stats here."""
pass
+
class PullSource(object):
def GetExtra(self, client):
return []
@@ -262,6 +288,7 @@ class PullSource(object):
def GetCurrentEntry(self, client, e_type, e_name):
raise PluginExecutionError
+
class PullTarget(object):
def AcceptChoices(self, entry, metadata):
raise PluginExecutionError
@@ -271,31 +298,38 @@ class PullTarget(object):
of bcfg2-admin pull."""
raise PluginExecutionError
+
class Decision(object):
"""Signal decision handling capability."""
def GetDecisions(self, metadata, mode):
return []
+
class ValidationError(Exception):
pass
+
class StructureValidator(object):
"""Validate/modify goal structures."""
def validate_structures(self, metadata, structures):
- raise ValidationError, "not implemented"
+ raise ValidationError("not implemented")
+
class GoalValidator(object):
"""Validate/modify configuration goals."""
def validate_goals(self, metadata, goals):
- raise ValidationError, "not implemented"
+ raise ValidationError("not implemented")
+
class Version(object):
"""Interact with various version control systems."""
def get_revision(self):
return []
+
def commit_data(self, file_list, comment=None):
pass
+
# the rest of the file contains classes for coherent file caching
class FileBacked(object):
@@ -315,7 +349,7 @@ class FileBacked(object):
if event and event.code2str() not in ['exists', 'changed', 'created']:
return
try:
- self.data = file(self.name).read()
+ self.data = BUILTIN_FILE_TYPE(self.name).read()
self.Index()
except IOError:
logger.error("Failed to read file %s" % (self.name))
@@ -324,6 +358,7 @@ class FileBacked(object):
"""Update local data structures based on current file state"""
pass
+
class DirectoryBacked(object):
"""This object is a coherent cache for a filesystem hierarchy of files."""
__child__ = FileBacked
@@ -341,7 +376,7 @@ class DirectoryBacked(object):
return self.entries[key]
def __iter__(self):
- return self.entries.iteritems()
+ return iter(list(self.entries.items()))
def AddEntry(self, name):
"""Add new entry to data structures upon file creation."""
@@ -380,9 +415,10 @@ class DirectoryBacked(object):
elif action in ['endExist']:
pass
else:
- print "Got unknown event %s %s %s" % (event.requestID,
+ print("Got unknown event %s %s %s" % (event.requestID,
event.code2str(),
- event.filename)
+ event.filename))
+
class XMLFileBacked(FileBacked):
"""
@@ -401,7 +437,7 @@ class XMLFileBacked(FileBacked):
try:
xdata = XML(self.data)
except XMLSyntaxError:
- logger.error("Failed to parse %s"%(self.name))
+ logger.error("Failed to parse %s" % (self.name))
return
self.label = xdata.attrib[self.__identifier__]
self.entries = xdata.getchildren()
@@ -409,12 +445,14 @@ class XMLFileBacked(FileBacked):
def __iter__(self):
return iter(self.entries)
+
class SingleXMLFileBacked(XMLFileBacked):
"""This object is a coherent cache for an independent XML file."""
def __init__(self, filename, fam):
XMLFileBacked.__init__(self, filename)
fam.AddMonitor(filename, self)
+
class StructFile(XMLFileBacked):
"""This file contains a set of structure file formatting logic."""
def __init__(self, name):
@@ -429,38 +467,52 @@ class StructFile(XMLFileBacked):
logger.error("Failed to parse file %s" % self.name)
return
self.fragments = {}
- work = {lambda x:True: xdata.getchildren()}
+ work = {lambda x: True: xdata.getchildren()}
while work:
(predicate, worklist) = work.popitem()
- self.fragments[predicate] = [item for item in worklist if item.tag != 'Group'
- and not isinstance(item, lxml.etree._Comment)]
- for group in [item for item in worklist if item.tag == 'Group']:
- # if only python had forceable early-binding
- if group.get('negate', 'false') in ['true', 'True']:
- cmd = "lambda x:'%s' not in x.groups and predicate(x)"
- else:
- cmd = "lambda x:'%s' in x.groups and predicate(x)"
-
- newpred = eval(cmd % (group.get('name')), {'predicate':predicate})
- work[newpred] = group.getchildren()
+ self.fragments[predicate] = \
+ [item for item in worklist
+ if (item.tag != 'Group' and
+ item.tag != 'Client' and
+ not isinstance(item,
+ lxml.etree._Comment))]
+ for item in worklist:
+ cmd = None
+ if item.tag == 'Group':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:'%s' not in x.groups and predicate(x)"
+ else:
+ cmd = "lambda x:'%s' in x.groups and predicate(x)"
+ elif item.tag == 'Client':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:x.hostname != '%s' and predicate(x)"
+ else:
+ cmd = "lambda x:x.hostname == '%s' and predicate(x)"
+ # else, ignore item
+ if cmd is not None:
+ newpred = eval(cmd % item.get('name'),
+ {'predicate':predicate})
+ work[newpred] = item.getchildren()
def Match(self, metadata):
"""Return matching fragments of independent."""
- matching = [frag for (pred, frag) in self.fragments.iteritems() if pred(metadata)]
+ matching = [frag for (pred, frag) in list(self.fragments.items())
+ if pred(metadata)]
if matching:
- return reduce(lambda x, y:x+y, matching)
+ return reduce(lambda x, y: x + y, matching)
logger.error("File %s got null match" % (self.name))
return []
+
class INode:
"""
LNodes provide lists of things available at a particular
group intersection.
"""
- raw = {'Client':"lambda x:'%s' == x.hostname and predicate(x)",
- 'Group':"lambda x:'%s' in x.groups and predicate(x)"}
- nraw = {'Client':"lambda x:'%s' != x.hostname and predicate(x)",
- 'Group':"lambda x:'%s' not in x.groups and predicate(x)"}
+ raw = {'Client': "lambda x:'%s' == x.hostname and predicate(x)",
+ 'Group': "lambda x:'%s' in x.groups and predicate(x)"}
+ nraw = {'Client': "lambda x:'%s' != x.hostname and predicate(x)",
+ 'Group': "lambda x:'%s' not in x.groups and predicate(x)"}
containers = ['Group', 'Client']
ignore = []
@@ -468,16 +520,16 @@ class INode:
self.data = data
self.contents = {}
if parent == None:
- self.predicate = lambda x:True
+ self.predicate = lambda x: True
else:
predicate = parent.predicate
if data.get('negate', 'false') in ['true', 'True']:
psrc = self.nraw
else:
psrc = self.raw
- if data.tag in psrc.keys():
+ if data.tag in list(psrc.keys()):
self.predicate = eval(psrc[data.tag] % (data.get('name')),
- {'predicate':predicate})
+ {'predicate': predicate})
else:
raise Exception
mytype = self.__class__
@@ -491,7 +543,7 @@ class INode:
try:
self.contents[item.tag][item.get('name')] = item.attrib
except KeyError:
- self.contents[item.tag] = {item.get('name'):item.attrib}
+ self.contents[item.tag] = {item.get('name'): item.attrib}
if item.text:
self.contents[item.tag]['__text__'] = item.text
try:
@@ -511,6 +563,7 @@ class INode:
for child in self.children:
child.Match(metadata, data)
+
class XMLSrc(XMLFileBacked):
"""XMLSrc files contain a LNode hierarchy that returns matching entries."""
__node__ = INode
@@ -527,7 +580,7 @@ class XMLSrc(XMLFileBacked):
def HandleEvent(self, _=None):
"""Read file upon update."""
try:
- data = file(self.name).read()
+ data = BUILTIN_FILE_TYPE(self.name).read()
except IOError:
logger.error("Failed to read file %s" % (self.name))
return
@@ -557,10 +610,12 @@ class XMLSrc(XMLFileBacked):
self.pnode.Match(metadata, cache[1])
self.cache = cache
+
class XMLDirectoryBacked(DirectoryBacked):
"""Directorybacked for *.xml."""
patterns = re.compile('.*\.xml')
+
class PrioDir(Plugin, Generator, XMLDirectoryBacked):
"""This is a generator that handles package assignments."""
name = 'PrioDir'
@@ -579,8 +634,8 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
"""Handle events and update dispatch table."""
XMLDirectoryBacked.HandleEvent(self, event)
self.Entries = {}
- for src in self.entries.values():
- for itype, children in src.items.iteritems():
+ for src in list(self.entries.values()):
+ for itype, children in list(src.items.items()):
for child in children:
try:
self.Entries[itype][child] = self.BindEntry
@@ -589,14 +644,14 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
def BindEntry(self, entry, metadata):
"""Check package lists of package entries."""
- [src.Cache(metadata) for src in self.entries.values()]
+ [src.Cache(metadata) for src in list(self.entries.values())]
name = entry.get('name')
if not src.cache:
self.logger.error("Called before data loaded")
raise PluginExecutionError
- matching = [src for src in self.entries.values()
+ matching = [src for src in list(self.entries.values())
if src.cache and entry.tag in src.cache[1]
- and src.cache[1][entry.tag].has_key(name)]
+ and name in src.cache[1][entry.tag]]
if len(matching) == 0:
raise PluginExecutionError
elif len(matching) == 1:
@@ -618,15 +673,17 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
entry.text = data['__text__']
if '__children__' in data:
[entry.append(copy.deepcopy(item)) for item in data['__children__']]
- [entry.attrib.__setitem__(key, data[key]) for key in data.keys() \
+ [entry.attrib.__setitem__(key, data[key]) for key in list(data.keys()) \
if not key.startswith('__')]
+
# new unified EntrySet backend
class SpecificityError(Exception):
"""Thrown in case of filename parse failure."""
pass
+
class Specificity:
def __init__(self, all=False, group=False, hostname=False, prio=0, delta=False):
@@ -665,6 +722,7 @@ class Specificity:
return True
return False
+
class SpecificData(object):
def __init__(self, name, specific, encoding):
self.name = name
@@ -678,9 +736,11 @@ class SpecificData(object):
except:
logger.error("Failed to read file %s" % self.name)
+
class EntrySet:
"""Entry sets deal with the host- and group-specific entries."""
ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\\.genshi_include)$")
+
def __init__(self, basename, path, entry_type, encoding):
self.path = path
self.entry_type = entry_type
@@ -693,7 +753,7 @@ class EntrySet:
self.specific = re.compile(pattern)
def get_matching(self, metadata):
- return [item for item in self.entries.values() \
+ return [item for item in list(self.entries.values()) \
if item.specific.matches(metadata)]
def handle_event(self, event):
@@ -761,11 +821,11 @@ class EntrySet:
for line in open(fpath).readlines():
match = info_regex.match(line)
if not match:
- logger.warning("Failed to match line: %s"%line)
+ logger.warning("Failed to match line: %s" % line)
continue
else:
mgd = match.groupdict()
- for key, value in mgd.iteritems():
+ for key, value in list(mgd.items()):
if value:
self.metadata[key] = value
if len(self.metadata['perms']) == 3:
@@ -795,7 +855,7 @@ class EntrySet:
(entry.get('name')))
raise PluginExecutionError
[entry.attrib.__setitem__(key, value) \
- for (key, value) in mdata['Info'][None].iteritems()]
+ for (key, value) in list(mdata['Info'][None].items())]
def bind_entry(self, entry, metadata):
"""Return the appropriate interpreted template from the set of available templates."""
@@ -817,6 +877,7 @@ class EntrySet:
raise PluginExecutionError
+
class GroupSpool(Plugin, Generator):
"""Unified interface for handling group-specific data (e.g. .G## files)."""
name = 'GroupSpool'
@@ -878,9 +939,9 @@ class GroupSpool(Plugin, Generator):
if not relative.endswith('/'):
relative += '/'
name = self.data + relative
- if relative not in self.handles.values():
+ if relative not in list(self.handles.values()):
if not posixpath.isdir(name):
- print "Failed to open directory %s" % (name)
+ print("Failed to open directory %s" % (name))
return
reqid = self.core.fam.AddMonitor(name, self)
self.handles[reqid] = relative
diff --git a/src/lib/Server/Plugins/Account.py b/src/lib/Server/Plugins/Account.py
index e3ea58761..f67819b9d 100644
--- a/src/lib/Server/Plugins/Account.py
+++ b/src/lib/Server/Plugins/Account.py
@@ -3,6 +3,7 @@ __revision__ = '$Revision$'
import Bcfg2.Server.Plugin
+
class Account(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Generator):
"""This module generates account config files,
@@ -21,13 +22,14 @@ class Account(Bcfg2.Server.Plugin.Plugin,
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Generator.__init__(self)
- self.Entries = {'ConfigFile':{'/etc/passwd':self.from_yp_cb,
- '/etc/group':self.from_yp_cb,
- '/etc/security/limits.conf':self.gen_limits_cb,
- '/root/.ssh/authorized_keys':self.gen_root_keys_cb,
- '/etc/sudoers':self.gen_sudoers}}
+ self.Entries = {'ConfigFile': {'/etc/passwd': self.from_yp_cb,
+ '/etc/group': self.from_yp_cb,
+ '/etc/security/limits.conf': self.gen_limits_cb,
+ '/root/.ssh/authorized_keys': self.gen_root_keys_cb,
+ '/etc/sudoers': self.gen_sudoers}}
try:
- self.repository = Bcfg2.Server.Plugin.DirectoryBacked(self.data, self.core.fam)
+ self.repository = Bcfg2.Server.Plugin.DirectoryBacked(self.data,
+ self.core.fam)
except:
self.logger.error("Failed to load repos: %s, %s" % \
(self.data, "%s/ssh" % (self.data)))
@@ -38,9 +40,11 @@ class Account(Bcfg2.Server.Plugin.Plugin,
fname = entry.attrib['name'].split('/')[-1]
entry.text = self.repository.entries["static.%s" % (fname)].data
entry.text += self.repository.entries["dyn.%s" % (fname)].data
- perms = {'owner':'root', 'group':'root', 'perms':'0644'}
+ perms = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '0644'}
[entry.attrib.__setitem__(key, value) for (key, value) in \
- perms.iteritems()]
+ list(perms.items())]
def gen_limits_cb(self, entry, metadata):
"""Build limits entries based on current ACLs."""
@@ -50,9 +54,11 @@ class Account(Bcfg2.Server.Plugin.Plugin,
self.repository.entries["useraccess"].data.split()]
users = [user for (user, host) in \
useraccess if host == metadata.hostname.split('.')[0]]
- perms = {'owner':'root', 'group':'root', 'perms':'0600'}
+ perms = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '0600'}
[entry.attrib.__setitem__(key, value) for (key, value) in \
- perms.iteritems()]
+ list(perms.items())]
entry.text += "".join(["%s hard maxlogins 1024\n" % uname for uname in superusers + users])
if "*" not in users:
entry.text += "* hard maxlogins 0\n"
@@ -71,9 +77,11 @@ class Account(Bcfg2.Server.Plugin.Plugin,
entry.text = "".join([rdata["%s.key" % user].data for user \
in superusers if \
("%s.key" % user) in rdata])
- perms = {'owner':'root', 'group':'root', 'perms':'0600'}
+ perms = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '0600'}
[entry.attrib.__setitem__(key, value) for (key, value) \
- in perms.iteritems()]
+ in list(perms.items())]
def gen_sudoers(self, entry, metadata):
"""Build root authorized keys file based on current ACLs."""
@@ -88,6 +96,8 @@ class Account(Bcfg2.Server.Plugin.Plugin,
entry.text = self.repository.entries['static.sudoers'].data
entry.text += "".join(["%s ALL=(ALL) ALL\n" % uname \
for uname in superusers])
- perms = {'owner':'root', 'group':'root', 'perms':'0440'}
+ perms = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '0440'}
[entry.attrib.__setitem__(key, value) for (key, value) \
- in perms.iteritems()]
+ in list(perms.items())]
diff --git a/src/lib/Server/Plugins/Base.py b/src/lib/Server/Plugins/Base.py
index 8e5ca1cd9..5e7d89727 100644
--- a/src/lib/Server/Plugins/Base.py
+++ b/src/lib/Server/Plugins/Base.py
@@ -1,9 +1,15 @@
"""This module sets up a base list of configuration entries."""
__revision__ = '$Revision$'
-import Bcfg2.Server.Plugin
import copy
import lxml.etree
+import sys
+# py3k compatibility
+if sys.hexversion >= 0x03000000:
+ from functools import reduce
+
+import Bcfg2.Server.Plugin
+
class Base(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Structure,
@@ -31,8 +37,8 @@ class Base(Bcfg2.Server.Plugin.Plugin,
def BuildStructures(self, metadata):
"""Build structures for client described by metadata."""
ret = lxml.etree.Element("Independent", version='2.0')
- fragments = reduce(lambda x, y: x+y,
+ fragments = reduce(lambda x, y: x + y,
[base.Match(metadata) for base
- in self.entries.values()], [])
+ in list(self.entries.values())], [])
[ret.append(copy.deepcopy(frag)) for frag in fragments]
return [ret]
diff --git a/src/lib/Server/Plugins/Bundler.py b/src/lib/Server/Plugins/Bundler.py
index 3f88fe26b..01ad3c78b 100644
--- a/src/lib/Server/Plugins/Bundler.py
+++ b/src/lib/Server/Plugins/Bundler.py
@@ -4,6 +4,7 @@ __revision__ = '$Revision$'
import copy
import lxml.etree
import re
+import sys
import Bcfg2.Server.Plugin
@@ -73,14 +74,15 @@ class Bundler(Bcfg2.Server.Plugin.Plugin,
"""Build all structures for client (metadata)."""
bundleset = []
for bundlename in metadata.bundles:
- entries = [item for (key, item) in self.entries.iteritems() if \
+ entries = [item for (key, item) in list(self.entries.items()) if \
self.patterns.match(key).group('name') == bundlename]
if len(entries) == 0:
continue
elif len(entries) == 1:
try:
bundleset.append(entries[0].get_xml_value(metadata))
- except genshi.template.base.TemplateError, t:
+ except genshi.template.base.TemplateError:
+ t = sys.exc_info()[1]
self.logger.error("Bundler: Failed to template genshi bundle %s" \
% (bundlename))
self.logger.error(t)
diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py
index f851b7914..41cf6c9c1 100644
--- a/src/lib/Server/Plugins/Cfg.py
+++ b/src/lib/Server/Plugins/Cfg.py
@@ -6,6 +6,7 @@ import logging
import lxml
import os
import re
+import sys
import tempfile
import Bcfg2.Server.Plugin
@@ -13,15 +14,21 @@ import Bcfg2.Server.Plugin
try:
import genshi.core
import genshi.input
- from genshi.template import TemplateLoader, \
- TextTemplate, MarkupTemplate, TemplateError
- from genshi.template import NewTextTemplate
+ from genshi.template import TemplateLoader, NewTextTemplate
have_genshi = True
except:
have_genshi = False
logger = logging.getLogger('Bcfg2.Plugins.Cfg')
+
+def u_str(string, encoding):
+ if sys.hexversion >= 0x03000000:
+ return str(string, encoding)
+ else:
+ return unicode(string, encoding)
+
+
# snipped from TGenshi
def removecomment(stream):
"""A genshi filter that removes comments from the stream."""
@@ -30,6 +37,7 @@ def removecomment(stream):
continue
yield kind, data, pos
+
def process_delta(data, delta):
if not delta.specific.delta:
return data
@@ -60,13 +68,15 @@ def process_delta(data, delta):
output = open(basefile.name, 'r').read()
[os.unlink(fname) for fname in [basefile.name, dfile.name]]
if ret >> 8 != 0:
- raise Bcfg2.Server.Plugin.PluginExecutionError, ('delta', delta)
+ raise Bcfg2.Server.Plugin.PluginExecutionError('delta', delta)
return output
+
class CfgMatcher:
+
def __init__(self, fname):
name = re.escape(fname)
- self.basefile_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+)|.G(?P<prio>\d+)_(?P<group>\S+))(?P<genshi>\\.genshi)?$' % name)
+ self.basefile_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+?)|.G(?P<prio>\d+)_(?P<group>\S+?))(?P<genshi>\\.genshi)?$' % name)
self.delta_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+)|\\.G(?P<prio>\d+)_(?P<group>\S+))\\.(?P<delta>(cat|diff))$' % name)
self.cat_count = fname.count(".cat")
self.diff_count = fname.count(".diff")
@@ -77,7 +87,9 @@ class CfgMatcher:
return self.delta_reg.match(fname)
return self.basefile_reg.match(fname)
+
class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
+
def __init__(self, basename, path, entry_type, encoding):
Bcfg2.Server.Plugin.EntrySet.__init__(self, basename, path,
entry_type, encoding)
@@ -87,15 +99,18 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
return cmp(one.specific, other.specific)
def get_pertinent_entries(self, metadata):
- '''return a list of all entries pertinent to a client => [base, delta1, delta2]'''
- matching = [ent for ent in self.entries.values() if \
+ """return a list of all entries pertinent
+ to a client => [base, delta1, delta2]
+ """
+ matching = [ent for ent in list(self.entries.values()) if \
ent.specific.matches(metadata)]
matching.sort(self.sort_by_specific)
- non_delta = [matching.index(m) for m in matching if not m.specific.delta]
+ non_delta = [matching.index(m) for m in matching
+ if not m.specific.delta]
if not non_delta:
raise Bcfg2.Server.Plugin.PluginExecutionError
base = min(non_delta)
- used = matching[:base+1]
+ used = matching[:base + 1]
used.reverse()
return used
@@ -113,17 +128,19 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
template_cls = NewTextTemplate
loader = TemplateLoader()
template = loader.load(basefile.name, cls=template_cls,
- encoding=self.encoding)
- stream = template.generate( \
- name=entry.get('name'), metadata=metadata,
- path=basefile.name).filter(removecomment)
+ encoding=self.encoding)
+ fname = entry.get('realname', entry.get('name'))
+ stream = template.generate(name=fname,
+ metadata=metadata,
+ path=basefile.name).filter(removecomment)
try:
data = stream.render('text', strip_whitespace=False)
except TypeError:
data = stream.render('text')
if data == '':
entry.set('empty', 'true')
- except Exception, e:
+ except Exception:
+ e = sys.exc_info()[1]
logger.error("Cfg: genshi exception: %s" % e)
raise Bcfg2.Server.Plugin.PluginExecutionError
else:
@@ -136,7 +153,13 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
if entry.get('encoding') == 'base64':
entry.text = binascii.b2a_base64(data)
else:
- entry.text = unicode(data, self.encoding)
+ try:
+ entry.text = u_str(data, self.encoding)
+ except UnicodeDecodeError:
+ e = sys.exc_info()[1]
+ logger.error("Failed to decode %s: %s" % (entry.get('name'), e))
+ logger.error("Please verify you are using the proper encoding.")
+ raise Bcfg2.Server.Plugin.PluginExecutionError
if entry.text in ['', None]:
entry.set('empty', 'true')
@@ -168,7 +191,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
open(name, 'w').write(new_entry['text'])
if log:
logger.info("Wrote file %s" % name)
- badattr = [attr for attr in ['owner', 'group', 'perms'] if attr in new_entry]
+ badattr = [attr for attr in ['owner', 'group', 'perms']
+ if attr in new_entry]
if badattr:
metadata_updates = {}
metadata_updates.update(self.metadata)
@@ -178,12 +202,13 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
infotag = lxml.etree.SubElement(infoxml, 'Info')
[infotag.attrib.__setitem__(attr, metadata_updates[attr]) \
for attr in metadata_updates]
- ofile = open(self.path + "/info.xml","w")
+ ofile = open(self.path + "/info.xml", "w")
ofile.write(lxml.etree.tostring(infoxml, pretty_print=True))
ofile.close()
if log:
logger.info("Wrote file %s" % (self.path + "/info.xml"))
+
class Cfg(Bcfg2.Server.Plugin.GroupSpool,
Bcfg2.Server.Plugin.PullTarget):
"""This generator in the configuration file repository for Bcfg2."""
@@ -197,4 +222,6 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
return self.entries[entry.get('name')].list_accept_choices(metadata)
def AcceptPullData(self, specific, new_entry, log):
- return self.entries[new_entry.get('name')].write_update(specific, new_entry, log)
+ return self.entries[new_entry.get('name')].write_update(specific,
+ new_entry,
+ log)
diff --git a/src/lib/Server/Plugins/DBStats.py b/src/lib/Server/Plugins/DBStats.py
index 27696a978..5ef1920e1 100644
--- a/src/lib/Server/Plugins/DBStats.py
+++ b/src/lib/Server/Plugins/DBStats.py
@@ -33,7 +33,8 @@ class DBStats(Bcfg2.Server.Plugin.Plugin,
logger.debug("Searching for new models to add to the statistics database")
try:
update_database()
- except Exception, inst:
+ except Exception:
+ inst = sys.exc_info()[1]
logger.debug(str(inst))
logger.debug(str(type(inst)))
@@ -61,7 +62,8 @@ class DBStats(Bcfg2.Server.Plugin.Plugin,
logger.info("Imported data for %s in %s seconds" \
% (metadata.hostname, time.time() - start))
return
- except MultipleObjectsReturned, e:
+ except MultipleObjectsReturned:
+ e = sys.exc_info()[1]
logger.error("DBStats: MultipleObjectsReturned while handling %s: %s" % \
(metadata.hostname, e))
logger.error("DBStats: Data is inconsistent")
diff --git a/src/lib/Server/Plugins/Decisions.py b/src/lib/Server/Plugins/Decisions.py
index 1f9525a0e..e239be5ee 100644
--- a/src/lib/Server/Plugins/Decisions.py
+++ b/src/lib/Server/Plugins/Decisions.py
@@ -26,7 +26,8 @@ class DecisionSet(Bcfg2.Server.Plugin.EntrySet):
DecisionFile, encoding)
try:
fam.AddMonitor(path, self)
- except OSError, e:
+ except OSError:
+ e = sys.exc_info()[1]
logger.error('Adding filemonitor for %s failed. '
'Make sure directory exists' % path)
raise Bcfg2.Server.Plugin.PluginInitError(e)
diff --git a/src/lib/Server/Plugins/Deps.py b/src/lib/Server/Plugins/Deps.py
index 088f8cdad..b186258cb 100644
--- a/src/lib/Server/Plugins/Deps.py
+++ b/src/lib/Server/Plugins/Deps.py
@@ -5,20 +5,22 @@ import lxml.etree
import Bcfg2.Server.Plugin
+
class DNode(Bcfg2.Server.Plugin.INode):
"""DNode provides supports for single predicate types for dependencies."""
- raw = {'Group':"lambda x:'%s' in x.groups and predicate(x)"}
+ raw = {'Group': "lambda x:'%s' in x.groups and predicate(x)"}
containers = ['Group']
def __init__(self, data, idict, parent=None):
self.data = data
self.contents = {}
if parent == None:
- self.predicate = lambda x:True
+ self.predicate = lambda x: True
else:
predicate = parent.predicate
- if data.tag in self.raw.keys():
- self.predicate = eval(self.raw[data.tag] % (data.get('name')), {'predicate':predicate})
+ if data.tag in list(self.raw.keys()):
+ self.predicate = eval(self.raw[data.tag] % (data.get('name')),
+ {'predicate': predicate})
else:
raise Exception
mytype = self.__class__
@@ -27,15 +29,18 @@ class DNode(Bcfg2.Server.Plugin.INode):
if item.tag in self.containers:
self.children.append(mytype(item, idict, self))
else:
- data = [(child.tag, child.get('name')) for child in item.getchildren()]
+ data = [(child.tag, child.get('name'))
+ for child in item.getchildren()]
try:
self.contents[item.tag][item.get('name')] = data
except KeyError:
- self.contents[item.tag] = {item.get('name'):data}
+ self.contents[item.tag] = {item.get('name'): data}
+
class DepXMLSrc(Bcfg2.Server.Plugin.XMLSrc):
__node__ = DNode
+
class Deps(Bcfg2.Server.Plugin.PrioDir,
Bcfg2.Server.Plugin.StructureValidator):
name = 'Deps'
@@ -68,12 +73,12 @@ class Deps(Bcfg2.Server.Plugin.PrioDir,
if (entries, gdata) in self.cache:
prereqs = self.cache[(entries, gdata)]
else:
- [src.Cache(metadata) for src in self.entries.values()]
+ [src.Cache(metadata) for src in list(self.entries.values())]
toexamine = list(entries[:])
while toexamine:
entry = toexamine.pop()
- matching = [src for src in self.entries.values()
+ matching = [src for src in list(self.entries.values())
if src.cache and entry[0] in src.cache[1]
and entry[1] in src.cache[1][entry[0]]]
if len(matching) > 1:
diff --git a/src/lib/Server/Plugins/Editor.py b/src/lib/Server/Plugins/Editor.py
index bfd4d6e93..76a03a325 100644
--- a/src/lib/Server/Plugins/Editor.py
+++ b/src/lib/Server/Plugins/Editor.py
@@ -2,6 +2,7 @@ import Bcfg2.Server.Plugin
import re
import lxml.etree
+
def linesub(pattern, repl, filestring):
"""Substitutes instances of pattern with repl in filestring."""
if filestring == None:
@@ -12,6 +13,7 @@ def linesub(pattern, repl, filestring):
output.append(re.sub(pattern, repl, filestring))
return '\n'.join(output)
+
class EditDirectives(Bcfg2.Server.Plugin.SpecificData):
"""This object handles the editing directives."""
def ProcessDirectives(self, input):
@@ -22,23 +24,29 @@ class EditDirectives(Bcfg2.Server.Plugin.SpecificData):
temp = linesub(directive[0], directive[1], temp)
return temp
+
class EditEntrySet(Bcfg2.Server.Plugin.EntrySet):
def __init__(self, basename, path, entry_type, encoding):
- self.ignore = re.compile("^(\.#.*|.*~|\\..*\\.(tmp|sw[px])|%s\.H_.*)$" %path.split('/')[-1])
- Bcfg2.Server.Plugin.EntrySet.__init__(self, basename, path, entry_type, encoding)
+ self.ignore = re.compile("^(\.#.*|.*~|\\..*\\.(tmp|sw[px])|%s\.H_.*)$" % path.split('/')[-1])
+ Bcfg2.Server.Plugin.EntrySet.__init__(self,
+ basename,
+ path,
+ entry_type,
+ encoding)
self.inputs = dict()
def bind_entry(self, entry, metadata):
client = metadata.hostname
filename = entry.get('name')
- permdata = {'owner':'root', 'group':'root'}
- permdata['perms'] = '0644'
+ permdata = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '0644'}
[entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
entry.text = self.entries['edits'].ProcessDirectives(self.get_client_data(client))
if not entry.text:
entry.set('empty', 'true')
try:
- f = open('%s/%s.H_%s' %(self.path, filename.split('/')[-1], client), 'w')
+ f = open('%s/%s.H_%s' % (self.path, filename.split('/')[-1], client), 'w')
f.write(entry.text)
f.close()
except:
@@ -60,7 +68,7 @@ class Editor(Bcfg2.Server.Plugin.GroupSpool,
def GetProbes(self, _):
'''Return a set of probes for execution on client'''
probelist = list()
- for name in self.entries.keys():
+ for name in list(self.entries.keys()):
probe = lxml.etree.Element('probe')
probe.set('name', name)
probe.set('source', "Editor")
diff --git a/src/lib/Server/Plugins/GroupPatterns.py b/src/lib/Server/Plugins/GroupPatterns.py
index 553f9d286..7faead39a 100644
--- a/src/lib/Server/Plugins/GroupPatterns.py
+++ b/src/lib/Server/Plugins/GroupPatterns.py
@@ -3,6 +3,7 @@ import re
import Bcfg2.Server.Plugin
+
class PackedDigitRange(object):
def __init__(self, digit_range):
self.sparse = list()
@@ -18,12 +19,14 @@ class PackedDigitRange(object):
if iother in self.sparse:
return True
for (start, end) in self.ranges:
- if iother in xrange(start, end+1):
+ if iother in range(start, end + 1):
return True
return False
+
class PatternMap(object):
range_finder = '\\[\\[[\d\-,]+\\]\\]'
+
def __init__(self, pattern, rangestr, groups):
self.pattern = pattern
self.rangestr = rangestr
@@ -33,8 +36,11 @@ class PatternMap(object):
self.process = self.process_re
elif rangestr != None:
self.process = self.process_range
- self.re = re.compile('^' + re.subn(self.range_finder, '(\d+)', rangestr)[0])
- dmatcher = re.compile(re.subn(self.range_finder, '\\[\\[([\d\-,]+)\\]\\]', rangestr)[0])
+ self.re = re.compile('^' + re.subn(self.range_finder, '(\d+)',
+ rangestr)[0])
+ dmatcher = re.compile(re.subn(self.range_finder,
+ '\\[\\[([\d\-,]+)\\]\\]',
+ rangestr)[0])
self.dranges = [PackedDigitRange(x) for x in dmatcher.match(rangestr).groups()]
else:
raise Exception
@@ -58,10 +64,11 @@ class PatternMap(object):
for group in self.groups:
newg = group
for idx in range(len(sub)):
- newg = newg.replace('$%s' % (idx+1), sub[idx])
+ newg = newg.replace('$%s' % (idx + 1), sub[idx])
ret.append(newg)
return ret
+
class PatternFile(Bcfg2.Server.Plugin.SingleXMLFileBacked):
def __init__(self, filename, fam):
Bcfg2.Server.Plugin.SingleXMLFileBacked.__init__(self, filename, fam)
@@ -101,6 +108,7 @@ class PatternFile(Bcfg2.Server.Plugin.SingleXMLFileBacked):
(pattern.pattern, hostname), exc_info=1)
return ret
+
class GroupPatterns(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector):
name = "GroupPatterns"
diff --git a/src/lib/Server/Plugins/Hostbase.py b/src/lib/Server/Plugins/Hostbase.py
index 65992596d..4180fd716 100644
--- a/src/lib/Server/Plugins/Hostbase.py
+++ b/src/lib/Server/Plugins/Hostbase.py
@@ -1,4 +1,7 @@
-'''This file provides the Hostbase plugin. It manages dns/dhcp/nis host information'''
+"""
+This file provides the Hostbase plugin.
+It manages dns/dhcp/nis host information
+"""
__revision__ = '$Revision$'
import os
@@ -11,7 +14,9 @@ from sets import Set
from django.template import Context, loader
from django.db import connection
import re
-import cStringIO
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import StringIO
+
class Hostbase(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Structure,
@@ -23,24 +28,29 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
filepath = '/my/adm/hostbase/files/bind'
def __init__(self, core, datastore):
-
+
self.ready = False
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Structure.__init__(self)
Bcfg2.Server.Plugin.Generator.__init__(self)
- files = ['zone.tmpl', 'reversesoa.tmpl', 'named.tmpl', 'reverseappend.tmpl',
- 'dhcpd.tmpl', 'hosts.tmpl', 'hostsappend.tmpl']
+ files = ['zone.tmpl',
+ 'reversesoa.tmpl',
+ 'named.tmpl',
+ 'reverseappend.tmpl',
+ 'dhcpd.tmpl',
+ 'hosts.tmpl',
+ 'hostsappend.tmpl']
self.filedata = {}
self.dnsservers = []
self.dhcpservers = []
- self.templates = {'zone':loader.get_template('zone.tmpl'),
- 'reversesoa':loader.get_template('reversesoa.tmpl'),
- 'named':loader.get_template('named.tmpl'),
- 'namedviews':loader.get_template('namedviews.tmpl'),
- 'reverseapp':loader.get_template('reverseappend.tmpl'),
- 'dhcp':loader.get_template('dhcpd.tmpl'),
- 'hosts':loader.get_template('hosts.tmpl'),
- 'hostsapp':loader.get_template('hostsappend.tmpl'),
+ self.templates = {'zone': loader.get_template('zone.tmpl'),
+ 'reversesoa': loader.get_template('reversesoa.tmpl'),
+ 'named': loader.get_template('named.tmpl'),
+ 'namedviews': loader.get_template('namedviews.tmpl'),
+ 'reverseapp': loader.get_template('reverseappend.tmpl'),
+ 'dhcp': loader.get_template('dhcpd.tmpl'),
+ 'hosts': loader.get_template('hosts.tmpl'),
+ 'hostsapp': loader.get_template('hostsappend.tmpl'),
}
self.Entries['ConfigFile'] = {}
self.__rmi__ = ['rebuildState']
@@ -48,14 +58,17 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
self.rebuildState(None)
except:
raise PluginInitError
-
+
def FetchFile(self, entry, metadata):
"""Return prebuilt file data."""
fname = entry.get('name').split('/')[-1]
if not fname in self.filedata:
raise PluginExecutionError
- perms = {'owner':'root', 'group':'root', 'perms':'644'}
- [entry.attrib.__setitem__(key, value) for (key, value) in perms.iteritems()]
+ perms = {'owner': 'root',
+ 'group': 'root',
+ 'perms': '644'}
+ [entry.attrib.__setitem__(key, value)
+ for (key, value) in list(perms.items())]
entry.text = self.filedata[fname]
def BuildStructures(self, metadata):
@@ -110,8 +123,8 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
hosts = {}
for zone in zones:
- zonefile = cStringIO.StringIO()
- externalzonefile = cStringIO.StringIO()
+ zonefile = StringIO()
+ externalzonefile = StringIO()
cursor.execute("""SELECT n.name FROM hostbase_zone_nameservers z
INNER JOIN hostbase_nameserver n ON z.nameserver_id = n.id
WHERE z.zone_id = \'%s\'""" % zone[0])
@@ -148,20 +161,20 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
cursor.execute(querystring)
zonehosts = cursor.fetchall()
prevhost = (None, None, None, None)
- cnames = cStringIO.StringIO()
- cnamesexternal = cStringIO.StringIO()
+ cnames = StringIO()
+ cnamesexternal = StringIO()
for host in zonehosts:
if not host[2].split(".", 1)[1] == zone[1]:
zonefile.write(cnames.getvalue())
externalzonefile.write(cnamesexternal.getvalue())
- cnames = cStringIO.StringIO()
- cnamesexternal = cStringIO.StringIO()
+ cnames = StringIO()
+ cnamesexternal = StringIO()
continue
if not prevhost[1] == host[1] or not prevhost[2] == host[2]:
zonefile.write(cnames.getvalue())
externalzonefile.write(cnamesexternal.getvalue())
- cnames = cStringIO.StringIO()
- cnamesexternal = cStringIO.StringIO()
+ cnames = StringIO()
+ cnamesexternal = StringIO()
zonefile.write("%-32s%-10s%-32s\n" %
(host[2].split(".", 1)[0], 'A', host[1]))
zonefile.write("%-32s%-10s%-3s%s.\n" %
@@ -173,29 +186,29 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
('', 'MX', host[4], host[5]))
elif not prevhost[5] == host[5]:
zonefile.write("%-32s%-10s%-3s%s.\n" %
- ('', 'MX', host[4], host[5]))
+ ('', 'MX', host[4], host[5]))
if host[6] == 'global':
externalzonefile.write("%-32s%-10s%-3s%s.\n" %
('', 'MX', host[4], host[5]))
-
+
if host[3]:
try:
if host[3].split(".", 1)[1] == zone[1]:
cnames.write("%-32s%-10s%-32s\n" %
(host[3].split(".", 1)[0],
- 'CNAME',host[2].split(".", 1)[0]))
+ 'CNAME', host[2].split(".", 1)[0]))
if host[6] == 'global':
cnamesexternal.write("%-32s%-10s%-32s\n" %
(host[3].split(".", 1)[0],
- 'CNAME',host[2].split(".", 1)[0]))
+ 'CNAME', host[2].split(".", 1)[0]))
else:
cnames.write("%-32s%-10s%-32s\n" %
- (host[3]+".",
+ (host[3] + ".",
'CNAME',
host[2].split(".", 1)[0]))
if host[6] == 'global':
cnamesexternal.write("%-32s%-10s%-32s\n" %
- (host[3]+".",
+ (host[3] + ".",
'CNAME',
host[2].split(".", 1)[0]))
@@ -215,9 +228,9 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
cursor.execute("SELECT * FROM hostbase_zone WHERE zone LIKE \'%%.rev\' AND zone <> \'.rev\'")
reversezones = cursor.fetchall()
-
+
reversenames = []
- for reversezone in reversezones:
+ for reversezone in reversezones:
cursor.execute("""SELECT n.name FROM hostbase_zone_nameservers z
INNER JOIN hostbase_nameserver n ON z.nameserver_id = n.id
WHERE z.zone_id = \'%s\'""" % reversezone[0])
@@ -236,7 +249,7 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
subnet = reversezone[1].split(".")
subnet.reverse()
- reversenames.append((reversezone[1].rstrip('.rev'),".".join(subnet[1:])))
+ reversenames.append((reversezone[1].rstrip('.rev'), ".".join(subnet[1:])))
for filename in reversenames:
cursor.execute("""
@@ -247,8 +260,8 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
WHERE p.ip_addr LIKE '%s%%%%' AND h.status = 'active' ORDER BY p.ip_addr
""" % filename[1])
reversehosts = cursor.fetchall()
- zonefile = cStringIO.StringIO()
- externalzonefile = cStringIO.StringIO()
+ zonefile = StringIO()
+ externalzonefile = StringIO()
if len(filename[0].split(".")) == 2:
originlist = []
[originlist.append((".".join([ip[1].split(".")[2], filename[0]]),
@@ -268,13 +281,13 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
'hosts': hosts,
'inaddr': origin[0],
'fileorigin': filename[0],
- })
+ })
zonefile.write(self.templates['reverseapp'].render(context))
context = Context({
'hosts': hosts_external,
'inaddr': origin[0],
'fileorigin': filename[0],
- })
+ })
externalzonefile.write(self.templates['reverseapp'].render(context))
else:
originlist = [filename[0]]
@@ -289,7 +302,7 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
'hosts': hosts,
'inaddr': filename[0],
'fileorigin': None,
- })
+ })
zonefile.write(self.templates['reverseapp'].render(context))
context = Context({
'hosts': hosts_external,
@@ -308,13 +321,12 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
context = Context({
'zones': zones,
'reverses': reversenames,
- })
+ })
self.filedata['named.conf'] = self.templates['named'].render(context)
self.Entries['ConfigFile']['/my/adm/hostbase/files/named.conf'] = self.FetchFile
self.filedata['named.conf.views'] = self.templates['namedviews'].render(context)
self.Entries['ConfigFile']['/my/adm/hostbase/files/named.conf.views'] = self.FetchFile
-
def buildDHCP(self):
"""Pre-build dhcpd.conf and stash in the filedata table."""
@@ -362,7 +374,6 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
self.filedata['dhcpd.conf'] = self.templates['dhcp'].render(context)
self.Entries['ConfigFile']['/my/adm/hostbase/files/dhcpd.conf'] = self.FetchFile
-
def buildHosts(self):
"""Pre-build and stash /etc/hosts file."""
@@ -490,7 +501,7 @@ Name Room User Type
def buildHostsLPD(self):
"""Creates the /mcs/etc/hosts.lpd file"""
-
+
# this header needs to be changed to be more generic
header = """+@machines
+@all-machines
@@ -503,7 +514,7 @@ delphi.esh.anl.gov
anlcv1.ctd.anl.gov
anlvms.ctd.anl.gov
olivia.ctd.anl.gov\n\n"""
-
+
cursor = connection.cursor()
cursor.execute("""
SELECT hostname FROM hostbase_host WHERE netgroup=\"red\" AND status = 'active'
@@ -534,7 +545,6 @@ olivia.ctd.anl.gov\n\n"""
self.filedata['hosts.lpd'] = hostslpdfile
self.Entries['ConfigFile']['/mcs/etc/hosts.lpd'] = self.FetchFile
-
def buildNetgroups(self):
"""Makes the *-machine files"""
header = """###################################################################
@@ -557,11 +567,11 @@ olivia.ctd.anl.gov\n\n"""
nameslist = cursor.fetchall()
# gets the first host and initializes the hash
hostdata = nameslist[0]
- netgroups = {hostdata[2]:[hostdata[0]]}
+ netgroups = {hostdata[2]: [hostdata[0]]}
for row in nameslist:
# if new netgroup, create it
if row[2] not in netgroups:
- netgroups.update({row[2]:[]})
+ netgroups.update({row[2]: []})
# if it belongs in the netgroup and has multiple interfaces, put them in
if hostdata[0] == row[0] and row[3]:
netgroups[row[2]].append(row[1])
@@ -572,7 +582,7 @@ olivia.ctd.anl.gov\n\n"""
hostdata = row
for netgroup in netgroups:
- fileoutput = cStringIO.StringIO()
+ fileoutput = StringIO()
fileoutput.write(header % (netgroup, netgroup, len(netgroups[netgroup])))
for each in netgroups[netgroup]:
fileoutput.write(each + "\n")
diff --git a/src/lib/Server/Plugins/Ldap.py b/src/lib/Server/Plugins/Ldap.py
index 4f10d8ca6..06ecaed7b 100644
--- a/src/lib/Server/Plugins/Ldap.py
+++ b/src/lib/Server/Plugins/Ldap.py
@@ -1,9 +1,18 @@
import imp
+import logging
+import sys
import time
-import ldap
import Bcfg2.Options
import Bcfg2.Server.Plugin
+logger = logging.getLogger('Bcfg2.Plugins.Ldap')
+
+try:
+ import ldap
+except:
+ logger.error("Unable to load ldap module. Is python-ldap installed?")
+ raise ImportError
+
# time in seconds between retries after failed LDAP connection
RETRY_DELAY = 5
# how many times to try reaching the LDAP server if a connection is broken
@@ -81,7 +90,8 @@ class Ldap(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector):
self.debug_log("LdapPlugin debug: query '" + query.name +
"' not applicable to host '" + metadata.hostname + "'")
return data
- except Exception, error_msg:
+ except Exception:
+ error_msg = sys.exc_info()[1]
if self.debug_flag:
raise
else:
diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Server/Plugins/Metadata.py
index aa482e7ed..ca6e43851 100644
--- a/src/lib/Server/Plugins/Metadata.py
+++ b/src/lib/Server/Plugins/Metadata.py
@@ -1,4 +1,6 @@
-"""This file stores persistent metadata for the Bcfg2 Configuration Repository."""
+"""
+This file stores persistent metadata for the Bcfg2 Configuration Repository.
+"""
__revision__ = '$Revision$'
@@ -12,6 +14,7 @@ import time
import Bcfg2.Server.FileMonitor
import Bcfg2.Server.Plugin
+
def locked(fd):
"""Aquire a lock on a file"""
try:
@@ -20,14 +23,19 @@ def locked(fd):
return True
return False
+
class MetadataConsistencyError(Exception):
"""This error gets raised when metadata is internally inconsistent."""
pass
+
class MetadataRuntimeError(Exception):
- """This error is raised when the metadata engine is called prior to reading enough data."""
+ """This error is raised when the metadata engine
+ is called prior to reading enough data.
+ """
pass
+
class XMLMetadataConfig(object):
"""Handles xml config files and all XInclude statements"""
def __init__(self, metadata, watch_clients, basefile):
@@ -39,7 +47,8 @@ class XMLMetadataConfig(object):
self.basedata = None
self.basedir = metadata.data
self.logger = metadata.logger
- self.pseudo_monitor = isinstance(metadata.core.fam, Bcfg2.Server.FileMonitor.Pseudo)
+ self.pseudo_monitor = isinstance(metadata.core.fam,
+ Bcfg2.Server.FileMonitor.Pseudo)
@property
def xdata(self):
@@ -56,7 +65,8 @@ class XMLMetadataConfig(object):
def add_monitor(self, fname):
"""Add a fam monitor for an included file"""
if self.should_monitor:
- self.metadata.core.fam.AddMonitor("%s/%s" % (self.basedir, fname), self.metadata)
+ self.metadata.core.fam.AddMonitor("%s/%s" % (self.basedir, fname),
+ self.metadata)
self.extras.append(fname)
def load_xml(self):
@@ -81,14 +91,16 @@ class XMLMetadataConfig(object):
def write(self):
"""Write changes to xml back to disk."""
- self.write_xml("%s/%s" % (self.basedir, self.basefile), self.basedata)
+ self.write_xml("%s/%s" % (self.basedir, self.basefile),
+ self.basedata)
def write_xml(self, fname, xmltree):
"""Write changes to xml back to disk."""
tmpfile = "%s.new" % fname
try:
datafile = open("%s" % tmpfile, 'w')
- except IOError, e:
+ except IOError:
+ e = sys.exc_info()[1]
self.logger.error("Failed to write %s: %s" % (tmpfile, e))
raise MetadataRuntimeError
# prep data
@@ -182,6 +194,7 @@ class ClientMetadata(object):
return grp
return ''
+
class MetadataQuery(object):
def __init__(self, by_name, get_clients, by_groups, by_profiles, all_groups, all_groups_in_category):
# resolver is set later
@@ -201,6 +214,7 @@ class MetadataQuery(object):
def all(self):
return [self.by_name(name) for name in self.all_clients()]
+
class Metadata(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Metadata,
Bcfg2.Server.Plugin.Statistics):
@@ -220,12 +234,13 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
except:
print("Unable to add file monitor for groups.xml or clients.xml")
raise Bcfg2.Server.Plugin.PluginInitError
-
+
self.clients_xml = XMLMetadataConfig(self, watch_clients, 'clients.xml')
self.groups_xml = XMLMetadataConfig(self, watch_clients, 'groups.xml')
self.states = {}
if watch_clients:
- self.states = {"groups.xml":False, "clients.xml":False}
+ self.states = {"groups.xml": False,
+ "clients.xml": False}
self.addresses = {}
self.auth = dict()
self.clients = {}
@@ -244,10 +259,11 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
self.session_cache = {}
self.default = None
self.pdirty = False
- self.extra = {'groups.xml':[], 'clients.xml':[]}
+ self.extra = {'groups.xml': [],
+ 'clients.xml': []}
self.password = core.password
self.query = MetadataQuery(core.build_metadata,
- lambda:self.clients.keys(),
+ lambda: list(self.clients.keys()),
self.get_client_names_by_groups,
self.get_client_names_by_profiles,
self.get_all_group_names,
@@ -288,7 +304,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
element = lxml.etree.SubElement(self.groups_xml.base_xdata.getroot(),
"Group", name=group_name)
- for key, val in attribs.iteritems():
+ for key, val in list(attribs.items()):
element.set(key, val)
self.groups_xml.write()
@@ -303,7 +319,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
self.logger.error("Unexpected error finding group")
raise MetadataConsistencyError
- for key, val in attribs.iteritems():
+ for key, val in list(attribs.items()):
xdict['xquery'][0].set(key, val)
self.groups_xml.write_xml(xdict['filename'], xdict['xmltree'])
@@ -330,7 +346,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
self.logger.error("Bundle \"%s\" already exists" % (bundle_name))
raise MetadataConsistencyError
root.append(element)
- group_tree = open(self.data + "/groups.xml","w")
+ group_tree = open(self.data + "/groups.xml", "w")
fd = group_tree.fileno()
while True:
try:
@@ -352,7 +368,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
self.logger.error("Bundle \"%s\" not found" % (bundle_name))
raise MetadataConsistencyError
root.remove(node)
- group_tree = open(self.data + "/groups.xml","w")
+ group_tree = open(self.data + "/groups.xml", "w")
fd = group_tree.fileno()
while True:
try:
@@ -384,7 +400,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
element = lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
"Client", name=client_name)
- for key, val in attribs.iteritems():
+ for key, val in list(attribs.items()):
element.set(key, val)
self.clients_xml.write()
@@ -401,7 +417,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
raise MetadataConsistencyError
node = xdict['xquery'][0]
- [node.set(key, value) for key, value in attribs.items()]
+ [node.set(key, value) for key, value in list(attribs.items())]
self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
def HandleEvent(self, event):
@@ -463,8 +479,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
grouptmp = {}
self.categories = {}
groupseen = list()
- for group in xdata.xpath('//Groups/Group') \
- + xdata.xpath('Group'):
+ for group in xdata.xpath('//Groups/Group'):
if group.get('name') not in groupseen:
groupseen.append(group.get('name'))
else:
@@ -506,17 +521,17 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
ggg))
[self.groups[group][0].add(bund) for bund in bundles]
self.states['groups.xml'] = True
- if False not in self.states.values():
+ if False not in list(self.states.values()):
# check that all client groups are real and complete
- real = self.groups.keys()
- for client in self.clients.keys():
+ real = list(self.groups.keys())
+ for client in list(self.clients.keys()):
if self.clients[client] not in self.profiles:
self.logger.error("Client %s set as nonexistent or incomplete group %s" \
% (client, self.clients[client]))
self.logger.error("Removing client mapping for %s" % (client))
self.bad_clients[client] = self.clients[client]
del self.clients[client]
- for bclient in self.bad_clients.keys():
+ for bclient in list(self.bad_clients.keys()):
if self.bad_clients[bclient] in self.profiles:
self.logger.info("Restored profile mapping for client %s" % bclient)
self.clients[bclient] = self.bad_clients[bclient]
@@ -525,7 +540,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def set_profile(self, client, profile, addresspair):
"""Set group parameter for provided client."""
self.logger.info("Asserting client %s profile to %s" % (client, profile))
- if False in self.states.values():
+ if False in list(self.states.values()):
raise MetadataRuntimeError
if profile not in self.public:
self.logger.error("Failed to set client %s to private group %s" % (client, profile))
@@ -579,7 +594,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def get_initial_metadata(self, client):
"""Return the metadata for a given client."""
- if False in self.states.values():
+ if False in list(self.states.values()):
raise MetadataRuntimeError
client = client.lower()
if client in self.aliases:
@@ -604,7 +619,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
password = self.passwords[client]
else:
password = None
- uuids = [item for item, value in self.uuid.iteritems() if value == client]
+ uuids = [item for item, value in list(self.uuid.items()) if value == client]
if uuids:
uuid = uuids[0]
else:
@@ -622,7 +637,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def get_all_group_names(self):
all_groups = set()
- [all_groups.update(g[1]) for g in self.groups.values()]
+ [all_groups.update(g[1]) for g in list(self.groups.values())]
return all_groups
def get_all_groups_in_category(self, category):
@@ -632,11 +647,12 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
return all_groups
def get_client_names_by_profiles(self, profiles):
- return [client for client, profile in self.clients.iteritems() \
+ return [client for client, profile in list(self.clients.items()) \
if profile in profiles]
def get_client_names_by_groups(self, groups):
- mdata = [self.core.build_metadata(client) for client in self.clients.keys()]
+ mdata = [self.core.build_metadata(client)
+ for client in list(self.clients.keys())]
return [md.hostname for md in mdata if md.groups.issuperset(groups)]
def merge_additional_groups(self, imd, groups):
@@ -766,7 +782,6 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
xdict['xquery'][0].set('auth', 'cert')
self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
-
def viz(self, hosts, bundles, key, colors):
"""Admin mode viz support."""
groups_tree = lxml.etree.parse(self.data + "/groups.xml")
@@ -775,7 +790,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
except lxml.etree.XIncludeError:
self.logger.error("Failed to process XInclude for file %s" % dest)
groups = groups_tree.getroot()
- categories = {'default':'grey83'}
+ categories = {'default': 'grey83'}
instances = {}
viz_str = ""
egroups = groups.findall("Group") + groups.findall('.//Groups/Group')
@@ -787,12 +802,12 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
del categories[None]
if hosts:
clients = self.clients
- for client, profile in clients.iteritems():
+ for client, profile in list(clients.items()):
if profile in instances:
instances[profile].append(client)
else:
instances[profile] = [client]
- for profile, clist in instances.iteritems():
+ for profile, clist in list(instances.items()):
clist.sort()
viz_str += '''\t"%s-instances" [ label="%s", shape="record" ];\n''' \
% (profile, '|'.join(clist))
diff --git a/src/lib/Server/Plugins/NagiosGen.py b/src/lib/Server/Plugins/NagiosGen.py
index 14277b63d..8a76c130d 100644
--- a/src/lib/Server/Plugins/NagiosGen.py
+++ b/src/lib/Server/Plugins/NagiosGen.py
@@ -1,149 +1,152 @@
'''This module implements a Nagios configuration generator'''
-import glob
-import logging
-import lxml.etree
import os
import re
+import sys
+import glob
import socket
+import logging
+import lxml.etree
import Bcfg2.Server.Plugin
LOGGER = logging.getLogger('Bcfg2.Plugins.NagiosGen')
-host_config_fmt = \
-'''
-define host{
- host_name %s
- alias %s
- address %s
-'''
+line_fmt = '\t%-32s %s'
+
+class NagiosGenConfig(Bcfg2.Server.Plugin.SingleXMLFileBacked,
+ Bcfg2.Server.Plugin.StructFile):
+ def __init__(self, filename, fam):
+ Bcfg2.Server.Plugin.SingleXMLFileBacked.__init__(self, filename, fam)
+ Bcfg2.Server.Plugin.StructFile.__init__(self, filename)
+
class NagiosGen(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Generator):
"""NagiosGen is a Bcfg2 plugin that dynamically generates
Nagios configuration file based on Bcfg2 data.
"""
name = 'NagiosGen'
- __version__ = '0.6'
+ __version__ = '0.7'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Generator.__init__(self)
+ self.config = NagiosGenConfig(os.path.join(self.data, 'config.xml'),
+ core.fam)
self.Entries = {'Path':
- {'/etc/nagiosgen.status' : self.createhostconfig,
- '/etc/nagios/nagiosgen.cfg': self.createserverconfig}}
-
- self.client_attrib = {'encoding':'ascii',
- 'owner':'root',
- 'group':'root',
- 'type':'file',
- 'perms':'0400'}
- self.server_attrib = {'encoding':'ascii',
- 'owner':'nagios',
- 'group':'nagios',
- 'type':'file',
- 'perms':'0440'}
-
- def getparents(self, hostname):
- """Return parents for given hostname."""
- depends=[]
- if not os.path.isfile('%s/parents.xml' % (self.data)):
- return depends
-
- tree = lxml.etree.parse('%s/parents.xml' % (self.data))
- for entry in tree.findall('.//Depend'):
- if entry.attrib['name'] == hostname:
- depends.append(entry.attrib['on'])
- return depends
+ {'/etc/nagiosgen.status': self.createhostconfig,
+ '/etc/nagios/nagiosgen.cfg': self.createserverconfig}}
+
+ self.client_attrib = {'encoding': 'ascii',
+ 'owner': 'root',
+ 'group': 'root',
+ 'type': 'file',
+ 'perms': '0400'}
+ self.server_attrib = {'encoding': 'ascii',
+ 'owner': 'nagios',
+ 'group': 'nagios',
+ 'type': 'file',
+ 'perms': '0440'}
def createhostconfig(self, entry, metadata):
"""Build host specific configuration file."""
host_address = socket.gethostbyname(metadata.hostname)
- host_groups = [grp for grp in metadata.groups if \
- os.path.isfile('%s/%s-group.cfg' % (self.data, grp))]
- host_config = host_config_fmt % \
- (metadata.hostname, metadata.hostname, host_address)
+ host_groups = [grp for grp in metadata.groups
+ if os.path.isfile('%s/%s-group.cfg' % (self.data, grp))]
+ host_config = ['define host {',
+ line_fmt % ('host_name', metadata.hostname),
+ line_fmt % ('alias', metadata.hostname),
+ line_fmt % ('address', host_address)]
if host_groups:
- host_config += ' hostgroups %s\n' % (",".join(host_groups))
-
- xtra = None
- if hasattr(metadata, 'Properties') and \
- 'NagiosGen.xml' in metadata.Properties:
- for q in (metadata.hostname, 'default'):
- xtra = metadata.Properties['NagiosGen.xml'].data.find(q)
- if xtra is not None:
- break
-
- if xtra is not None:
- directives = list(xtra)
- for item in directives:
- host_config += ' %-32s %s\n' % (item.tag, item.text)
-
+ host_config.append(line_fmt % ("hostgroups",
+ ",".join(host_groups)))
+
+ # read the old-style Properties config, but emit a warning.
+ xtra = dict()
+ props = None
+ if (hasattr(metadata, 'Properties') and
+ 'NagiosGen.xml' in metadata.Properties):
+ props = metadata.Properties['NagiosGen.xml'].data
+ if props is not None:
+ LOGGER.warn("Parsing deprecated Properties/NagiosGen.xml. "
+ "Update to the new-style config with "
+ "nagiosgen-convert.py.")
+ xtra = dict((el.tag, el.text)
+ for el in props.find(metadata.hostname))
+ # hold off on parsing the defaults until we've checked for
+ # a new-style config
+
+ # read the old-style parents.xml, but emit a warning
+ pfile = os.path.join(self.data, "parents.xml")
+ if os.path.exists(pfile):
+ LOGGER.warn("Parsing deprecated NagiosGen/parents.xml. "
+ "Update to the new-style config with "
+ "nagiosgen-convert.py.")
+ parents = lxml.etree.parse(pfile)
+ for el in parents.xpath("//Depend[@name='%s']" % metadata.hostname):
+ if 'parent' in xtra:
+ xtra['parent'] += "," + el.get("on")
+ else:
+ xtra['parent'] = el.get("on")
+
+ # read the new-style config and overwrite the old-style config
+ for el in self.config.Match(metadata):
+ if el.tag == 'Option':
+ xtra[el.get("name")] = el.text
+
+ # if we haven't found anything in the new- or old-style
+ # configs, finally read defaults from old-style config
+ if not xtra and props is not None:
+ xtra = dict((el.tag, el.text) for el in props.find('default'))
+
+ if xtra:
+ host_config.extend([line_fmt % (opt, val)
+ for opt, val in list(xtra.items())])
else:
- host_config += ' use default\n'
+ host_config.append(line_fmt % ('use', 'default'))
- host_config += '}\n'
- entry.text = host_config
- [entry.attrib.__setitem__(key, value) for \
- (key, value) in self.client_attrib.iteritems()]
+ host_config.append('}')
+ entry.text = "%s\n" % "\n".join(host_config)
+ [entry.attrib.__setitem__(key, value)
+ for (key, value) in list(self.client_attrib.items())]
try:
- fileh = open("%s/%s-host.cfg" % \
- (self.data, metadata.hostname), 'w')
- fileh.write(host_config)
+ fileh = open("%s/%s-host.cfg" %
+ (self.data, metadata.hostname), 'w')
+ fileh.write(entry.text)
fileh.close()
- except OSError, ioerr:
- LOGGER.error("Failed to write %s/%s-host.cfg" % \
- (self.data, metadata.hostname))
+ except OSError:
+ ioerr = sys.exc_info()[1]
+ LOGGER.error("Failed to write %s/%s-host.cfg" %
+ (self.data, metadata.hostname))
LOGGER.error(ioerr)
def createserverconfig(self, entry, _):
"""Build monolithic server configuration file."""
- host_configs = glob.glob('%s/*-host.cfg' % self.data)
+ host_configs = glob.glob('%s/*-host.cfg' % self.data)
group_configs = glob.glob('%s/*-group.cfg' % self.data)
- host_data = ""
- group_data = ""
+ host_data = []
+ group_data = []
for host in host_configs:
- hostfile = open(host, 'r')
- hostname=host.split('/')[-1].replace('-host.cfg','')
- parents=self.getparents(hostname)
- if parents:
- hostlines = hostfile.readlines()
- else:
- hostdata = hostfile.read()
- hostfile.close()
-
- if parents:
- hostdata=''
- addparents=True
- for line in hostlines:
- line=line.replace('\n','')
- if 'parents' in line:
- line+=','+','.join(parents)
- addparents=False
- if '}' in line:
- line=''
- hostdata+="%s\n" % line
- if addparents:
- hostdata+=" parents %s\n" % ','.join(parents)
- hostdata+="}\n"
-
- host_data += hostdata
+ host_data.append(open(host, 'r').read())
+
for group in group_configs:
group_name = re.sub("(-group.cfg|.*/(?=[^/]+))", "", group)
- if host_data.find(group_name) != -1:
+ if "\n".join(host_data).find(group_name) != -1:
groupfile = open(group, 'r')
- group_data += groupfile.read()
+ group_data.append(groupfile.read())
groupfile.close()
- entry.text = group_data + host_data
- [entry.attrib.__setitem__(key, value) for \
- (key, value) in self.server_attrib.iteritems()]
+
+ entry.text = "%s\n\n%s" % ("\n".join(group_data), "\n".join(host_data))
+ [entry.attrib.__setitem__(key, value)
+ for (key, value) in list(self.server_attrib.items())]
try:
- fileh = open("%s/nagiosgen.cfg" % (self.data), 'w')
- fileh.write(group_data + host_data)
+ fileh = open("%s/nagiosgen.cfg" % self.data, 'w')
+ fileh.write(entry.text)
fileh.close()
- except OSError, ioerr:
- LOGGER.error("Failed to write %s/nagiosgen.cfg" % (self.data))
+ except OSError:
+ ioerr = sys.exc_info()[1]
+ LOGGER.error("Failed to write %s/nagiosgen.cfg" % self.data)
LOGGER.error(ioerr)
diff --git a/src/lib/Server/Plugins/Ohai.py b/src/lib/Server/Plugins/Ohai.py
index 0f7c7187f..6bd3edc34 100644
--- a/src/lib/Server/Plugins/Ohai.py
+++ b/src/lib/Server/Plugins/Ohai.py
@@ -37,12 +37,12 @@ class OhaiCache(object):
try:
data = open("%s/%s.json" % (self.dirname, item)).read()
except:
- raise KeyError, item
+ raise KeyError(item)
self.cache[item] = json.loads(data)
return self.cache[item]
def __iter__(self):
- data = self.cache.keys()
+ data = list(self.cache.keys())
data.extend([x[:-5] for x in os.listdir(self.dirname)])
return data.__iter__()
@@ -50,7 +50,9 @@ class OhaiCache(object):
class Ohai(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Probing,
Bcfg2.Server.Plugin.Connector):
- """The Ohai plugin is used to detect information about the client operating system."""
+ """The Ohai plugin is used to detect information
+ about the client operating system.
+ """
name = 'Ohai'
experimental = True
diff --git a/src/lib/Server/Plugins/Packages.py b/src/lib/Server/Plugins/Packages.py
index 438c1d5c0..4e47f8549 100644
--- a/src/lib/Server/Plugins/Packages.py
+++ b/src/lib/Server/Plugins/Packages.py
@@ -1,4 +1,3 @@
-import cPickle
import copy
import gzip
import tarfile
@@ -8,7 +7,21 @@ import lxml.etree
import os
import re
import sys
-import urllib2
+
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import cPickle
+from Bcfg2.Bcfg2Py3k import HTTPBasicAuthHandler
+from Bcfg2.Bcfg2Py3k import HTTPPasswordMgrWithDefaultRealm
+from Bcfg2.Bcfg2Py3k import HTTPError
+from Bcfg2.Bcfg2Py3k import install_opener
+from Bcfg2.Bcfg2Py3k import build_opener
+from Bcfg2.Bcfg2Py3k import urlopen
+
+# py3k compatibility
+if sys.hexversion >= 0x03000000:
+ from io import FileIO as BUILTIN_FILE_TYPE
+else:
+ BUILTIN_FILE_TYPE = file
# FIXME: Remove when server python dep is 2.5 or greater
if sys.version_info >= (2, 5):
@@ -22,14 +35,17 @@ import Bcfg2.Server.Plugin
# build sources.list?
# caching for yum
+
class NoData(Exception):
pass
+
class SomeData(Exception):
pass
logger = logging.getLogger('Packages')
+
def source_from_xml(xsource):
ret = dict([('rawurl', False), ('url', False)])
for key, tag in [('groups', 'Group'), ('components', 'Component'),
@@ -60,6 +76,7 @@ def source_from_xml(xsource):
ret['url'] += '/'
return ret
+
def _fetch_url(url):
if '@' in url:
mobj = re.match('(\w+://)([^:]+):([^@]+)@(.*)$', url)
@@ -68,10 +85,11 @@ def _fetch_url(url):
user = mobj.group(2)
passwd = mobj.group(3)
url = mobj.group(1) + mobj.group(4)
- auth = urllib2.HTTPBasicAuthHandler(urllib2.HTTPPasswordMgrWithDefaultRealm())
+ auth = HTTPBasicAuthHandler(HTTPPasswordMgrWithDefaultRealm())
auth.add_password(None, url, user, passwd)
- urllib2.install_opener(urllib2.build_opener(auth))
- return urllib2.urlopen(url).read()
+ install_opener(build_opener(auth))
+ return urlopen(url).read()
+
class Source(object):
basegroups = []
@@ -135,7 +153,7 @@ class Source(object):
agroups = ['global'] + [a for a in self.arches if a in meta.groups]
vdict = dict()
for agrp in agroups:
- for key, value in self.provides[agrp].iteritems():
+ for key, value in list(self.provides[agrp].items()):
if key not in vdict:
vdict[key] = set(value)
else:
@@ -160,11 +178,12 @@ class Source(object):
except ValueError:
logger.error("Packages: Bad url string %s" % url)
continue
- except urllib2.HTTPError, h:
+ except HTTPError:
+ h = sys.exc_info()[1]
logger.error("Packages: Failed to fetch url %s. code=%s" \
% (url, h.code))
continue
- file(fname, 'w').write(data)
+ BUILTIN_FILE_TYPE(fname, 'w').write(data)
def applies(self, metadata):
return len([g for g in self.basegroups if g in metadata.groups]) != 0 and \
@@ -193,6 +212,7 @@ class Source(object):
return {'groups': copy.copy(self.groups), \
'urls': [copy.deepcopy(url) for url in self.url_map]}
+
class YUMSource(Source):
xp = '{http://linux.duke.edu/metadata/common}'
rp = '{http://linux.duke.edu/metadata/rpm}'
@@ -217,13 +237,13 @@ class YUMSource(Source):
self.file_to_arch = dict()
def save_state(self):
- cache = file(self.cachefile, 'wb')
+ cache = BUILTIN_FILE_TYPE(self.cachefile, 'wb')
cPickle.dump((self.packages, self.deps, self.provides,
self.filemap, self.url_map), cache, 2)
cache.close()
def load_state(self):
- data = file(self.cachefile)
+ data = BUILTIN_FILE_TYPE(self.cachefile)
(self.packages, self.deps, self.provides, \
self.filemap, self.url_map) = cPickle.load(data)
@@ -250,7 +270,8 @@ class YUMSource(Source):
except ValueError:
logger.error("Packages: Bad url string %s" % rmdurl)
continue
- except urllib2.HTTPError, h:
+ except HTTPError:
+ h = sys.exc_info()[1]
logger.error("Packages: Failed to fetch url %s. code=%s" \
% (rmdurl, h.code))
continue
@@ -277,7 +298,7 @@ class YUMSource(Source):
fdata = lxml.etree.parse(fname).getroot()
self.parse_filelist(fdata, farch)
# merge data
- sdata = self.packages.values()
+ sdata = list(self.packages.values())
self.packages['global'] = copy.deepcopy(sdata.pop())
while sdata:
self.packages['global'] = self.packages['global'].intersection(sdata.pop())
@@ -337,10 +358,10 @@ class YUMSource(Source):
def get_vpkgs(self, metadata):
rv = Source.get_vpkgs(self, metadata)
- for arch, fmdata in self.filemap.iteritems():
+ for arch, fmdata in list(self.filemap.items()):
if arch not in metadata.groups and arch != 'global':
continue
- for filename, pkgs in fmdata.iteritems():
+ for filename, pkgs in list(fmdata.items()):
rv[filename] = pkgs
return rv
@@ -348,6 +369,7 @@ class YUMSource(Source):
filtered = set([u for u in unknown if u.startswith('rpmlib')])
unknown.difference_update(filtered)
+
class APTSource(Source):
basegroups = ['apt', 'debian', 'ubuntu', 'nexenta']
ptype = 'deb'
@@ -362,13 +384,13 @@ class APTSource(Source):
'components': self.components, 'arches': self.arches, 'groups': self.groups}]
def save_state(self):
- cache = file(self.cachefile, 'wb')
+ cache = BUILTIN_FILE_TYPE(self.cachefile, 'wb')
cPickle.dump((self.pkgnames, self.deps, self.provides),
cache, 2)
cache.close()
def load_state(self):
- data = file(self.cachefile)
+ data = BUILTIN_FILE_TYPE(self.cachefile)
self.pkgnames, self.deps, self.provides = cPickle.load(data)
def filter_unknown(self, unknown):
@@ -407,7 +429,7 @@ class APTSource(Source):
print("Failed to read file %s" % fname)
raise
for line in reader.readlines():
- words = line.strip().split(':', 1)
+ words = str(line.strip()).split(':', 1)
if words[0] == 'Package':
pkgname = words[1].strip().rstrip()
self.pkgnames.add(pkgname)
@@ -449,7 +471,7 @@ class APTSource(Source):
for barch in bdeps:
self.deps[barch][pkgname] = bdeps[barch][pkgname]
provided = set()
- for bprovided in bprov.values():
+ for bprovided in list(bprov.values()):
provided.update(set(bprovided))
for prov in provided:
prset = set()
@@ -469,6 +491,7 @@ class APTSource(Source):
pkg not in self.blacklist and \
(len(self.whitelist) == 0 or pkg in self.whitelist)
+
class PACSource(Source):
basegroups = ['arch', 'parabola']
ptype = 'pacman'
@@ -483,13 +506,13 @@ class PACSource(Source):
'components': self.components, 'arches': self.arches, 'groups': self.groups}]
def save_state(self):
- cache = file(self.cachefile, 'wb')
+ cache = BUILTIN_FILE_TYPE(self.cachefile, 'wb')
cPickle.dump((self.pkgnames, self.deps, self.provides),
cache, 2)
cache.close()
def load_state(self):
- data = file(self.cachefile)
+ data = BUILTIN_FILE_TYPE(self.cachefile)
self.pkgnames, self.deps, self.provides = cPickle.load(data)
def filter_unknown(self, unknown):
@@ -526,7 +549,7 @@ class PACSource(Source):
bdeps[barch] = dict()
bprov[barch] = dict()
try:
- print "try to read : " + fname
+ print("try to read : " + fname)
tar = tarfile.open(fname, "r")
reader = gzip.GzipFile(fname)
except:
@@ -536,7 +559,7 @@ class PACSource(Source):
for tarinfo in tar:
if tarinfo.isdir():
self.pkgnames.add(tarinfo.name.rsplit("-", 2)[0])
- print "added : " + tarinfo.name.rsplit("-", 2)[0]
+ print("added : " + tarinfo.name.rsplit("-", 2)[0])
tar.close()
self.deps['global'] = dict()
@@ -556,7 +579,7 @@ class PACSource(Source):
for barch in bdeps:
self.deps[barch][pkgname] = bdeps[barch][pkgname]
provided = set()
- for bprovided in bprov.values():
+ for bprovided in list(bprov.values()):
provided.update(set(bprovided))
for prov in provided:
prset = set()
@@ -576,6 +599,7 @@ class PACSource(Source):
pkg not in self.blacklist and \
(len(self.whitelist) == 0 or pkg in self.whitelist)
+
class Packages(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.StructureValidator,
Bcfg2.Server.Plugin.Generator,
@@ -614,7 +638,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
vpkgs = dict()
for source in self.get_matching_sources(meta):
s_vpkgs = source.get_vpkgs(meta)
- for name, prov_set in s_vpkgs.iteritems():
+ for name, prov_set in list(s_vpkgs.items()):
if name not in vpkgs:
vpkgs[name] = set(prov_set)
else:
@@ -726,7 +750,9 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
satisfied_vpkgs.add(current)
elif [item for item in vpkg_cache[current] if item in packages]:
if debug:
- self.logger.debug("Packages: requirement %s satisfied by %s" % (current, [item for item in vpkg_cache[current] if item in packages]))
+ self.logger.debug("Packages: requirement %s satisfied by %s" % (current,
+ [item for item in vpkg_cache[current]
+ if item in packages]))
satisfied_vpkgs.add(current)
vpkgs.difference_update(satisfied_vpkgs)
@@ -736,7 +762,9 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
# allow use of virt through explicit specification, then fall back to forcing current on last pass
if [item for item in vpkg_cache[current] if item in packages]:
if debug:
- self.logger.debug("Packages: requirement %s satisfied by %s" % (current, [item for item in vpkg_cache[current] if item in packages]))
+ self.logger.debug("Packages: requirement %s satisfied by %s" % (current,
+ [item for item in vpkg_cache[current]
+ if item in packages]))
satisfied_both.add(current)
elif current in input_requirements or final_pass:
pkgs.add(current)
@@ -828,7 +856,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
xdata.xinclude()
xdata = xdata.getroot()
except (lxml.etree.XIncludeError, \
- lxml.etree.XMLSyntaxError), xmlerr:
+ lxml.etree.XMLSyntaxError):
+ xmlerr = sys.exc_info()[1]
self.logger.error("Package: Error processing xml: %s" % xmlerr)
raise Bcfg2.Server.Plugin.PluginInitError
except IOError:
diff --git a/src/lib/Server/Plugins/Pkgmgr.py b/src/lib/Server/Plugins/Pkgmgr.py
index b58a7c91d..b96e7ea7d 100644
--- a/src/lib/Server/Plugins/Pkgmgr.py
+++ b/src/lib/Server/Plugins/Pkgmgr.py
@@ -7,15 +7,17 @@ import Bcfg2.Server.Plugin
logger = logging.getLogger('Bcfg2.Plugins.Pkgmgr')
+
class FuzzyDict(dict):
fuzzy = re.compile('(?P<name>.*):(?P<alist>\S+(,\S+)*)')
+
def __getitem__(self, key):
if isinstance(key, str):
mdata = self.fuzzy.match(key)
if mdata:
return dict.__getitem__(self, mdata.groupdict()['name'])
else:
- print "got non-string key %s" % str(key)
+ print("got non-string key %s" % str(key))
return dict.__getitem__(self, key)
def has_key(self, key):
@@ -33,11 +35,14 @@ class FuzzyDict(dict):
return default
raise
+
class PNode(Bcfg2.Server.Plugin.INode):
- """PNode has a list of packages available at a particular group intersection."""
- splitters = {'rpm':re.compile('^(.*/)?(?P<name>[\w\+\d\.]+(-[\w\+\d\.]+)*)-' + \
+ """PNode has a list of packages available at a
+ particular group intersection.
+ """
+ splitters = {'rpm': re.compile('^(.*/)?(?P<name>[\w\+\d\.]+(-[\w\+\d\.]+)*)-' + \
'(?P<version>[\w\d\.]+-([\w\d\.]+))\.(?P<arch>\S+)\.rpm$'),
- 'encap':re.compile('^(?P<name>[\w-]+)-(?P<version>[\w\d\.+-]+).encap.*$')}
+ 'encap': re.compile('^(?P<name>[\w-]+)-(?P<version>[\w\d\.+-]+).encap.*$')}
ignore = ['Package']
def Match(self, metadata, data):
@@ -54,41 +59,44 @@ class PNode(Bcfg2.Server.Plugin.INode):
def __init__(self, data, pdict, parent=None):
# copy local attributes to all child nodes if no local attribute exists
- if not pdict.has_key('Package'):
+ if 'Package' not in pdict:
pdict['Package'] = set()
for child in data.getchildren():
- for attr in [key for key in data.attrib.keys() \
- if key != 'name' and not child.attrib.has_key(key)]:
+ for attr in [key for key in list(data.attrib.keys())
+ if key != 'name' and key not in child.attrib]:
try:
child.set(attr, data.get(attr))
except:
# don't fail on things like comments and other immutable elements
pass
Bcfg2.Server.Plugin.INode.__init__(self, data, pdict, parent)
- if not self.contents.has_key('Package'):
+ if 'Package' not in self.contents:
self.contents['Package'] = FuzzyDict()
for pkg in data.findall('./Package'):
- if pkg.attrib.has_key('name') and pkg.get('name') not in pdict['Package']:
+ if 'name' in pkg.attrib and pkg.get('name') not in pdict['Package']:
pdict['Package'].add(pkg.get('name'))
if pkg.get('name') != None:
self.contents['Package'][pkg.get('name')] = {}
if pkg.getchildren():
self.contents['Package'][pkg.get('name')]['__children__'] \
= pkg.getchildren()
- if pkg.attrib.has_key('simplefile'):
+ if 'simplefile' in pkg.attrib:
pkg.set('url', "%s/%s" % (pkg.get('uri'), pkg.get('simplefile')))
self.contents['Package'][pkg.get('name')].update(pkg.attrib)
else:
- if pkg.attrib.has_key('file'):
- if pkg.attrib.has_key('multiarch'):
+ if 'file' in pkg.attrib:
+ if 'multiarch' in pkg.attrib:
archs = pkg.get('multiarch').split()
srcs = pkg.get('srcs', pkg.get('multiarch')).split()
- url = ' '.join(["%s/%s" % (pkg.get('uri'), pkg.get('file') % {'src':srcs[idx], 'arch':archs[idx]})
+ url = ' '.join(["%s/%s" % (pkg.get('uri'),
+ pkg.get('file') % {'src':srcs[idx],
+ 'arch':archs[idx]})
for idx in range(len(archs))])
pkg.set('url', url)
else:
- pkg.set('url', '%s/%s' % (pkg.get('uri'), pkg.get('file')))
- if self.splitters.has_key(pkg.get('type')) and pkg.get('file') != None:
+ pkg.set('url', '%s/%s' % (pkg.get('uri'),
+ pkg.get('file')))
+ if pkg.get('type') in self.splitters and pkg.get('file') != None:
mdata = self.splitters[pkg.get('type')].match(pkg.get('file'))
if not mdata:
logger.error("Failed to match pkg %s" % pkg.get('file'))
@@ -112,10 +120,13 @@ class PNode(Bcfg2.Server.Plugin.INode):
class PkgSrc(Bcfg2.Server.Plugin.XMLSrc):
- """PkgSrc files contain a PNode hierarchy that returns matching package entries."""
+ """PkgSrc files contain a PNode hierarchy that
+ returns matching package entries.
+ """
__node__ = PNode
__cacheobj__ = FuzzyDict
+
class Pkgmgr(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles package assignments."""
name = 'Pkgmgr'
@@ -127,8 +138,8 @@ class Pkgmgr(Bcfg2.Server.Plugin.PrioDir):
def HandleEvent(self, event):
'''Handle events and update dispatch table'''
Bcfg2.Server.Plugin.XMLDirectoryBacked.HandleEvent(self, event)
- for src in self.entries.values():
- for itype, children in src.items.iteritems():
+ for src in list(self.entries.values()):
+ for itype, children in list(src.items.items()):
for child in children:
try:
self.Entries[itype][child] = self.BindEntry
@@ -149,7 +160,7 @@ class Pkgmgr(Bcfg2.Server.Plugin.PrioDir):
if inst.get('arch') not in arches]
def HandlesEntry(self, entry, metadata):
- return entry.tag == 'Package' and entry.get('name').split(':')[0] in self.Entries['Package'].keys()
+ return entry.tag == 'Package' and entry.get('name').split(':')[0] in list(self.Entries['Package'].keys())
def HandleEntry(self, entry, metadata):
self.BindEntry(entry, metadata)
diff --git a/src/lib/Server/Plugins/Probes.py b/src/lib/Server/Plugins/Probes.py
index 57dd4f698..ea2e79ccc 100644
--- a/src/lib/Server/Plugins/Probes.py
+++ b/src/lib/Server/Plugins/Probes.py
@@ -6,8 +6,10 @@ import Bcfg2.Server.Plugin
specific_probe_matcher = re.compile("(.*/)?(?P<basename>\S+)(.(?P<mode>[GH](\d\d)?)_\S+)")
probe_matcher = re.compile("(.*/)?(?P<basename>\S+)")
+
class ProbeSet(Bcfg2.Server.Plugin.EntrySet):
ignore = re.compile("^(\.#.*|.*~|\\..*\\.(tmp|sw[px])|probed\\.xml)$")
+
def __init__(self, path, fam, encoding, plugin_name):
fpattern = '[0-9A-Za-z_\-]+'
self.plugin_name = plugin_name
@@ -34,7 +36,7 @@ class ProbeSet(Bcfg2.Server.Plugin.EntrySet):
if pname not in build:
build[pname] = entry
- for (name, entry) in build.iteritems():
+ for (name, entry) in list(build.items()):
probe = lxml.etree.Element('probe')
probe.set('name', name.split('/')[-1])
probe.set('source', self.plugin_name)
@@ -47,6 +49,7 @@ class ProbeSet(Bcfg2.Server.Plugin.EntrySet):
ret.append(probe)
return ret
+
class Probes(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Probing,
Bcfg2.Server.Plugin.Connector):
@@ -80,7 +83,8 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
value=self.probedata[client][probe])
for group in sorted(self.cgroups[client]):
lxml.etree.SubElement(cx, "Group", name=group)
- data = lxml.etree.tostring(top, encoding='UTF-8', xml_declaration=True,
+ data = lxml.etree.tostring(top, encoding='UTF-8',
+ xml_declaration=True,
pretty_print='true')
try:
datafile = open("%s/%s" % (self.data, 'probed.xml'), 'w')
@@ -98,7 +102,7 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
self.cgroups = {}
for client in data.getchildren():
self.probedata[client.get('name')] = {}
- self.cgroups[client.get('name')]=[]
+ self.cgroups[client.get('name')] = []
for pdata in client:
if (pdata.tag == 'Probe'):
self.probedata[client.get('name')][pdata.get('name')] = pdata.get('value')
@@ -118,7 +122,7 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
def ReceiveDataItem(self, client, data):
"""Receive probe results pertaining to client."""
- if not self.cgroups.has_key(client.hostname):
+ if client.hostname not in self.cgroups:
self.cgroups[client.hostname] = []
if data.text == None:
self.logger.error("Got null response to probe %s from %s" % \
@@ -139,9 +143,9 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
dlines.remove(line)
dtext = "\n".join(dlines)
try:
- self.probedata[client.hostname].update({data.get('name'):dtext})
+ self.probedata[client.hostname].update({data.get('name'): dtext})
except KeyError:
- self.probedata[client.hostname] = {data.get('name'):dtext}
+ self.probedata[client.hostname] = {data.get('name'): dtext}
def get_additional_groups(self, meta):
return self.cgroups.get(meta.hostname, list())
diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py
index 2888ef1d1..dea797a10 100644
--- a/src/lib/Server/Plugins/Properties.py
+++ b/src/lib/Server/Plugins/Properties.py
@@ -4,15 +4,45 @@ import lxml.etree
import Bcfg2.Server.Plugin
-class PropertyFile(Bcfg2.Server.Plugin.XMLFileBacked):
+class PropertyFile(Bcfg2.Server.Plugin.StructFile):
"""Class for properties files."""
-
def Index(self):
- """Build data into an xml object."""
- try:
- self.data = lxml.etree.XML(self.data)
- except lxml.etree.XMLSyntaxError:
- Bcfg2.Server.Plugin.logger.error("Failed to parse %s" % self.name)
+ """Build internal data structures."""
+ if type(self.data) is not lxml.etree._Element:
+ try:
+ self.data = lxml.etree.XML(self.data)
+ except lxml.etree.XMLSyntaxError:
+ Bcfg2.Server.Plugin.logger.error("Failed to parse %s" %
+ self.name)
+
+ self.fragments = {}
+ work = {lambda x: True: self.data.getchildren()}
+ while work:
+ (predicate, worklist) = work.popitem()
+ self.fragments[predicate] = \
+ [item for item in worklist
+ if (item.tag != 'Group' and
+ item.tag != 'Client' and
+ not isinstance(item,
+ lxml.etree._Comment))]
+ for item in worklist:
+ cmd = None
+ if item.tag == 'Group':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:'%s' not in x.groups and predicate(x)"
+ else:
+ cmd = "lambda x:'%s' in x.groups and predicate(x)"
+ elif item.tag == 'Client':
+ if item.get('negate', 'false').lower() == 'true':
+ cmd = "lambda x:x.hostname != '%s' and predicate(x)"
+ else:
+ cmd = "lambda x:x.hostname == '%s' and predicate(x)"
+ # else, ignore item
+ if cmd is not None:
+ newpred = eval(cmd % item.get('name'),
+ {'predicate':predicate})
+ work[newpred] = item.getchildren()
+
class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
@@ -33,9 +63,10 @@ class Properties(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector.__init__(self)
try:
self.store = PropDirectoryBacked(self.data, core.fam)
- except OSError, e:
+ except OSError:
+ e = sys.exc_info()[1]
Bcfg2.Server.Plugin.logger.error("Error while creating Properties "
- "store: %s %s" % (e.strerror,e.filename))
+ "store: %s %s" % (e.strerror, e.filename))
raise Bcfg2.Server.Plugin.PluginInitError
def get_additional_data(self, _):
diff --git a/src/lib/Server/Plugins/SGenshi.py b/src/lib/Server/Plugins/SGenshi.py
index cead06e34..efd981956 100644
--- a/src/lib/Server/Plugins/SGenshi.py
+++ b/src/lib/Server/Plugins/SGenshi.py
@@ -5,6 +5,7 @@ import genshi.input
import genshi.template
import lxml.etree
import logging
+import sys
import Bcfg2.Server.Plugin
import Bcfg2.Server.Plugins.TGenshi
@@ -23,11 +24,14 @@ class SGenshiTemplateFile(Bcfg2.Server.Plugins.TGenshi.TemplateFile):
Bcfg2.Server.Plugins.TGenshi.removecomment)
data = stream.render('xml', strip_whitespace=False)
return lxml.etree.XML(data)
- except LookupError, lerror:
+ except LookupError:
+ lerror = sys.exc_info()[1]
logger.error('Genshi lookup error: %s' % lerror)
- except genshi.template.TemplateError, terror:
+ except genshi.template.TemplateError:
+ terror = sys.exc_info()[1]
logger.error('Genshi template error: %s' % terror)
- except genshi.input.ParseError, perror:
+ except genshi.input.ParseError:
+ perror = sys.exc_info()[1]
logger.error('Genshi parse error: %s' % perror)
raise
diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Server/Plugins/SSHbase.py
index 96a444875..cf0998aaa 100644
--- a/src/lib/Server/Plugins/SSHbase.py
+++ b/src/lib/Server/Plugins/SSHbase.py
@@ -5,6 +5,7 @@ import binascii
import os
import socket
import shutil
+import sys
import tempfile
from subprocess import Popen, PIPE
import Bcfg2.Server.Plugin
@@ -52,7 +53,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
try:
Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data,
self.core.fam)
- except OSError, ioerr:
+ except OSError:
+ ioerr = sys.exc_info()[1]
self.logger.error("Failed to load SSHbase repository from %s" \
% (self.data))
self.logger.error(ioerr)
@@ -72,8 +74,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
def get_skn(self):
"""Build memory cache of the ssh known hosts file."""
if not self.__skn:
- self.__skn = "\n".join([value.data for key, value in \
- self.entries.iteritems() if \
+ self.__skn = "\n".join([str(value.data) for key, value in \
+ list(self.entries.items()) if \
key.endswith('.static')])
names = dict()
# if no metadata is registered yet, defer
@@ -103,7 +105,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
continue
names[cmeta.hostname] = sorted(names[cmeta.hostname])
# now we have our name cache
- pubkeys = [pubk for pubk in self.entries.keys() \
+ pubkeys = [pubk for pubk in list(self.entries.keys()) \
if pubk.find('.pub.H_') != -1]
pubkeys.sort()
badnames = set()
@@ -131,7 +133,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
if event and event.filename.endswith('.static'):
self.skn = False
if not self.__skn:
- if (len(self.entries.keys())) >= (len(os.listdir(self.data))-1):
+ if (len(list(self.entries.keys()))) >= (len(os.listdir(self.data)) - 1):
_ = self.skn
def HandlesEntry(self, entry, _):
@@ -205,26 +207,26 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
for hostkey in hostkeys:
entry.text += "localhost,localhost.localdomain,127.0.0.1 %s" % (
self.entries[hostkey].data)
- permdata = {'owner':'root',
- 'group':'root',
- 'type':'file',
- 'perms':'0644'}
+ permdata = {'owner': 'root',
+ 'group': 'root',
+ 'type': 'file',
+ 'perms': '0644'}
[entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
def build_hk(self, entry, metadata):
"""This binds host key data into entries."""
client = metadata.hostname
filename = "%s.H_%s" % (entry.get('name').split('/')[-1], client)
- if filename not in self.entries.keys():
+ if filename not in list(self.entries.keys()):
self.GenerateHostKeys(client)
if not filename in self.entries:
self.logger.error("%s still not registered" % filename)
raise Bcfg2.Server.Plugin.PluginExecutionError
keydata = self.entries[filename].data
- permdata = {'owner':'root',
- 'group':'root',
- 'type':'file',
- 'perms':'0600'}
+ permdata = {'owner': 'root',
+ 'group': 'root',
+ 'type': 'file',
+ 'perms': '0600'}
if entry.get('name')[-4:] == '.pub':
permdata['perms'] = '0644'
[entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
@@ -245,7 +247,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
else:
keytype = 'rsa1'
- if hostkey not in self.entries.keys():
+ if hostkey not in list(self.entries.keys()):
fileloc = "%s/%s" % (self.data, hostkey)
publoc = self.data + '/' + ".".join([hostkey.split('.')[0],
'pub',
@@ -257,8 +259,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
shutil.copy(temploc, fileloc)
shutil.copy("%s.pub" % temploc, publoc)
self.AddEntry(hostkey)
- self.AddEntry(".".join([hostkey.split('.')[0]]+['pub', "H_%s" \
- % client]))
+ self.AddEntry(".".join([hostkey.split('.')[0]] + ['pub', "H_%s" \
+ % client]))
try:
os.unlink(temploc)
os.unlink("%s.pub" % temploc)
@@ -277,7 +279,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
try:
open(filename, 'w').write(entry['text'])
if log:
- print "Wrote file %s" % filename
+ print("Wrote file %s" % filename)
except KeyError:
self.logger.error("Failed to pull %s. This file does not currently "
"exist on the client" % entry.get('name'))
diff --git a/src/lib/Server/Plugins/SSLCA.py b/src/lib/Server/Plugins/SSLCA.py
index 1c9e1b59d..baaa14ba9 100644
--- a/src/lib/Server/Plugins/SSLCA.py
+++ b/src/lib/Server/Plugins/SSLCA.py
@@ -5,7 +5,8 @@ import posixpath
import tempfile
import os
from subprocess import Popen, PIPE, STDOUT
-from ConfigParser import ConfigParser
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
@@ -41,14 +42,14 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
if event.filename.endswith('.xml'):
if action in ['exists', 'created', 'changed']:
if event.filename.endswith('key.xml'):
- key_spec = dict(lxml.etree.parse(epath).find('Key').items())
+ key_spec = dict(list(lxml.etree.parse(epath).find('Key').items()))
self.key_specs[ident] = {
'bits': key_spec.get('bits', 2048),
'type': key_spec.get('type', 'rsa')
}
self.Entries['Path'][ident] = self.get_key
elif event.filename.endswith('cert.xml'):
- cert_spec = dict(lxml.etree.parse(epath).find('Cert').items())
+ cert_spec = dict(list(lxml.etree.parse(epath).find('Cert').items()))
ca = cert_spec.get('ca', 'default')
self.cert_specs[ident] = {
'ca': ca,
@@ -64,7 +65,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
}
cp = ConfigParser()
cp.read(self.core.cfile)
- self.CAs[ca] = dict(cp.items('sslca_'+ca))
+ self.CAs[ca] = dict(cp.items('sslca_' + ca))
self.Entries['Path'][ident] = self.get_cert
if action == 'deleted':
if ident in self.Entries['Path']:
@@ -99,12 +100,14 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
# check if we already have a hostfile, or need to generate a new key
# TODO: verify key fits the specs
path = entry.get('name')
- filename = "".join([path, '/', path.rsplit('/', 1)[1], '.H_', metadata.hostname])
- if filename not in self.entries.keys():
+ filename = "".join([path, '/', path.rsplit('/', 1)[1],
+ '.H_', metadata.hostname])
+ if filename not in list(self.entries.keys()):
key = self.build_key(filename, entry, metadata)
open(self.data + filename, 'w').write(key)
entry.text = key
- self.entries[filename] = self.__child__("%s%s" % (self.data, filename))
+ self.entries[filename] = self.__child__("%s%s" % (self.data,
+ filename))
self.entries[filename].HandleEvent()
else:
entry.text = self.entries[filename].data
@@ -135,23 +138,28 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
[entry.attrib.__setitem__(key, permdata[key]) for key in permdata]
path = entry.get('name')
- filename = "".join([path, '/', path.rsplit('/', 1)[1], '.H_', metadata.hostname])
+ filename = "".join([path, '/', path.rsplit('/', 1)[1],
+ '.H_', metadata.hostname])
# first - ensure we have a key to work with
key = self.cert_specs[entry.get('name')].get('key')
- key_filename = "".join([key, '/', key.rsplit('/', 1)[1], '.H_', metadata.hostname])
+ key_filename = "".join([key, '/', key.rsplit('/', 1)[1],
+ '.H_', metadata.hostname])
if key_filename not in self.entries:
e = lxml.etree.Element('Path')
e.attrib['name'] = key
self.core.Bind(e, metadata)
# check if we have a valid hostfile
- if filename in self.entries.keys() and self.verify_cert(filename, key_filename, entry):
+ if filename in list(self.entries.keys()) and self.verify_cert(filename,
+ key_filename,
+ entry):
entry.text = self.entries[filename].data
else:
cert = self.build_cert(key_filename, entry, metadata)
open(self.data + filename, 'w').write(cert)
- self.entries[filename] = self.__child__("%s%s" % (self.data, filename))
+ self.entries[filename] = self.__child__("%s%s" % (self.data,
+ filename))
self.entries[filename].HandleEvent()
entry.text = cert
@@ -188,7 +196,6 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
return True
return False
-
def build_cert(self, key_filename, entry, metadata):
"""
creates a new certificate according to the specification
@@ -200,9 +207,14 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
days = self.cert_specs[entry.get('name')]['days']
passphrase = self.CAs[ca].get('passphrase')
if passphrase:
- cmd = "openssl ca -config %s -in %s -days %s -batch -passin pass:%s" % (ca_config, req, days, passphrase)
+ cmd = "openssl ca -config %s -in %s -days %s -batch -passin pass:%s" % (ca_config,
+ req,
+ days,
+ passphrase)
else:
- cmd = "openssl ca -config %s -in %s -days %s -batch" % (ca_config, req, days)
+ cmd = "openssl ca -config %s -in %s -days %s -batch" % (ca_config,
+ req,
+ days)
cert = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
try:
os.unlink(req_config)
@@ -234,7 +246,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
},
'alt_names': {}
}
- for section in defaults.keys():
+ for section in list(defaults.keys()):
cp.add_section(section)
for key in defaults[section]:
cp.set(section, key, defaults[section][key])
@@ -242,7 +254,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
altnames = list(metadata.aliases)
altnames.append(metadata.hostname)
for altname in altnames:
- cp.set('alt_names', 'DNS.'+str(x), altname)
+ cp.set('alt_names', 'DNS.' + str(x), altname)
x += 1
for item in ['C', 'L', 'ST', 'O', 'OU', 'emailAddress']:
if self.cert_specs[entry.get('name')][item]:
@@ -259,6 +271,9 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
req = tempfile.mkstemp()[1]
days = self.cert_specs[entry.get('name')]['days']
key = self.data + key_filename
- cmd = "openssl req -new -config %s -days %s -key %s -text -out %s" % (req_config, days, key, req)
+ cmd = "openssl req -new -config %s -days %s -key %s -text -out %s" % (req_config,
+ days,
+ key,
+ req)
res = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
return req
diff --git a/src/lib/Server/Plugins/Snapshots.py b/src/lib/Server/Plugins/Snapshots.py
index a4489ae95..8b6bad574 100644
--- a/src/lib/Server/Plugins/Snapshots.py
+++ b/src/lib/Server/Plugins/Snapshots.py
@@ -8,10 +8,13 @@ import Bcfg2.Server.Plugin
import Bcfg2.Server.Snapshots
import Bcfg2.Logger
from Bcfg2.Server.Snapshots.model import Snapshot
-import Queue
+import sys
import time
import threading
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import Queue
+
logger = logging.getLogger('Snapshots')
ftypes = ['ConfigFile', 'SymLink', 'Directory']
@@ -24,13 +27,21 @@ datafields = {
'SymLink': ['to'],
}
+
+def u_str(string):
+ if sys.hexversion >= 0x03000000:
+ return string
+ else:
+ return unicode(string)
+
+
def build_snap_ent(entry):
basefields = []
if entry.tag in ['Package', 'Service']:
basefields += ['type']
- desired = dict([(key, unicode(entry.get(key))) for key in basefields])
- state = dict([(key, unicode(entry.get(key))) for key in basefields])
- desired.update([(key, unicode(entry.get(key))) for key in \
+ desired = dict([(key, u_str(entry.get(key))) for key in basefields])
+ state = dict([(key, u_str(entry.get(key))) for key in basefields])
+ desired.update([(key, u_str(entry.get(key))) for key in \
datafields[entry.tag]])
if entry.tag == 'ConfigFile' or \
((entry.tag == 'Path') and (entry.get('type') == 'file')):
@@ -38,19 +49,19 @@ def build_snap_ent(entry):
desired['contents'] = None
else:
if entry.get('encoding', 'ascii') == 'ascii':
- desired['contents'] = unicode(entry.text)
+ desired['contents'] = u_str(entry.text)
else:
- desired['contents'] = unicode(binascii.a2b_base64(entry.text))
+ desired['contents'] = u_str(binascii.a2b_base64(entry.text))
if 'current_bfile' in entry.attrib:
- state['contents'] = unicode(binascii.a2b_base64( \
+ state['contents'] = u_str(binascii.a2b_base64( \
entry.get('current_bfile')))
elif 'current_bdiff' in entry.attrib:
diff = binascii.a2b_base64(entry.get('current_bdiff'))
- state['contents'] = unicode( \
+ state['contents'] = u_str( \
'\n'.join(difflib.restore(diff.split('\n'), 1)))
- state.update([(key, unicode(entry.get('current_' + key, entry.get(key)))) \
+ state.update([(key, u_str(entry.get('current_' + key, entry.get(key)))) \
for key in datafields[entry.tag]])
if entry.tag in ['ConfigFile', 'Path'] and entry.get('exists', 'true') == 'false':
state = None
@@ -66,7 +77,7 @@ class Snapshots(Bcfg2.Server.Plugin.Statistics,
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Statistics.__init__(self)
self.session = Bcfg2.Server.Snapshots.setup_session(core.cfile)
- self.work_queue = Queue.Queue()
+ self.work_queue = Queue()
self.loader = threading.Thread(target=self.load_snapshot)
self.loader.start()
@@ -92,9 +103,9 @@ class Snapshots(Bcfg2.Server.Plugin.Statistics,
bad = []
state = xdata.find('.//Statistics')
correct = state.get('state') == 'clean'
- revision = unicode(state.get('revision', '-1'))
+ revision = u_str(state.get('revision', '-1'))
for entry in state.find('.//Bad'):
- data = [False, False, unicode(entry.get('name'))] \
+ data = [False, False, u_str(entry.get('name'))] \
+ build_snap_ent(entry)
if entry.tag in ftypes:
etag = 'Path'
@@ -107,24 +118,24 @@ class Snapshots(Bcfg2.Server.Plugin.Statistics,
else:
etag = entry.tag
if entry.get('name') in entries[etag]:
- data = [True, False, unicode(entry.get('name'))] + \
+ data = [True, False, u_str(entry.get('name'))] + \
build_snap_ent(entry)
else:
- data = [True, False, unicode(entry.get('name'))] + \
+ data = [True, False, u_str(entry.get('name'))] + \
build_snap_ent(entry)
for entry in state.find('.//Extra'):
if entry.tag in datafields:
data = build_snap_ent(entry)[1]
- ename = unicode(entry.get('name'))
+ ename = u_str(entry.get('name'))
data['name'] = ename
extra[entry.tag][ename] = data
else:
- print "extra", entry.tag, entry.get('name')
+ print("extra", entry.tag, entry.get('name'))
t2 = time.time()
snap = Snapshot.from_data(self.session, correct, revision,
metadata, entries, extra)
self.session.add(snap)
self.session.commit()
t3 = time.time()
- logger.info("Snapshot storage took %fs" % (t3-t2))
+ logger.info("Snapshot storage took %fs" % (t3 - t2))
return True
diff --git a/src/lib/Server/Plugins/Statistics.py b/src/lib/Server/Plugins/Statistics.py
index c7fa0e534..f4f4c7175 100644
--- a/src/lib/Server/Plugins/Statistics.py
+++ b/src/lib/Server/Plugins/Statistics.py
@@ -8,7 +8,6 @@ import logging
from lxml.etree import XML, SubElement, Element, XMLSyntaxError
import lxml.etree
import os
-import Queue
from time import asctime, localtime, time, strptime, mktime
import threading
@@ -33,7 +32,8 @@ class StatisticsStore(object):
or force:
try:
fout = open(self.filename + '.new', 'w')
- except IOError, ioerr:
+ except IOError:
+ ioerr = sys.exc_info()[1]
self.logger.error("Failed to open %s for writing: %s" % (self.filename + '.new', ioerr))
else:
fout.write(lxml.etree.tostring(self.element, encoding='UTF-8', xml_declaration=True))
diff --git a/src/lib/Server/Plugins/Svn2.py b/src/lib/Server/Plugins/Svn2.py
index 875e9e6a6..35f555294 100644
--- a/src/lib/Server/Plugins/Svn2.py
+++ b/src/lib/Server/Plugins/Svn2.py
@@ -1,4 +1,3 @@
-import os
try:
import pysvn
missing = False
@@ -7,7 +6,7 @@ except:
import Bcfg2.Server.Plugin
class Svn2(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+ Bcfg2.Server.Plugin.Version):
"""Svn is a version plugin for dealing with Bcfg2 repos."""
name = 'Svn2'
__version__ = '$Id$'
@@ -36,7 +35,7 @@ class Svn2(Bcfg2.Server.Plugin.Plugin,
if not self.revision:
raise Bcfg2.Server.Plugin.PluginInitError
- self.logger.debug("Initialized svn plugin with svn root %s at revision %s" \
+ self.logger.debug("Initialized svn plugin with svn root %s at revision %s"
% (self.svn_root, revision))
def get_revision(self):
@@ -63,25 +62,50 @@ class Svn2(Bcfg2.Server.Plugin.Plugin,
#FIXME - look for conflicts?
- for file in file_list:
- stat = self.client.status(file)
+ for fname in file_list:
+ stat = self.client.status(fname)
self.client.add([f.path for f in stat \
if f.text_status == pysvn.wc_status_kind.unversioned])
try:
self.revision = self.client.checkin([self.datastore], comment,
recurse=True)
self.revision = self.client.update(self.datastore, recurse=True)[0]
- self.logger.info("Svn2: Commited changes. At %s" % self.revision.number)
- except:
- self.logger.error("Svn2: Failed to commit changes", exc_info=1)
+ self.logger.info("Svn2: Commited changes. At %s" %
+ self.revision.number)
+ except Exception, err:
+ # try to be smart about the error we got back
+ details = None
+ if "callback_ssl_server_trust_prompt" in err.message:
+ details = "SVN server certificate is not trusted"
+ elif "callback_get_login" in err.message:
+ details = "SVN credentials not cached"
+
+ if details is None:
+ self.logger.error("Svn2: Failed to commit changes",
+ exc_info=1)
+ else:
+ self.logger.error("Svn2: Failed to commit changes: %s" %
+ details)
def Update(self):
'''Svn2.Update() => True|False\nUpdate svn working copy\n'''
try:
old_revision = self.revision.number
self.revision = self.client.update(self.datastore, recurse=True)[0]
- except:
- self.logger.error("Svn2: Failed to update server repository", exc_info=1)
+ except Exception, err:
+ # try to be smart about the error we got back
+ details = None
+ if "callback_ssl_server_trust_prompt" in err.message:
+ details = "SVN server certificate is not trusted"
+ elif "callback_get_login" in err.message:
+ details = "SVN credentials not cached"
+
+ if details is None:
+ self.logger.error("Svn2: Failed to update server repository",
+ exc_info=1)
+ else:
+ self.logger.error("Svn2: Failed to update server repository: %s" %
+ details)
return False
if old_revision == self.revision.number:
diff --git a/src/lib/Server/Plugins/TCheetah.py b/src/lib/Server/Plugins/TCheetah.py
index d40f4baf3..49be88881 100644
--- a/src/lib/Server/Plugins/TCheetah.py
+++ b/src/lib/Server/Plugins/TCheetah.py
@@ -6,6 +6,9 @@ import logging
import sys
import traceback
import Bcfg2.Server.Plugin
+# py3k compatibility
+if sys.hexversion >= 0x03000000:
+ unicode = str
logger = logging.getLogger('Bcfg2.Plugins.TCheetah')
@@ -36,7 +39,8 @@ class TemplateFile:
self.template = Cheetah.Template.Template(open(self.name).read(),
compilerSettings=s,
searchList=self.searchlist)
- except Cheetah.Parser.ParseError, perror:
+ except Cheetah.Parser.ParseError:
+ perror = sys.exc_info()[1]
logger.error("Cheetah parse error for file %s" % (self.name))
logger.error(perror.report())
@@ -56,7 +60,7 @@ class TemplateFile:
entry.text = self.template
else:
if entry.get('encoding') == 'base64':
- # take care of case where file needs base64 encoding
+ # take care of case where file needs base64 encoding
entry.text = binascii.b2a_base64(self.template)
else:
entry.text = unicode(str(self.template), self.encoding)
diff --git a/src/lib/Server/Plugins/TGenshi.py b/src/lib/Server/Plugins/TGenshi.py
index 2a12672cc..bc5e00400 100644
--- a/src/lib/Server/Plugins/TGenshi.py
+++ b/src/lib/Server/Plugins/TGenshi.py
@@ -3,7 +3,11 @@ __revision__ = '$Revision$'
import binascii
import logging
+import sys
import Bcfg2.Server.Plugin
+# py3k compatibility
+if sys.hexversion >= 0x03000000:
+ unicode = str
logger = logging.getLogger('Bcfg2.Plugins.TGenshi')
@@ -63,11 +67,14 @@ class TemplateFile:
try:
self.template = loader.load(self.name, cls=self.template_cls,
encoding=self.encoding)
- except LookupError, lerror:
+ except LookupError:
+ lerror = sys.exc_info()[1]
logger.error('Genshi lookup error: %s' % lerror)
- except TemplateError, terror:
+ except TemplateError:
+ terror = sys.exc_info()[1]
logger.error('Genshi template error: %s' % terror)
- except genshi.input.ParseError, perror:
+ except genshi.input.ParseError:
+ perror = sys.exc_info()[1]
logger.error('Genshi parse error: %s' % perror)
def bind_entry(self, entry, metadata):
@@ -92,7 +99,7 @@ class TemplateFile:
entry.text = textdata
else:
if entry.get('encoding') == 'base64':
- # take care of case where file needs base64 encoding
+ # take care of case where file needs base64 encoding
entry.text = binascii.b2a_base64(textdata)
else:
entry.text = unicode(textdata, self.encoding)
@@ -107,10 +114,12 @@ class TemplateFile:
entry.text = unicode(xmldata, self.encoding)
if entry.text == '':
entry.set('empty', 'true')
- except TemplateError, terror:
+ except TemplateError:
+ terror = sys.exc_info()[1]
logger.error('Genshi template error: %s' % terror)
raise Bcfg2.Server.Plugin.PluginExecutionError
- except AttributeError, err:
+ except AttributeError:
+ err = sys.exc_info()[1]
logger.error('Genshi template loading error: %s' % err)
raise Bcfg2.Server.Plugin.PluginExecutionError
diff --git a/src/lib/Server/Reports/backends.py b/src/lib/Server/Reports/backends.py
index 9207038ed..85241932f 100644
--- a/src/lib/Server/Reports/backends.py
+++ b/src/lib/Server/Reports/backends.py
@@ -1,35 +1,34 @@
from django.contrib.auth.models import User
from nisauth import *
+
class NISBackend(object):
def authenticate(self, username=None, password=None):
try:
- print "start nis authenticate"
+ print("start nis authenticate")
n = nisauth(username, password)
temp_pass = User.objects.make_random_password(100)
nis_user = dict(username=username,
)
- user_session_obj = dict(
- email = username,
- first_name = None,
- last_name = None,
- uid = n.uid
- )
+ user_session_obj = dict(email=username,
+ first_name=None,
+ last_name=None,
+ uid=n.uid)
user, created = User.objects.get_or_create(username=username)
-
+
return user
- except NISAUTHError, e:
- print str(e)
+ except NISAUTHError:
+ e = sys.exc_info()[1]
+ print(e)
return None
-
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
- except User.DoesNotExist, e:
- print str(e)
+ except User.DoesNotExist:
+ e = sys.exc_info()[1]
+ print(e)
return None
-
diff --git a/src/lib/Server/Reports/importscript.py b/src/lib/Server/Reports/importscript.py
index cdfd8079c..b6a3c2599 100755
--- a/src/lib/Server/Reports/importscript.py
+++ b/src/lib/Server/Reports/importscript.py
@@ -1,11 +1,17 @@
#! /usr/bin/env python
-'''Imports statistics.xml and clients.xml files in to database backend for new statistics engine'''
+"""
+Imports statistics.xml and clients.xml files in to database backend for
+new statistics engine
+"""
__revision__ = '$Revision$'
-import os, sys, binascii
+import binascii
+import os
+import sys
try:
import Bcfg2.Server.Reports.settings
-except Exception, e:
+except Exception:
+ e = sys.exc_info()[1]
sys.stderr.write("Failed to load configuration settings. %s\n" % e)
sys.exit(1)
@@ -24,21 +30,24 @@ from datetime import datetime
from time import strptime
from django.db import connection
from Bcfg2.Server.Reports.updatefix import update_database
-import ConfigParser
import logging
import Bcfg2.Logger
import platform
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
+
+
def build_reason_kwargs(r_ent):
- binary_file=False
+ binary_file = False
if r_ent.get('current_bfile', False):
- binary_file=True
+ binary_file = True
rc_diff = r_ent.get('current_bfile')
- if len(rc_diff) > 1024*1024:
+ if len(rc_diff) > 1024 * 1024:
rc_diff = ''
elif len(rc_diff) == 0:
# No point in flagging binary if we have no data
- binary_file=False
+ binary_file = False
elif r_ent.get('current_bdiff', False):
rc_diff = binascii.a2b_base64(r_ent.get('current_bdiff'))
elif r_ent.get('current_diff', False):
@@ -57,7 +66,7 @@ def build_reason_kwargs(r_ent):
current_to=r_ent.get('current_to', default=""),
version=r_ent.get('version', default=""),
current_version=r_ent.get('current_version', default=""),
- current_exists=r_ent.get('current_exists', default="True").capitalize()=="True",
+ current_exists=r_ent.get('current_exists', default="True").capitalize() == "True",
current_diff=rc_diff,
is_binary=binary_file)
@@ -75,7 +84,7 @@ def load_stats(cdata, sdata, vlevel, logger, quick=False, location=''):
name = node.get('name')
c_inst, created = Client.objects.get_or_create(name=name)
if vlevel > 0:
- logger.info("Client %s added to db" % name)
+ logger.info("Client %s added to db" % name)
clients[name] = c_inst
try:
pingability[name]
@@ -93,24 +102,30 @@ def load_stats(cdata, sdata, vlevel, logger, quick=False, location=''):
continue
else:
newint = Interaction(client=c_inst,
- timestamp = timestamp,
- state = statistics.get('state', default="unknown"),
- repo_rev_code = statistics.get('revision',default="unknown"),
- client_version = statistics.get('client_version',default="unknown"),
- goodcount = statistics.get('good',default="0"),
- totalcount = statistics.get('total',default="0"),
- server = location)
+ timestamp=timestamp,
+ state=statistics.get('state',
+ default="unknown"),
+ repo_rev_code=statistics.get('revision',
+ default="unknown"),
+ client_version=statistics.get('client_version',
+ default="unknown"),
+ goodcount=statistics.get('good',
+ default="0"),
+ totalcount=statistics.get('total',
+ default="0"),
+ server=location)
newint.save()
current_interaction = newint
if vlevel > 0:
- logger.info("Interaction for %s at %s with id %s INSERTED in to db"%(c_inst.id,
+ logger.info("Interaction for %s at %s with id %s INSERTED in to db" % (c_inst.id,
timestamp, current_interaction.id))
-
- counter_fields = { TYPE_CHOICES[0]: 0, TYPE_CHOICES[1]: 0, TYPE_CHOICES[2]: 0 }
+ counter_fields = {TYPE_CHOICES[0]: 0,
+ TYPE_CHOICES[1]: 0,
+ TYPE_CHOICES[2]: 0}
pattern = [('Bad/*', TYPE_CHOICES[0]),
('Extra/*', TYPE_CHOICES[2]),
- ('Modified/*', TYPE_CHOICES[1]),]
+ ('Modified/*', TYPE_CHOICES[1])]
for (xpath, type) in pattern:
for x in statistics.findall(xpath):
counter_fields[type] = counter_fields[type] + 1
@@ -118,25 +133,23 @@ def load_stats(cdata, sdata, vlevel, logger, quick=False, location=''):
try:
rr = None
- if not quick:
- try:
- rr = Reason.objects.filter(**kargs)[0]
- except IndexError:
- pass
- if not rr:
+ try:
+ rr = Reason.objects.filter(**kargs)[0]
+ except IndexError:
rr = Reason(**kargs)
rr.save()
if vlevel > 0:
logger.info("Created reason: %s" % rr.id)
- except Exception, ex:
+ except Exception:
+ ex = sys.exc_info()[1]
logger.error("Failed to create reason for %s: %s" % (x.get('name'), ex))
rr = Reason(current_exists=x.get('current_exists',
- default="True").capitalize()=="True")
+ default="True").capitalize() == "True")
rr.save()
entry, created = Entries.objects.get_or_create(\
name=x.get('name'), kind=x.tag)
-
+
Entries_interactions(entry=entry, reason=rr,
interaction=current_interaction,
type=type[0]).save()
@@ -151,7 +164,7 @@ def load_stats(cdata, sdata, vlevel, logger, quick=False, location=''):
mperfs = []
for times in statistics.findall('OpStamps'):
- for metric, value in times.items():
+ for metric, value in list(times.items()):
mmatch = []
if not quick:
mmatch = Performance.objects.filter(metric=metric, value=value)
@@ -164,7 +177,7 @@ def load_stats(cdata, sdata, vlevel, logger, quick=False, location=''):
mperfs.append(mperf)
current_interaction.performance_items.add(*mperfs)
- for key in pingability.keys():
+ for key in list(pingability.keys()):
if key not in clients:
continue
try:
@@ -191,27 +204,33 @@ if __name__ == '__main__':
clientpath = False
statpath = False
syslog = False
-
+
try:
- opts, args = getopt(argv[1:], "hvudc:s:CS", ["help", "verbose", "updates" ,
- "debug", "clients=", "stats=",
- "config=", "syslog"])
- except GetoptError, mesg:
+ opts, args = getopt(argv[1:], "hvudc:s:CS", ["help",
+ "verbose",
+ "updates",
+ "debug",
+ "clients=",
+ "stats=",
+ "config=",
+ "syslog"])
+ except GetoptError:
+ mesg = sys.exc_info()[1]
# print help information and exit:
- print "%s\nUsage:\nimportscript.py [-h] [-v] [-u] [-d] [-S] [-C bcfg2 config file] [-c clients-file] [-s statistics-file]" % (mesg)
- raise SystemExit, 2
+ print("%s\nUsage:\nimportscript.py [-h] [-v] [-u] [-d] [-S] [-C bcfg2 config file] [-c clients-file] [-s statistics-file]" % (mesg))
+ raise SystemExit(2)
for o, a in opts:
if o in ("-h", "--help"):
- print "Usage:\nimportscript.py [-h] [-v] -c <clients-file> -s <statistics-file> \n"
- print "h : help; this message"
- print "v : verbose; print messages on record insertion/skip"
- print "u : updates; print status messages as items inserted semi-verbose"
- print "d : debug; print most SQL used to manipulate database"
- print "C : path to bcfg2.conf config file."
- print "c : clients.xml file"
- print "s : statistics.xml file"
- print "S : syslog; output to syslog"
+ print("Usage:\nimportscript.py [-h] [-v] -c <clients-file> -s <statistics-file> \n")
+ print("h : help; this message")
+ print("v : verbose; print messages on record insertion/skip")
+ print("u : updates; print status messages as items inserted semi-verbose")
+ print("d : debug; print most SQL used to manipulate database")
+ print("C : path to bcfg2.conf config file.")
+ print("c : clients.xml file")
+ print("s : statistics.xml file")
+ print("S : syslog; output to syslog")
raise SystemExit
if o in ["-C", "--config"]:
cpath = a
@@ -243,28 +262,33 @@ if __name__ == '__main__':
try:
statpath = "%s/etc/statistics.xml" % cf.get('server', 'repository')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
- print "Could not read bcfg2.conf; exiting"
- raise SystemExit, 1
+ print("Could not read bcfg2.conf; exiting")
+ raise SystemExit(1)
try:
statsdata = XML(open(statpath).read())
except (IOError, XMLSyntaxError):
- print("StatReports: Failed to parse %s"%(statpath))
- raise SystemExit, 1
+ print("StatReports: Failed to parse %s" % (statpath))
+ raise SystemExit(1)
if not clientpath:
try:
clientspath = "%s/Metadata/clients.xml" % \
cf.get('server', 'repository')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
- print "Could not read bcfg2.conf; exiting"
- raise SystemExit, 1
+ print("Could not read bcfg2.conf; exiting")
+ raise SystemExit(1)
try:
clientsdata = XML(open(clientspath).read())
except (IOError, XMLSyntaxError):
- print("StatReports: Failed to parse %s"%(clientspath))
- raise SystemExit, 1
+ print("StatReports: Failed to parse %s" % (clientspath))
+ raise SystemExit(1)
q = '-O3' in sys.argv
# Be sure the database is ready for new schema
update_database()
- load_stats(clientsdata, statsdata, verb, logger, quick=q, location=platform.node())
+ load_stats(clientsdata,
+ statsdata,
+ verb,
+ logger,
+ quick=q,
+ location=platform.node())
diff --git a/src/lib/Server/Reports/manage.py b/src/lib/Server/Reports/manage.py
index 5e78ea979..858bddeca 100755
--- a/src/lib/Server/Reports/manage.py
+++ b/src/lib/Server/Reports/manage.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
from django.core.management import execute_manager
try:
- import settings # Assumed to be in the same directory.
+ import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
diff --git a/src/lib/Server/Reports/nisauth.py b/src/lib/Server/Reports/nisauth.py
index b4be0e391..6fc346f1e 100644
--- a/src/lib/Server/Reports/nisauth.py
+++ b/src/lib/Server/Reports/nisauth.py
@@ -1,15 +1,17 @@
-import os
-import crypt, nis
+import crypt
+import nis
from Bcfg2.Server.Reports.settings import AUTHORIZED_GROUP
"""Checks with NIS to see if the current user is in the support group"""
__revision__ = "$Revision: $"
+
class NISAUTHError(Exception):
"""NISAUTHError is raised when somehting goes boom."""
pass
+
class nisauth(object):
group_test = False
samAcctName = None
@@ -18,26 +20,27 @@ class nisauth(object):
telephoneNumber = None
title = None
memberOf = None
- department = None #this will be a list
+ department = None # this will be a list
mail = None
- extensionAttribute1 = None #badgenumber
+ extensionAttribute1 = None # badgenumber
badge_no = None
uid = None
- def __init__(self,login,passwd=None):
+ def __init__(self, login, passwd=None):
"""get user profile from NIS"""
try:
p = nis.match(login, 'passwd.byname').split(":")
- print p
+ print(p)
except:
raise NISAUTHError('username')
# check user password using crypt and 2 character salt from passwd file
if p[1] == crypt.crypt(passwd, p[1][:2]):
# check to see if user is in valid support groups
# will have to include these groups in a settings file eventually
- if not login in nis.match(AUTHORIZED_GROUP, 'group.byname').split(':')[-1].split(','):
+ if not login in nis.match(AUTHORIZED_GROUP,
+ 'group.byname').split(':')[-1].split(','):
raise NISAUTHError('group')
self.uid = p[2]
- print self.uid
+ print(self.uid)
else:
raise NISAUTHError('password')
diff --git a/src/lib/Server/Reports/reports/models.py b/src/lib/Server/Reports/reports/models.py
index 1963a9090..d94b2e1ba 100644
--- a/src/lib/Server/Reports/reports/models.py
+++ b/src/lib/Server/Reports/reports/models.py
@@ -29,6 +29,7 @@ TYPE_CHOICES = (
(TYPE_EXTRA, 'Extra'),
)
+
def convert_entry_type_to_id(type_name):
"""Convert a entry type to its entry id"""
for e_id, e_name in TYPE_CHOICES:
@@ -36,23 +37,25 @@ def convert_entry_type_to_id(type_name):
return e_id
return -1
+
class ClientManager(models.Manager):
"""Extended client manager functions."""
def active(self, timestamp=None):
- """returns a set of clients that have been created and have not yet been
- expired as of optional timestmamp argument. Timestamp should be a
- datetime object."""
-
+ """returns a set of clients that have been created and have not
+ yet been expired as of optional timestmamp argument. Timestamp
+ should be a datetime object."""
+
if timestamp == None:
timestamp = datetime.now()
elif not isinstance(timestamp, datetime):
- raise ValueError, 'Expected a datetime object'
+ raise ValueError('Expected a datetime object')
else:
try:
- timestamp = datetime(*strptime(timestamp, "%Y-%m-%d %H:%M:%S")[0:6])
+ timestamp = datetime(*strptime(timestamp,
+ "%Y-%m-%d %H:%M:%S")[0:6])
except ValueError:
return self.none()
-
+
return self.filter(Q(expiration__gt=timestamp) | Q(expiration__isnull=True),
creation__lt=timestamp)
@@ -65,25 +68,27 @@ class Client(models.Model):
null=True, blank=True,
related_name="parent_client")
expiration = models.DateTimeField(blank=True, null=True)
-
+
def __str__(self):
return self.name
objects = ClientManager()
-
+
class Admin:
pass
+
class Ping(models.Model):
"""Represents a ping of a client (sparsely)."""
client = models.ForeignKey(Client, related_name="pings")
starttime = models.DateTimeField()
endtime = models.DateTimeField()
- status = models.CharField(max_length=4, choices=PING_CHOICES)#up/down
+ status = models.CharField(max_length=4, choices=PING_CHOICES) # up/down
class Meta:
get_latest_by = 'endtime'
-
+
+
class InteractiveManager(models.Manager):
"""Manages interactions objects."""
@@ -94,31 +99,31 @@ class InteractiveManager(models.Manager):
This method uses aggregated queries to return a ValuesQueryDict object.
Faster then raw sql since this is executed as a single query.
"""
-
- return self.values('client').annotate(max_timestamp=Max('timestamp')).values()
- def interaction_per_client(self, maxdate = None, active_only=True):
+ return list(self.values('client').annotate(max_timestamp=Max('timestamp')).values())
+
+ def interaction_per_client(self, maxdate=None, active_only=True):
"""
Returns the most recent interactions for clients as of a date
Arguments:
maxdate -- datetime object. Most recent date to pull. (dafault None)
active_only -- Include only active clients (default True)
-
+
"""
- if maxdate and not isinstance(maxdate,datetime):
- raise ValueError, 'Expected a datetime object'
- return self.filter(id__in = self.get_interaction_per_client_ids(maxdate, active_only))
+ if maxdate and not isinstance(maxdate, datetime):
+ raise ValueError('Expected a datetime object')
+ return self.filter(id__in=self.get_interaction_per_client_ids(maxdate, active_only))
- def get_interaction_per_client_ids(self, maxdate = None, active_only=True):
+ def get_interaction_per_client_ids(self, maxdate=None, active_only=True):
"""
Returns the ids of most recent interactions for clients as of a date.
Arguments:
maxdate -- datetime object. Most recent date to pull. (dafault None)
active_only -- Include only active clients (default True)
-
+
"""
from django.db import connection
cursor = connection.cursor()
@@ -127,10 +132,10 @@ class InteractiveManager(models.Manager):
sql = 'select reports_interaction.id, x.client_id from (select client_id, MAX(timestamp) ' + \
'as timer from reports_interaction'
if maxdate:
- if not isinstance(maxdate,datetime):
- raise ValueError, 'Expected a datetime object'
+ if not isinstance(maxdate, datetime):
+ raise ValueError('Expected a datetime object')
sql = sql + " where timestamp <= '%s' " % maxdate
- cfilter = "(expiration is null or expiration > '%s') and creation <= '%s'" % (maxdate,maxdate)
+ cfilter = "(expiration is null or expiration > '%s') and creation <= '%s'" % (maxdate, maxdate)
sql = sql + ' GROUP BY client_id) x, reports_interaction where ' + \
'reports_interaction.client_id = x.client_id AND reports_interaction.timestamp = x.timer'
if active_only:
@@ -144,16 +149,17 @@ class InteractiveManager(models.Manager):
pass
return []
+
class Interaction(models.Model):
"""Models each reconfiguration operation interaction between client and server."""
client = models.ForeignKey(Client, related_name="interactions",)
- timestamp = models.DateTimeField()#Timestamp for this record
- state = models.CharField(max_length=32)#good/bad/modified/etc
- repo_rev_code = models.CharField(max_length=64)#repo revision at time of interaction
- client_version = models.CharField(max_length=32)#Client Version
- goodcount = models.IntegerField()#of good config-items
- totalcount = models.IntegerField()#of total config-items
- server = models.CharField(max_length=256) # Name of the server used for the interaction
+ timestamp = models.DateTimeField() # Timestamp for this record
+ state = models.CharField(max_length=32) # good/bad/modified/etc
+ repo_rev_code = models.CharField(max_length=64) # repo revision at time of interaction
+ client_version = models.CharField(max_length=32) # Client Version
+ goodcount = models.IntegerField() # of good config-items
+ totalcount = models.IntegerField() # of total config-items
+ server = models.CharField(max_length=256) # Name of the server used for the interaction
bad_entries = models.IntegerField(default=-1)
modified_entries = models.IntegerField(default=-1)
extra_entries = models.IntegerField(default=-1)
@@ -163,25 +169,25 @@ class Interaction(models.Model):
def percentgood(self):
if not self.totalcount == 0:
- return (self.goodcount/float(self.totalcount))*100
+ return (self.goodcount / float(self.totalcount)) * 100
else:
return 0
def percentbad(self):
if not self.totalcount == 0:
- return ((self.totalcount-self.goodcount)/(float(self.totalcount)))*100
+ return ((self.totalcount - self.goodcount) / (float(self.totalcount))) * 100
else:
return 0
-
+
def isclean(self):
if (self.bad_entry_count() == 0 and self.goodcount == self.totalcount):
return True
else:
return False
-
+
def isstale(self):
- if (self == self.client.current_interaction):#Is Mostrecent
- if(datetime.now()-self.timestamp > timedelta(hours=25) ):
+ if (self == self.client.current_interaction): # Is Mostrecent
+ if(datetime.now() - self.timestamp > timedelta(hours=25)):
return True
else:
return False
@@ -194,10 +200,11 @@ class Interaction(models.Model):
return True
else:
return False
+
def save(self):
- super(Interaction, self).save() #call the real save...
+ super(Interaction, self).save() # call the real save...
self.client.current_interaction = self.client.interactions.latest()
- self.client.save()#save again post update
+ self.client.save() # save again post update
def delete(self):
'''Override the default delete. Allows us to remove Performance items'''
@@ -239,35 +246,38 @@ class Interaction(models.Model):
self.extra_entries = Entries_interactions.objects.filter(interaction=self, type=TYPE_EXTRA).count()
self.save()
return self.extra_entries
-
+
objects = InteractiveManager()
class Admin:
list_display = ('client', 'timestamp', 'state')
list_filter = ['client', 'timestamp']
pass
+
class Meta:
get_latest_by = 'timestamp'
ordering = ['-timestamp']
unique_together = ("client", "timestamp")
+
class Reason(models.Model):
"""reason why modified or bad entry did not verify, or changed."""
owner = models.TextField(max_length=128, blank=True)
current_owner = models.TextField(max_length=128, blank=True)
group = models.TextField(max_length=128, blank=True)
current_group = models.TextField(max_length=128, blank=True)
- perms = models.TextField(max_length=4, blank=True)#txt fixes typing issue
+ perms = models.TextField(max_length=4, blank=True) # txt fixes typing issue
current_perms = models.TextField(max_length=4, blank=True)
- status = models.TextField(max_length=3, blank=True)#on/off/(None)
- current_status = models.TextField(max_length=1, blank=True)#on/off/(None)
+ status = models.TextField(max_length=3, blank=True) # on/off/(None)
+ current_status = models.TextField(max_length=1, blank=True) # on/off/(None)
to = models.TextField(max_length=256, blank=True)
current_to = models.TextField(max_length=256, blank=True)
version = models.TextField(max_length=128, blank=True)
current_version = models.TextField(max_length=128, blank=True)
- current_exists = models.BooleanField()#False means its missing. Default True
+ current_exists = models.BooleanField() # False means its missing. Default True
current_diff = models.TextField(max_length=1280, blank=True)
is_binary = models.BooleanField(default=False)
+
def _str_(self):
return "Reason"
@@ -278,7 +288,7 @@ class Reason(models.Model):
cursor = connection.cursor()
cursor.execute('delete from reports_reason where not exists (select rei.id from reports_entries_interactions rei where rei.reason_id = reports_reason.id)')
transaction.set_dirty()
-
+
class Entries(models.Model):
"""Contains all the entries feed by the client."""
@@ -295,19 +305,22 @@ class Entries(models.Model):
cursor = connection.cursor()
cursor.execute('delete from reports_entries where not exists (select rei.id from reports_entries_interactions rei where rei.entry_id = reports_entries.id)')
transaction.set_dirty()
-
+
+
class Entries_interactions(models.Model):
"""Define the relation between the reason, the interaction and the entry."""
entry = models.ForeignKey(Entries)
reason = models.ForeignKey(Reason)
interaction = models.ForeignKey(Interaction)
type = models.IntegerField(choices=TYPE_CHOICES)
-
+
+
class Performance(models.Model):
"""Object representing performance data for any interaction."""
interaction = models.ManyToManyField(Interaction, related_name="performance_items")
metric = models.CharField(max_length=128)
value = models.DecimalField(max_digits=32, decimal_places=16)
+
def __str__(self):
return self.metric
@@ -318,7 +331,8 @@ class Performance(models.Model):
cursor = connection.cursor()
cursor.execute('delete from reports_performance where not exists (select ri.id from reports_performance_interaction ri where ri.performance_id = reports_performance.id)')
transaction.set_dirty()
-
+
+
class InternalDatabaseVersion(models.Model):
"""Object that tell us to witch version is the database."""
version = models.IntegerField()
diff --git a/src/lib/Server/Reports/reports/templates/base.html b/src/lib/Server/Reports/reports/templates/base.html
index a64f1c76a..6ef4c9aff 100644
--- a/src/lib/Server/Reports/reports/templates/base.html
+++ b/src/lib/Server/Reports/reports/templates/base.html
@@ -87,7 +87,7 @@
<div style='clear:both'></div>
</div><!-- document -->
<div id="footer">
- <span>Bcfg2 Version 1.2.0pre1</span>
+ <span>Bcfg2 Version 1.2.0pre2</span>
</div>
<div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div>
diff --git a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py b/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
index 7fffe289d..629984f26 100644
--- a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
+++ b/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py
@@ -21,9 +21,9 @@ def page_navigator(context):
path = context['request'].META['PATH_INFO']
total_pages = int(context['total_pages'])
records_per_page = int(context['records_per_page'])
- except KeyError, e:
+ except KeyError:
return fragment
- except ValueError, e:
+ except ValueError:
return fragment
if total_pages < 2:
@@ -84,7 +84,8 @@ def page_navigator(context):
except Resolver404:
path = "404"
- except NoReverseMatch, nr:
+ except NoReverseMatch:
+ nr = sys.exc_info()[1]
path = "NoReverseMatch: %s" % nr
except ValueError:
path = "ValueError"
@@ -193,12 +194,13 @@ class AddUrlFilter(template.Node):
del kwargs['server']
try:
link = reverse(view, args=args, kwargs=kwargs)
- except NoReverseMatch, rm:
+ except NoReverseMatch:
link = reverse(self.fallback_view, args=None,
kwargs={ filter_name: filter_value })
- except NoReverseMatch, rm:
+ except NoReverseMatch:
+ rm = sys.exc_info()[1]
raise rm
- except (Resolver404, ValueError), e:
+ except (Resolver404, ValueError):
pass
return link
@@ -219,9 +221,9 @@ def add_url_filter(parser, token):
filter_name = filter_name.strip()
filter_value = parser.compile_filter(filter_value)
except ValueError:
- raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
+ raise template.TemplateSyntaxError("%r tag requires exactly one argument" % token.contents.split()[0])
if not filter_name or not filter_value:
- raise template.TemplateSyntaxError, "argument should be a filter=value pair"
+ raise template.TemplateSyntaxError("argument should be a filter=value pair")
return AddUrlFilter(filter_name, filter_value)
@@ -268,7 +270,7 @@ def to_media_url(parser, token):
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]
+ 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/templatetags/syntax_coloring.py b/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py
index 43dafb262..291528e2e 100644
--- a/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py
+++ b/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py
@@ -1,3 +1,4 @@
+import sys
from django import template
from django.utils.encoding import smart_unicode, smart_str
from django.utils.html import conditional_escape
@@ -14,6 +15,12 @@ try:
except:
colorize = False
+def u_str(string):
+ if sys.hexversion >= 0x03000000:
+ return string
+ else:
+ return unicode(string)
+
@register.filter
def syntaxhilight(value, arg="diff", autoescape=None):
"""
@@ -26,9 +33,9 @@ def syntaxhilight(value, arg="diff", autoescape=None):
if colorize:
try:
- output = u'<style type="text/css">' \
+ output = u_str('<style type="text/css">') \
+ smart_unicode(HtmlFormatter().get_style_defs('.highlight')) \
- + u'</style>'
+ + u_str('</style>')
lexer = get_lexer_by_name(arg)
output += highlight(value, lexer, HtmlFormatter())
@@ -36,6 +43,6 @@ def syntaxhilight(value, arg="diff", autoescape=None):
except:
return value
else:
- return mark_safe(u'<div class="note-box">Tip: Install pygments for highlighting</div><pre>%s</pre>' % value)
+ return mark_safe(u_str('<div class="note-box">Tip: Install pygments for highlighting</div><pre>%s</pre>') % value)
syntaxhilight.needs_autoescape = True
diff --git a/src/lib/Server/Reports/reports/views.py b/src/lib/Server/Reports/reports/views.py
index 00d35c092..ccd71a60e 100644
--- a/src/lib/Server/Reports/reports/views.py
+++ b/src/lib/Server/Reports/reports/views.py
@@ -3,22 +3,26 @@ Report views
Functions to handle all of the reporting views.
"""
-from django.template import Context, RequestContext, loader
-from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError, Http404
+from datetime import datetime, timedelta
+import sys
+from time import strptime
+
+from django.template import Context, RequestContext
+from django.http import \
+ HttpResponse, HttpResponseRedirect, HttpResponseServerError, Http404
from django.shortcuts import render_to_response, get_object_or_404
-from django.core.urlresolvers import resolve, reverse, Resolver404, NoReverseMatch
+from django.core.urlresolvers import \
+ resolve, reverse, Resolver404, NoReverseMatch
from django.db import connection
-from django.db.backends import util
from Bcfg2.Server.Reports.reports.models import *
-from datetime import datetime, timedelta
-from time import strptime
-import sys
+
class PaginationError(Exception):
"""This error is raised when pagination cannot be completed."""
pass
+
def server_error(request):
"""
500 error handler.
@@ -29,6 +33,7 @@ def server_error(request):
from django.views import debug
return debug.technical_500_response(request, *sys.exc_info())
+
def timeview(fn):
"""
Setup a timeview view
@@ -53,28 +58,32 @@ def timeview(fn):
if cal_date.find(' ') > -1:
kw['hour'] = timestamp.hour
kw['minute'] = timestamp.minute
- return HttpResponseRedirect(reverse(view, args=args, kwargs=kw))
+ return HttpResponseRedirect(reverse(view,
+ args=args,
+ kwargs=kw))
except KeyError:
pass
except:
pass
# FIXME - Handle this
-
+
"""Extract timestamp from args."""
timestamp = None
try:
- timestamp = datetime(int(kwargs.pop('year')), int(kwargs.pop('month')),
+ timestamp = datetime(int(kwargs.pop('year')),
+ int(kwargs.pop('month')),
int(kwargs.pop('day')), int(kwargs.pop('hour', 0)),
int(kwargs.pop('minute', 0)), 0)
kwargs['timestamp'] = timestamp
except KeyError:
pass
- except:
+ except:
raise
return fn(request, **kwargs)
return _handle_timeview
-
+
+
def config_item(request, pk, type="bad"):
"""
Display a single entry.
@@ -83,30 +92,33 @@ def config_item(request, pk, type="bad"):
"""
item = get_object_or_404(Entries_interactions, id=pk)
- timestamp=item.interaction.timestamp
- time_start=item.interaction.timestamp.replace(\
- hour=0, minute=0, second=0, microsecond=0)
- time_end=time_start + timedelta(days=1)
-
- todays_data = Interaction.objects.filter(\
- timestamp__gte=time_start,\
- timestamp__lt=time_end)
- shared_entries = Entries_interactions.objects.filter(entry=item.entry,\
- reason=item.reason, type=item.type,
- interaction__in=[x['id']\
- for x in todays_data.values('id')])
+ timestamp = item.interaction.timestamp
+ time_start = item.interaction.timestamp.replace(hour=0,
+ minute=0,
+ second=0,
+ microsecond=0)
+ time_end = time_start + timedelta(days=1)
+
+ todays_data = Interaction.objects.filter(timestamp__gte=time_start,
+ timestamp__lt=time_end)
+ shared_entries = Entries_interactions.objects.filter(entry=item.entry,
+ reason=item.reason,
+ type=item.type,
+ interaction__in=[x['id']\
+ for x in todays_data.values('id')])
associated_list = Interaction.objects.filter(id__in=[x['interaction']\
for x in shared_entries.values('interaction')])\
- .order_by('client__name','timestamp').select_related().all()
+ .order_by('client__name', 'timestamp').select_related().all()
return render_to_response('config_items/item.html',
- {'item':item,
- 'isextra': item.type == TYPE_EXTRA,
- 'mod_or_bad': type,
- 'associated_list':associated_list,
- 'timestamp' : timestamp},
- context_instance=RequestContext(request))
+ {'item': item,
+ 'isextra': item.type == TYPE_EXTRA,
+ 'mod_or_bad': type,
+ 'associated_list': associated_list,
+ 'timestamp': timestamp},
+ context_instance=RequestContext(request))
+
@timeview
def config_item_list(request, type, timestamp=None):
@@ -115,11 +127,12 @@ def config_item_list(request, type, timestamp=None):
type = convert_entry_type_to_id(type)
if type < 0:
raise Http404
-
+
current_clients = Interaction.objects.get_interaction_per_client_ids(timestamp)
item_list_dict = {}
seen = dict()
- for x in Entries_interactions.objects.filter(interaction__in=current_clients, type=type).select_related():
+ for x in Entries_interactions.objects.filter(interaction__in=current_clients,
+ type=type).select_related():
if (x.entry, x.reason) in seen:
continue
seen[(x.entry, x.reason)] = 1
@@ -129,13 +142,15 @@ def config_item_list(request, type, timestamp=None):
item_list_dict[x.entry.kind] = [x]
for kind in item_list_dict:
- item_list_dict[kind].sort(lambda a,b: cmp(a.entry.name, b.entry.name))
+ item_list_dict[kind].sort(lambda a, b: cmp(a.entry.name, b.entry.name))
- return render_to_response('config_items/listing.html', {'item_list_dict':item_list_dict,
- 'mod_or_bad':mod_or_bad,
- 'timestamp' : timestamp},
+ return render_to_response('config_items/listing.html',
+ {'item_list_dict': item_list_dict,
+ 'mod_or_bad': mod_or_bad,
+ 'timestamp': timestamp},
context_instance=RequestContext(request))
+
@timeview
def client_index(request, timestamp=None):
"""
@@ -149,8 +164,10 @@ def client_index(request, timestamp=None):
.order_by("client__name").all()
return render_to_response('clients/index.html',
- { 'inter_list': list, 'timestamp' : timestamp},
- context_instance=RequestContext(request))
+ {'inter_list': list,
+ 'timestamp': timestamp},
+ context_instance=RequestContext(request))
+
@timeview
def client_detailed_list(request, timestamp=None, **kwargs):
@@ -165,7 +182,8 @@ def client_detailed_list(request, timestamp=None, **kwargs):
kwargs['page_limit'] = 0
return render_history_view(request, 'clients/detailed-list.html', **kwargs)
-def client_detail(request, hostname = None, pk = None):
+
+def client_detail(request, hostname=None, pk=None):
context = dict()
client = get_object_or_404(Client, name=hostname)
if(pk == None):
@@ -177,6 +195,7 @@ def client_detail(request, hostname = None, pk = None):
return render_history_view(request, 'clients/detail.html', page_limit=5,
client=client, maxdate=context['interaction'].timestamp, context=context)
+
def client_manage(request):
"""Manage client expiration"""
message = ''
@@ -186,12 +205,12 @@ def client_manage(request):
client_action = request.POST.get('client_action', None)
client = Client.objects.get(name=client_name)
if client_action == 'expire':
- client.expiration = datetime.now();
+ client.expiration = datetime.now()
client.save()
message = "Expiration for %s set to %s." % \
(client_name, client.expiration.strftime("%Y-%m-%d %H:%M:%S"))
elif client_action == 'unexpire':
- client.expiration = None;
+ client.expiration = None
client.save()
message = "%s is now active." % client_name
else:
@@ -205,6 +224,7 @@ def client_manage(request):
{'clients': Client.objects.order_by('name').all(), 'message': message},
context_instance=RequestContext(request))
+
@timeview
def display_summary(request, timestamp=None):
"""
@@ -216,7 +236,12 @@ def display_summary(request, timestamp=None):
if not timestamp:
timestamp = datetime.now()
- collected_data = dict(clean=[],bad=[],modified=[],extra=[],stale=[],pings=[])
+ collected_data = dict(clean=[],
+ bad=[],
+ modified=[],
+ extra=[],
+ stale=[],
+ pings=[])
for node in recent_data:
if timestamp - node.timestamp > timedelta(hours=24):
collected_data['stale'].append(node)
@@ -238,42 +263,47 @@ def display_summary(request, timestamp=None):
# label, header_text, node_list
summary_data = []
- get_dict = lambda name, label: { 'name': name,
- 'nodes': collected_data[name],
- 'label': label }
+ get_dict = lambda name, label: {'name': name,
+ 'nodes': collected_data[name],
+ 'label': label}
if len(collected_data['clean']) > 0:
- summary_data.append( get_dict('clean', 'nodes are clean.') )
+ summary_data.append(get_dict('clean',
+ 'nodes are clean.'))
if len(collected_data['bad']) > 0:
- summary_data.append( get_dict('bad', 'nodes are bad.') )
+ summary_data.append(get_dict('bad',
+ 'nodes are bad.'))
if len(collected_data['modified']) > 0:
- summary_data.append( get_dict('modified', 'nodes were modified.') )
+ summary_data.append(get_dict('modified',
+ 'nodes were modified.'))
if len(collected_data['extra']) > 0:
- summary_data.append( get_dict('extra',
- 'nodes have extra configurations.') )
+ summary_data.append(get_dict('extra',
+ 'nodes have extra configurations.'))
if len(collected_data['stale']) > 0:
- summary_data.append( get_dict('stale',
- 'nodes did not run within the last 24 hours.') )
+ summary_data.append(get_dict('stale',
+ 'nodes did not run within the last 24 hours.'))
if len(collected_data['pings']) > 0:
- summary_data.append( get_dict('pings',
- 'are down.') )
+ summary_data.append(get_dict('pings',
+ 'are down.'))
return render_to_response('displays/summary.html',
{'summary_data': summary_data, 'node_count': node_count,
'timestamp': timestamp},
context_instance=RequestContext(request))
+
@timeview
def display_timing(request, timestamp=None):
mdict = dict()
inters = Interaction.objects.interaction_per_client(timestamp).select_related().all()
[mdict.__setitem__(inter, {'name': inter.client.name}) \
for inter in inters]
- for metric in Performance.objects.filter(interaction__in=mdict.keys()).all():
+ for metric in Performance.objects.filter(interaction__in=list(mdict.keys())).all():
for i in metric.interaction.all():
mdict[i][metric.metric] = metric.value
return render_to_response('displays/timing.html',
- {'metrics': mdict.values(), 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ {'metrics': list(mdict.values()),
+ 'timestamp': timestamp},
+ context_instance=RequestContext(request))
def render_history_view(request, template='clients/history.html', **kwargs):
@@ -303,7 +333,7 @@ def render_history_view(request, template='clients/history.html', **kwargs):
max_results = int(kwargs.get('page_limit', 25))
page = int(kwargs.get('page_number', 1))
- client=kwargs.get('client', None)
+ client = kwargs.get('client', None)
if not client and 'hostname' in kwargs:
client = get_object_or_404(Client, name=kwargs['hostname'])
if client:
@@ -333,8 +363,13 @@ def render_history_view(request, template='clients/history.html', **kwargs):
entry_list = []
if max_results > 0:
try:
- rec_start, rec_end = prepare_paginated_list(request, context, iquery, page, max_results)
- except PaginationError, page_error:
+ rec_start, rec_end = prepare_paginated_list(request,
+ context,
+ iquery,
+ page,
+ max_results)
+ except PaginationError:
+ page_error = sys.exc_info()[1]
if isinstance(page_error[0], HttpResponse):
return page_error[0]
return HttpResponseServerError(page_error)
@@ -345,20 +380,21 @@ def render_history_view(request, template='clients/history.html', **kwargs):
return render_to_response(template, context,
context_instance=RequestContext(request))
+
def prepare_paginated_list(request, context, paged_list, page=1, max_results=25):
"""
Prepare context and slice an object for pagination.
"""
if max_results < 1:
- raise PaginationError, "Max results less then 1"
+ raise PaginationError("Max results less then 1")
if paged_list == None:
- raise PaginationError, "Invalid object"
+ raise PaginationError("Invalid object")
try:
nitems = paged_list.count()
except TypeError:
nitems = len(paged_list)
-
+
rec_start = (page - 1) * int(max_results)
try:
total_pages = (nitems / int(max_results)) + 1
@@ -369,11 +405,11 @@ def prepare_paginated_list(request, context, paged_list, page=1, max_results=25)
try:
view, args, kwargs = resolve(request.META['PATH_INFO'])
kwargs['page_number'] = total_pages
- raise PaginationError, HttpResponseRedirect( reverse(view, kwargs=kwargs) )
+ raise PaginationError(HttpResponseRedirect(reverse(view,
+ kwards=kwargs)))
except (Resolver404, NoReverseMatch, ValueError):
raise "Accessing beyond last page. Unable to resolve redirect."
context['total_pages'] = total_pages
context['records_per_page'] = max_results
return (rec_start, rec_start + int(max_results))
-
diff --git a/src/lib/Server/Reports/settings.py b/src/lib/Server/Reports/settings.py
index 66da7a8b1..fff30d30a 100644
--- a/src/lib/Server/Reports/settings.py
+++ b/src/lib/Server/Reports/settings.py
@@ -1,8 +1,9 @@
import django
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
# Django settings for bcfg2 reports project.
-from ConfigParser import ConfigParser, NoSectionError, NoOptionError
-c = ConfigParser()
+c = ConfigParser.ConfigParser()
c.read(['/etc/bcfg2.conf', '/etc/bcfg2-web.conf'])
try:
@@ -18,31 +19,40 @@ else:
TEMPLATE_DEBUG = DEBUG
ADMINS = (
- ('Bcfg2', 'admin@email.address'),
+ ('Root', 'root'),
)
MANAGERS = ADMINS
-DATABASE_ENGINE = c.get('statistics', 'database_engine')
-# 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
+db_engine = c.get('statistics', 'database_engine')
+db_name = ''
if c.has_option('statistics', 'database_name'):
- DATABASE_NAME = c.get('statistics', 'database_name')
-else:
- DATABASE_NAME = ''
-# Or path to database file if using sqlite3.
-#<repository>/etc/brpt.sqlite is default path
-
-if DATABASE_ENGINE != 'sqlite3':
- DATABASE_USER = c.get('statistics', 'database_user')
- # Not used with sqlite3.
- DATABASE_PASSWORD = c.get('statistics', 'database_password')
- # Not used with sqlite3.
- DATABASE_HOST = c.get('statistics', 'database_host')
- # Set to empty string for localhost. Not used with sqlite3.
- DATABASE_PORT = c.get('statistics', 'database_port')
- # Set to empty string for default. Not used with sqlite3.
-if DATABASE_ENGINE == 'sqlite3' and DATABASE_NAME == '':
- DATABASE_NAME = "%s/etc/brpt.sqlite" % c.get('server', 'repository')
+ db_name = c.get('statistics', 'database_name')
+if db_engine == 'sqlite3' and db_name == '':
+ db_name = "%s/etc/brpt.sqlite" % c.get('server', 'repository')
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': "django.db.backends.%s" % db_engine,
+ 'NAME': db_name
+ }
+}
+
+if db_engine != 'sqlite3':
+ DATABASES['default']['USER'] = c.get('statistics', 'database_user')
+ DATABASES['default']['PASSWORD'] = c.get('statistics', 'database_password')
+ DATABASES['default']['HOST'] = c.get('statistics', 'database_host')
+ DATABASES['default']['PORT'] = c.get('statistics', 'database_port')
+
+if django.VERSION[0] == 1 and django.VERSION[1] < 2:
+ DATABASE_ENGINE = db_engine
+ DATABASE_NAME = DATABASES['default']['NAME']
+ if DATABASE_ENGINE != 'sqlite3':
+ DATABASE_USER = DATABASES['default']['USER']
+ DATABASE_PASSWORD = DATABASES['default']['PASSWORD']
+ DATABASE_HOST = DATABASES['default']['HOST']
+ DATABASE_PORT = DATABASES['default']['PORT']
+
# Local time zone for this installation. All choices can be found here:
# http://docs.djangoproject.com/en/dev/ref/settings/#time-zone
diff --git a/src/lib/Server/Reports/updatefix.py b/src/lib/Server/Reports/updatefix.py
index f8fca1f90..4d3c964f5 100644
--- a/src/lib/Server/Reports/updatefix.py
+++ b/src/lib/Server/Reports/updatefix.py
@@ -2,12 +2,13 @@ import Bcfg2.Server.Reports.settings
from django.db import connection
import django.core.management
+import logging
+import traceback
from Bcfg2.Server.Reports.reports.models import InternalDatabaseVersion, \
TYPE_BAD, TYPE_MODIFIED, TYPE_EXTRA
-
-import logging, traceback
logger = logging.getLogger('Bcfg2.Server.Reports.UpdateFix')
+
# all update function should go here
def _merge_database_table_entries():
cursor = connection.cursor()
@@ -21,7 +22,7 @@ def _merge_database_table_entries():
select name, kind from reports_extra
""")
# this fetch could be better done
- entries_map={}
+ entries_map = {}
for row in cursor.fetchall():
insert_cursor.execute("insert into reports_entries (name, kind) \
values (%s, %s)", (row[0], row[1]))
@@ -48,6 +49,7 @@ def _merge_database_table_entries():
insert_cursor.execute("insert into reports_entries_interactions \
(entry_id, interaction_id, reason_id, type) values (%s, %s, %s, %s)", (entry_id, row[3], row[2], row[4]))
+
def _interactions_constraint_or_idx():
'''sqlite doesn't support alter tables.. or constraints'''
cursor = connection.cursor()
@@ -55,27 +57,28 @@ def _interactions_constraint_or_idx():
cursor.execute('alter table reports_interaction add constraint reports_interaction_20100601 unique (client_id,timestamp)')
except:
cursor.execute('create unique index reports_interaction_20100601 on reports_interaction (client_id,timestamp)')
-
+
def _populate_interaction_entry_counts():
'''Populate up the type totals for the interaction table'''
cursor = connection.cursor()
- count_field = { TYPE_BAD: 'bad_entries',
- TYPE_MODIFIED: 'modified_entries',
- TYPE_EXTRA: 'extra_entries' }
+ count_field = {TYPE_BAD: 'bad_entries',
+ TYPE_MODIFIED: 'modified_entries',
+ TYPE_EXTRA: 'extra_entries'}
- for type in count_field.keys():
- cursor.execute("select count(type), interaction_id "+
+ for type in list(count_field.keys()):
+ cursor.execute("select count(type), interaction_id " +
"from reports_entries_interactions where type = %s group by interaction_id" % type)
updates = []
for row in cursor.fetchall():
updates.append(row)
try:
cursor.executemany("update reports_interaction set " + count_field[type] + "=%s where id = %s", updates)
- except Exception, e:
- print e
+ except Exception:
+ e = sys.exc_info()[1]
+ print(e)
cursor.close()
-
+
# be sure to test your upgrade query before reflecting the change in the models
# the list of function and sql command to do should go here
@@ -104,6 +107,7 @@ _fixes = [_merge_database_table_entries,
# this will calculate the last possible version of the database
lastversion = len(_fixes)
+
def rollupdate(current_version):
""" function responsible to coordinates all the updates
need current_version as integer
@@ -119,11 +123,12 @@ def rollupdate(current_version):
except:
logger.error("Failed to perform db update %s" % (_fixes[i]), exc_info=1)
# since array start at 0 but version start at 1 we add 1 to the normal count
- ret = InternalDatabaseVersion.objects.create(version=i+1)
+ ret = InternalDatabaseVersion.objects.create(version=i + 1)
return ret
else:
return None
+
def dosync():
"""Function to do the syncronisation for the models"""
# try to detect if it's a fresh new database
@@ -164,7 +169,7 @@ def dosync():
def update_database():
''' methode to search where we are in the revision of the database models and update them '''
- try :
+ try:
logger.debug("Running upgrade of models to the new one")
dosync()
know_version = InternalDatabaseVersion.objects.order_by('-version')
diff --git a/src/lib/Server/Reports/utils.py b/src/lib/Server/Reports/utils.py
index b74f09e74..e0b6ead59 100755
--- a/src/lib/Server/Reports/utils.py
+++ b/src/lib/Server/Reports/utils.py
@@ -1,11 +1,11 @@
"""Helper functions for reports"""
-from Bcfg2.Server.Reports.reports.models import TYPE_CHOICES
from django.conf.urls.defaults import *
import re
"""List of filters provided by filteredUrls"""
filter_list = ('server', 'state')
+
class BatchFetch(object):
"""Fetch Django objects in smaller batches to save memory"""
@@ -21,6 +21,10 @@ class BatchFetch(object):
return self
def next(self):
+ """Provide compatibility with python < 3.0"""
+ return self.__next__()
+
+ def __next__(self):
"""Return the next object from our array and fetch from the
database when needed"""
if self.block_count + self.count - self.step == self.max:
@@ -34,11 +38,12 @@ class BatchFetch(object):
self.count += 1
return self.data[self.count - 1]
+
def generateUrls(fn):
"""
Parse url tuples and send to functions.
- Decorator for url generators. Handles url tuple parsing
+ Decorator for url generators. Handles url tuple parsing
before the actual function is called.
"""
def url_gen(*urls):
@@ -51,13 +56,14 @@ def generateUrls(fn):
return results
return url_gen
+
@generateUrls
def paginatedUrls(pattern, view, kwargs=None, name=None):
"""
Takes a group of url tuples and adds paginated urls.
- Extends a url tuple to include paginated urls. Currently doesn't handle url() compiled
- patterns.
+ Extends a url tuple to include paginated urls.
+ Currently doesn't handle url() compiled patterns.
"""
results = [(pattern, view, kwargs, name)]
@@ -67,13 +73,15 @@ def paginatedUrls(pattern, view, kwargs=None, name=None):
tail = mtail.group(1)
pattern = pattern[:len(pattern) - len(tail)]
results += [(pattern + "/(?P<page_number>\d+)" + tail, view, kwargs)]
- results += [(pattern + "/(?P<page_number>\d+)\|(?P<page_limit>\d+)" + tail, view, kwargs)]
+ results += [(pattern + "/(?P<page_number>\d+)\|(?P<page_limit>\d+)" +
+ tail, view, kwargs)]
if not kwargs:
kwargs = dict()
kwargs['page_limit'] = 0
results += [(pattern + "/?\|(?P<page_limit>all)" + tail, view, kwargs)]
return results
+
@generateUrls
def filteredUrls(pattern, view, kwargs=None, name=None):
"""
@@ -93,7 +101,8 @@ def filteredUrls(pattern, view, kwargs=None, name=None):
'/server/(?P<server>[\w\-\.]+)/(?P<state>[A-Za-z]+)'):
results += [(pattern + filter + tail, view, kwargs)]
return results
-
+
+
@generateUrls
def timeviewUrls(pattern, view, kwargs=None, name=None):
"""
@@ -113,4 +122,3 @@ def timeviewUrls(pattern, view, kwargs=None, name=None):
'/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'):
results += [(pattern + filter + tail, view, kwargs)]
return results
-
diff --git a/src/lib/Server/Snapshots/__init__.py b/src/lib/Server/Snapshots/__init__.py
index 6018377cb..7c901adb2 100644
--- a/src/lib/Server/Snapshots/__init__.py
+++ b/src/lib/Server/Snapshots/__init__.py
@@ -2,7 +2,8 @@ __all__ = ['models', 'db_from_config', 'setup_session']
import sqlalchemy
import sqlalchemy.orm
-import ConfigParser
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import ConfigParser
def db_from_config(cfile):
@@ -19,7 +20,7 @@ def db_from_config(cfile):
db = cp.get('snapshots', 'database')
return '%s://%s:%s@%s/%s' % (driver, user, password, host, db)
else:
- raise Exception, "unsupported db driver %s" % driver
+ raise Exception("unsupported db driver %s" % driver)
def setup_session(cfile, debug=False):
diff --git a/src/lib/Server/Snapshots/model.py b/src/lib/Server/Snapshots/model.py
index cbb14be79..2aa35f1ec 100644
--- a/src/lib/Server/Snapshots/model.py
+++ b/src/lib/Server/Snapshots/model.py
@@ -1,3 +1,4 @@
+import sys
from sqlalchemy import Table, Column, Integer, Unicode, ForeignKey, Boolean, \
DateTime, UnicodeText, desc
import datetime
@@ -6,6 +7,13 @@ from sqlalchemy.orm import relation, backref
from sqlalchemy.ext.declarative import declarative_base
+def u_str(string):
+ if sys.hexversion >= 0x03000000:
+ return string
+ else:
+ return unicode(string)
+
+
class Uniquer(object):
force_rt = True
@@ -33,12 +41,20 @@ class Administrator(Uniquer, Base):
email = Column(Unicode(64))
admin_client = Table('admin_client', Base.metadata,
- Column('admin_id', Integer, ForeignKey('administrator.id')),
- Column('client_id', Integer, ForeignKey('client.id')))
+ Column('admin_id',
+ Integer,
+ ForeignKey('administrator.id')),
+ Column('client_id',
+ Integer,
+ ForeignKey('client.id')))
admin_group = Table('admin_group', Base.metadata,
- Column('admin_id', Integer, ForeignKey('administrator.id')),
- Column('group_id', Integer, ForeignKey('group.id')))
+ Column('admin_id',
+ Integer,
+ ForeignKey('administrator.id')),
+ Column('group_id',
+ Integer,
+ ForeignKey('group.id')))
class Client(Uniquer, Base):
@@ -68,12 +84,20 @@ class ConnectorKeyVal(Uniquer, Base):
value = Column(UnicodeText)
meta_group = Table('meta_group', Base.metadata,
- Column('metadata_id', Integer, ForeignKey('metadata.id')),
- Column('group_id', Integer, ForeignKey('group.id')))
+ Column('metadata_id',
+ Integer,
+ ForeignKey('metadata.id')),
+ Column('group_id',
+ Integer,
+ ForeignKey('group.id')))
meta_conn = Table('meta_conn', Base.metadata,
- Column('metadata_id', Integer, ForeignKey('metadata.id')),
- Column('connkeyval_id', Integer, ForeignKey('connkeyval.id')))
+ Column('metadata_id',
+ Integer,
+ ForeignKey('metadata.id')),
+ Column('connkeyval_id',
+ Integer,
+ ForeignKey('connkeyval.id')))
class Metadata(Base):
@@ -87,21 +111,21 @@ class Metadata(Base):
@classmethod
def from_metadata(cls, mysession, mymetadata):
- client = Client.by_value(mysession, name=unicode(mymetadata.hostname))
+ client = Client.by_value(mysession, name=u_str(mymetadata.hostname))
m = cls(client=client)
for group in mymetadata.groups:
- m.groups.append(Group.by_value(mysession, name=unicode(group)))
+ m.groups.append(Group.by_value(mysession, name=u_str(group)))
for connector in mymetadata.connectors:
data = getattr(mymetadata, connector)
if not isinstance(data, dict):
continue
- for key, value in data.iteritems():
+ for key, value in list(data.items()):
if not isinstance(value, str):
continue
m.keyvals.append(ConnectorKeyVal.by_value(mysession,
- connector=unicode(connector),
- key=unicode(key),
- value=unicode(value)))
+ connector=u_str(connector),
+ key=u_str(key),
+ value=u_str(value)))
return m
@@ -143,8 +167,12 @@ class PackageCorrespondence(Base, CorrespondenceType):
correct = Column(Boolean)
package_snap = Table('package_snap', Base.metadata,
- Column('ppair_id', Integer, ForeignKey('package_pair.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('ppair_id',
+ Integer,
+ ForeignKey('package_pair.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
class Service(Base, Uniquer):
@@ -167,8 +195,12 @@ class ServiceCorrespondence(Base, CorrespondenceType):
correct = Column(Boolean)
service_snap = Table('service_snap', Base.metadata,
- Column('spair_id', Integer, ForeignKey('service_pair.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('spair_id',
+ Integer,
+ ForeignKey('service_pair.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
class File(Base, Uniquer):
@@ -194,20 +226,36 @@ class FileCorrespondence(Base, CorrespondenceType):
correct = Column(Boolean)
file_snap = Table('file_snap', Base.metadata,
- Column('fpair_id', Integer, ForeignKey('file_pair.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('fpair_id',
+ Integer,
+ ForeignKey('file_pair.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
extra_pkg_snap = Table('extra_pkg_snap', Base.metadata,
- Column('package_id', Integer, ForeignKey('package.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('package_id',
+ Integer,
+ ForeignKey('package.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
extra_file_snap = Table('extra_file_snap', Base.metadata,
- Column('file_id', Integer, ForeignKey('file.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('file_id',
+ Integer,
+ ForeignKey('file.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
extra_service_snap = Table('extra_service_snap', Base.metadata,
- Column('service_id', Integer, ForeignKey('service.id')),
- Column('snapshot_id', Integer, ForeignKey('snapshot.id')))
+ Column('service_id',
+ Integer,
+ ForeignKey('service.id')),
+ Column('snapshot_id',
+ Integer,
+ ForeignKey('snapshot.id')))
class Action(Base):
@@ -228,7 +276,7 @@ class Snapshot(Base):
correct = Column(Boolean)
revision = Column(Unicode(36))
metadata_id = Column(Integer, ForeignKey('metadata.id'))
- client_metadata = relation(Metadata, primaryjoin=metadata_id==Metadata.id)
+ client_metadata = relation(Metadata, primaryjoin=metadata_id == Metadata.id)
timestamp = Column(DateTime, default=datetime.datetime.now)
client_id = Column(Integer, ForeignKey('client.id'))
client = relation(Client, backref=backref('snapshots'))
@@ -256,23 +304,25 @@ class Snapshot(Base):
(cls.e_dispatch, extra)]:
for key in dispatch:
dest, ecls = dispatch[key]
- for edata in data[key].values():
+ for edata in list(data[key].values()):
getattr(snap, dest).append(ecls.from_record(session, edata))
return snap
@classmethod
def by_client(cls, session, clientname):
- return session.query(cls).join(cls.client_metadata, Metadata.client).filter(Client.name==clientname)
+ return session.query(cls).join(cls.client_metadata,
+ Metadata.client).filter(Client.name == clientname)
@classmethod
def get_current(cls, session, clientname):
- return session.query(Snapshot).join(Snapshot.client_metadata, Metadata.client).filter(Client.name==clientname).order_by(desc(Snapshot.timestamp)).first()
+ return session.query(Snapshot).join(Snapshot.client_metadata,
+ Metadata.client).filter(Client.name == clientname).order_by(desc(Snapshot.timestamp)).first()
@classmethod
def get_by_date(cls, session, clientname, timestamp):
return session.query(Snapshot)\
.join(Snapshot.client_metadata, Metadata.client)\
.filter(Snapshot.timestamp < timestamp)\
- .filter(Client.name==clientname)\
+ .filter(Client.name == clientname)\
.order_by(desc(Snapshot.timestamp))\
.first()
diff --git a/src/lib/Statistics.py b/src/lib/Statistics.py
index b2240db98..a0cb8f39b 100644
--- a/src/lib/Statistics.py
+++ b/src/lib/Statistics.py
@@ -29,4 +29,4 @@ class Statistics(object):
self.data[name].add_value(value)
def display(self):
- return dict([value.get_value() for value in self.data.values()])
+ return dict([value.get_value() for value in list(self.data.values())])
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 9bc50fe65..7f7d8f5c6 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -3,18 +3,20 @@
"""Bcfg2 Client"""
__revision__ = '$Revision$'
+import fcntl
import logging
import os
import signal
+import stat
import sys
import tempfile
import time
-import xmlrpclib
-import fcntl
import Bcfg2.Options
import Bcfg2.Client.XML
import Bcfg2.Client.Frame
import Bcfg2.Client.Tools
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import xmlrpclib
import Bcfg2.Proxy
import Bcfg2.Logger
@@ -106,7 +108,12 @@ class Client:
raise SystemExit(0)
if self.setup['remove'] and 'services' in self.setup['remove']:
self.logger.error("Service removal is nonsensical, disable services to get former behavior")
- if self.setup['remove'] not in [False, 'all', 'services', 'packages']:
+ if self.setup['remove'] not in [False,
+ 'all',
+ 'Services',
+ 'Packages',
+ 'services',
+ 'packages']:
self.logger.error("Got unknown argument %s for -r" % (self.setup['remove']))
if (self.setup["file"] != False) and (self.setup["cache"] != False):
print("cannot use -f and -c together")
@@ -130,7 +137,9 @@ class Client:
script.write(probe.text)
script.close()
os.close(scripthandle)
- os.chmod(script.name, 0755)
+ os.chmod(script.name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
+ stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
+ stat.S_IWUSR) # 0755
ret.text = os.popen(script.name).read().strip()
self.logger.info("Probe %s has result:\n%s" % (name, ret.text))
finally:
@@ -183,7 +192,8 @@ class Client:
try:
probe_data = proxy.GetProbes()
- except xmlrpclib.Fault, flt:
+ except xmlrpclib.Fault:
+ flt = sys.exc_info()[1]
self.logger.error("Failed to download probes from bcfg2")
self.logger.error(flt.faultString)
raise SystemExit(1)
@@ -192,7 +202,8 @@ class Client:
try:
probes = Bcfg2.Client.XML.XML(probe_data)
- except Bcfg2.Client.XML.ParseError, syntax_error:
+ except Bcfg2.Client.XML.ParseError:
+ syntax_error = sys.exc_info()[1]
self.fatal_error(
"Server returned invalid probe requests: %s" %
(syntax_error))
@@ -223,7 +234,8 @@ class Client:
self.setup['decision'])
self.logger.info("Got decision list from server:")
self.logger.info(self.setup['decision_list'])
- except xmlrpclib.Fault, f:
+ except xmlrpclib.Fault:
+ f = sys.exc_info()[1]
if f.faultCode == 1:
print("GetDecisionList method not supported by server")
else:
@@ -249,7 +261,8 @@ class Client:
try:
self.config = Bcfg2.Client.XML.XML(rawconfig)
- except Bcfg2.Client.XML.ParseError, syntax_error:
+ except Bcfg2.Client.XML.ParseError:
+ syntax_error = sys.exc_info()[1]
self.fatal_error("The configuration could not be parsed: %s" %
(syntax_error))
return(1)
diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin
index 2c9a43859..36be6ab14 100755
--- a/src/sbin/bcfg2-admin
+++ b/src/sbin/bcfg2-admin
@@ -2,11 +2,12 @@
"""bcfg2-admin is a script that helps to administrate a Bcfg2 deployment."""
from optparse import OptionParser
-from StringIO import StringIO
import logging
import Bcfg2.Server.Core
import Bcfg2.Logger
import Bcfg2.Options
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import StringIO
log = logging.getLogger('bcfg2-admin')
@@ -56,14 +57,15 @@ def main():
else:
# Print short help for all modes
parser.print_help()
- print create_description()
+ print(create_description())
raise SystemExit(0)
if args[0] in get_modes():
modname = args[0].capitalize()
try:
mode_cls = mode_import(modname)
- except ImportError, e:
+ except ImportError:
+ e = sys.exc_info()[1]
log.error("Failed to load admin mode %s: %s" % (modname, e))
raise SystemExit(1)
mode = mode_cls(options.configfile)
@@ -73,7 +75,7 @@ def main():
else:
log.error("Unknown mode %s" % args[0])
parser.print_help()
- print create_description()
+ print(create_description())
raise SystemExit(1)
if __name__ == '__main__':
diff --git a/src/sbin/bcfg2-build-reports b/src/sbin/bcfg2-build-reports
index 231f52105..7122fb300 100755
--- a/src/sbin/bcfg2-build-reports
+++ b/src/sbin/bcfg2-build-reports
@@ -13,8 +13,9 @@ import os
import socket
import sys
from time import asctime, strptime
-from ConfigParser import ConfigParser, NoSectionError, NoOptionError
from lxml.etree import XML, XSLT, parse, Element, ElementTree, SubElement, tostring, XMLSyntaxError
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import ConfigParser
def generatereport(rspec, nrpt):
"""
@@ -42,9 +43,9 @@ def generatereport(rspec, nrpt):
# This line actually sorts from most recent to oldest.
statisticslist.sort(lambda y, x: cmp(strptime(x.get("time")), strptime(y.get("time"))))
stats = statisticslist[0]
-
+
[node.remove(item) for item in node.findall('Statistics')]
-
+
# Add a good tag if node is good and we wnat to report such.
if reportgood == 'Y' and stats.get('state') == 'clean':
SubElement(stats,"Good")
@@ -52,7 +53,7 @@ def generatereport(rspec, nrpt):
[stats.remove(item) for item in stats.findall("Bad") + stats.findall("Modified") if \
item.getchildren() == []]
[stats.remove(item) for item in stats.findall("Modified") if reportmodified == 'N']
-
+
# Test for staleness -if stale add Stale tag.
if stats.get("time").find(current_date) == -1:
SubElement(stats,"Stale")
@@ -64,7 +65,7 @@ def mail(mailbody, confi):
try:
mailer = confi.get('statistics', 'sendmailpath')
- except (NoSectionError, NoOptionError):
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
mailer = "/usr/sbin/sendmail"
# Open a pipe to the mail program and
# write the data to the pipe.
@@ -72,7 +73,7 @@ def mail(mailbody, confi):
pipe.write(mailbody)
exitcode = pipe.close()
if exitcode:
- print "Exit code: %s" % exitcode
+ print("Exit code: %s" % exitcode)
def rss(reportxml, delivery, report):
"""rss appends a new report to the specified rss file
@@ -98,7 +99,7 @@ def rss(reportxml, delivery, report):
chantitle = SubElement(channel, "title")
chantitle.text = report.attrib['name']
chanlink = SubElement(channel, "link")
-
+
# This can later link to WWW report if one gets published
# simultaneously?
chanlink.text = "http://www.mcs.anl.gov/cobalt/bcfg2"
@@ -119,7 +120,7 @@ def www(reportxml, delivery):
"""www outputs report to."""
# This can later link to WWW report if one gets published
- # simultaneously?
+ # simultaneously?
for destination in delivery.findall('Destination'):
fil = open(destination.attrib['address'], 'w')
@@ -138,15 +139,15 @@ def pretty_print(element, level=0):
"""Produce a pretty-printed text representation of element."""
if element.text:
fmt = "%s<%%s %%s>%%s</%%s>" % (level*" ")
- data = (element.tag, (" ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()])),
+ data = (element.tag, (" ".join(["%s='%s'" % keyval for keyval in list(element.attrib.items())])),
element.text, element.tag)
if element._children:
fmt = "%s<%%s %%s>\n" % (level*" ",) + (len(element._children) * "%s") + "%s</%%s>\n" % (level*" ")
- data = (element.tag, ) + (" ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()]),)
+ data = (element.tag, ) + (" ".join(["%s='%s'" % keyval for keyval in list(element.attrib.items())]),)
data += tuple([pretty_print(entry, level+2) for entry in element._children]) + (element.tag, )
else:
fmt = "%s<%%s %%s/>\n" % (level * " ")
- data = (element.tag, " ".join(["%s='%s'" % keyval for keyval in element.attrib.iteritems()]))
+ data = (element.tag, " ".join(["%s='%s'" % keyval for keyval in list(element.attrib.items())]))
return fmt % data
@@ -157,14 +158,14 @@ if __name__ == '__main__':
cfpath = sys.argv[sys.argv.index('-C') + 1]
else:
cfpath = '/etc/bcfg2.conf'
- c = ConfigParser()
+ c = ConfigParser.ConfigParser()
c.read([cfpath])
configpath = "%s/etc/report-configuration.xml" % c.get('server', 'repository')
statpath = "%s/etc/statistics.xml" % c.get('server', 'repository')
clientsdatapath = "%s/Metadata/clients.xml" % c.get('server', 'repository')
try:
prefix = c.get('server', 'prefix')
- except (NoSectionError, NoOptionError):
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
prefix = '/usr'
transformpath = "/%s/share/bcfg2/xsl-transforms/" % (prefix)
@@ -172,13 +173,14 @@ if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "C:hAc:Ns:", ["help", "all", "config=","no-ping", "stats="])
- except getopt.GetoptError, mesg:
+ except getopt.GetoptError:
+ mesg = sys.exc_info()[1]
# Print help information and exit:
- print "%s\nUsage:\nbcfg2-build-reports [-h][-A (include ALL clients)] [-c <configuration-file>] [-s <statistics-file>][-N (do not ping clients)]" % (mesg)
- raise SystemExit, 2
+ print("%s\nUsage:\nbcfg2-build-reports [-h][-A (include ALL clients)] [-c <configuration-file>] [-s <statistics-file>][-N (do not ping clients)]" % (mesg))
+ raise SystemExit(2)
for o, a in opts:
if o in ("-h", "--help"):
- print "Usage:\nbcfg2-build-reports [-h] [-c <configuration-file>] [-s <statistics-file>]"
+ print("Usage:\nbcfg2-build-reports [-h] [-c <configuration-file>] [-s <statistics-file>]")
raise SystemExit
if o in ("-A", "--all"):
all=True
@@ -205,17 +207,17 @@ if __name__ == '__main__':
statsdata = XML(open(statpath).read())
except (IOError, XMLSyntaxError):
print("bcfg2-build-reports: Failed to parse %s"%(statpath))
- raise SystemExit, 1
+ raise SystemExit(1)
try:
configdata = XML(open(configpath).read())
except (IOError, XMLSyntaxError):
print("bcfg2-build-reports: Failed to parse %s"%(configpath))
- raise SystemExit, 1
+ raise SystemExit(1)
try:
clientsdata = XML(open(clientsdatapath).read())
except (IOError, XMLSyntaxError):
print("bcfg2-build-reports: Failed to parse %s"%(clientsdatapath))
- raise SystemExit, 1
+ raise SystemExit(1)
# Merge data from three sources.
nodereport = Element("Report", attrib={"time" : asctime()})
@@ -229,7 +231,7 @@ if __name__ == '__main__':
for statel in nod.findall("Statistics"):
nodel.append(statel)
nodereport.append(nodel)
-
+
if all:
for nod in statsdata.findall("Node"):
for client in clientsdata.findall("Client"):
@@ -242,8 +244,8 @@ if __name__ == '__main__':
for statel in nod.findall("Statistics"):
nodel.append(statel)
nodereport.append(nodel)
-
-
+
+
for reprt in configdata.findall('Report'):
nodereport.set("name", reprt.get("name", default="BCFG Report"))
@@ -254,7 +256,7 @@ if __name__ == '__main__':
for deliv in reprt.findall('Delivery'):
# Is a deepcopy of procnodereport necessary?
-
+
delivtype = deliv.get('type', default='nodes-digest')
deliverymechanism = deliv.get('mechanism', default='www')
@@ -269,14 +271,14 @@ if __name__ == '__main__':
except:
print("bcfg2-build-reports: Invalid report type or delivery mechanism.\n Can't find: "\
+ transformpath + transform)
- raise SystemExit, 1
+ raise SystemExit(1)
try: # Try to parse stylesheet.
stylesheet = XSLT(parse(transformpath + transform))
except:
print("bcfg2-build-reports: invalid XSLT transform file.")
- raise SystemExit, 1
-
+ raise SystemExit(1)
+
if deliverymechanism == 'mail':
if delivtype == 'nodes-individual':
reportdata = copy.deepcopy(procnodereport)
@@ -285,7 +287,7 @@ if __name__ == '__main__':
reportdata.append(noden)
result = stylesheet.apply(ElementTree(reportdata))
outputstring = stylesheet.tostring(result)
-
+
if not outputstring == None:
toastring = ''
for desti in deliv.findall("Destination"):
@@ -295,13 +297,13 @@ if __name__ == '__main__':
outputstring = "To: %s\nFrom: root@%s\n%s"% \
(toastring, socket.getfqdn(), outputstring)
mail(outputstring, c) #call function to send
-
+
else:
reportdata = copy.deepcopy(procnodereport)
result = stylesheet.apply(ElementTree(reportdata))
outputstring = stylesheet.tostring(result)
-
+
if not outputstring == None:
toastring = ''
for desti in deliv.findall("Destination"):
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index a6d236bc8..161fee441 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -27,6 +27,40 @@ import Bcfg2.Server.Plugins.Metadata
import Bcfg2.Server.Plugin
logger = logging.getLogger('bcfg2-info')
+USAGE = """Commands:
+build <hostname> <filename> - Build config for hostname, writing to filename
+builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname
+buildall <directory> - Build configs for all clients in directory
+buildfile <filename> <hostname> - Build config file for hostname (not written to disk)
+bundles - Print out group/bundle information
+clients - Print out client/profile information
+config - Print out the configuration of the Bcfg2 server
+debug - Shell out to native python interpreter
+event_debug - Display filesystem events as they are processed
+generators - List current versions of generators
+groups - List groups
+help - Print this list of available commands
+mappings <type*> <name*> - Print generator mappings for optional type and name
+profile <command> <args> - Profile a single bcfg2-info command
+quit - Exit the bcfg2-info command line
+showentries <hostname> <type> - Show abstract configuration entries for a given host
+showclient <client1> <client2> - Show metadata for given hosts
+update - Process pending file events
+version - Print version of this tool"""
+
+BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir>
+
+Generates a config for client <hostname> and writes the
+individual configuration files out separately in a tree
+under <output dir>. The <output dir> directory must be
+rooted under /tmp unless the -f argument is provided, in
+which case it can be located anywhere.
+
+NOTE: Currently only handles file entries and writes
+all content with the default owner and permissions. These
+could be much more permissive than would be created by the
+Bcfg2 client itself."""
+
class mockLog(object):
def error(self, *args, **kwargs):
@@ -75,7 +109,8 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
encoding)
if event_debug:
self.fam.debug = True
- except Bcfg2.Server.Core.CoreInitError, msg:
+ except Bcfg2.Server.Core.CoreInitError:
+ msg = sys.exc_info()[1]
print("Core load failed because %s" % msg)
raise SystemExit(1)
self.prompt = '> '
@@ -89,7 +124,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
try:
self.cmdloop('Welcome to bcfg2-info\n'
'Type "help" for more information')
- except SystemExit, val:
+ except SystemExit:
raise
except Bcfg2.Server.Plugin.PluginExecutionError:
continue
@@ -106,7 +141,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
try:
opts, _ = getopt.getopt(args.split(), 'nf:')
except:
- print "Usage: debug [-n] [-f <command list>]"
+ print("Usage: debug [-n] [-f <command list>]")
return
self.cont = False
scriptmode = False
@@ -136,7 +171,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
Exit program.
Usage: [quit|exit]
"""
- for plugin in self.plugins.values():
+ for plugin in list(self.plugins.values()):
plugin.shutdown()
os._exit(0)
@@ -145,27 +180,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
def do_help(self, _):
"""Print out usage info."""
- print 'Commands:'
- print 'build <hostname> <filename> - Build config for hostname, writing to filename'
- print 'builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname'
- print 'buildall <directory> - Build configs for all clients in directory'
- print 'buildfile <filename> <hostname> - Build config file for hostname (not written to disk)'
- print 'bundles - Print out group/bundle information'
- print 'clients - Print out client/profile information'
- print 'config - Print out the configuration of the Bcfg2 server'
- print 'debug - Shell out to native python interpreter'
- print 'event_debug - Display filesystem events as they are processed'
- print 'generators - List current versions of generators'
- print 'groups - List groups'
- print 'help - Print this list of available commands'
- print 'mappings <type*> <name*> - Print generator mappings for optional type and name'
- print 'profile <command> <args> - Profile a single bcfg2-info command'
- print 'quit - Exit the bcfg2-info command line'
- print 'showentries <hostname> <type> - Show abstract configuration entries for a given host'
- print 'showclient <client1> <client2> - Show metadata for given hosts'
- print 'update - Process pending file events'
- print 'version - Print version of this tool'
-
+ print(USAGE)
def do_update(self, _):
"""Process pending filesystem events."""
@@ -198,18 +213,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
def help_builddir(self):
"""Display help for builddir command."""
- print('Usage: builddir [-f] <hostname> <output dir>')
- print('')
- print('Generates a config for client <hostname> and writes the')
- print('individual configuration files out separately in a tree')
- print('under <output dir>. The <output dir> directory must be')
- print('rooted under /tmp unless the -f argument is provided, in')
- print('which case it can be located anywhere.')
- print('')
- print('NOTE: Currently only handles file entries and writes')
- print('all content with the default owner and permissions. These')
- print('could be much more permissive than would be created by the')
- print('Bcfg2 client itself.')
+ print(BUILDDIR_USAGE)
def do_builddir(self, args):
"""Build client configuration as separate files within a dir."""
@@ -238,7 +242,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
p = Bcfg2.Client.Tools.POSIX.POSIX(log, setup, client_config)
states = dict()
p.Inventory(states)
- p.Install(states.keys(), states)
+ p.Install(list(states.keys()), states)
else:
print('Error: Incorrect number of parameters.')
self.help_builddir()
@@ -262,7 +266,7 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
try:
metadata = self.build_metadata(client)
self.Bind(entry, metadata)
- print(lxml.etree.tostring(entry, encoding="UTF-8",
+ print(lxml.etree.tostring(entry, encoding="UTF-8",
xml_declaration=True))
except:
print("Failed to build entry %s for host %s" % (fname, client))
@@ -371,22 +375,22 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
except:
print("Client %s not defined" % client)
continue
- print "Hostname:\t", client_meta.hostname
- print "Profile:\t", client_meta.profile
- print "Groups:\t\t", list(client_meta.groups)[0]
+ print("Hostname:\t", client_meta.hostname)
+ print("Profile:\t", client_meta.profile)
+ print("Groups:\t\t", list(client_meta.groups)[0])
for grp in list(client_meta.groups)[1:]:
- print '\t\t%s' % grp
+ print('\t\t%s' % grp)
if client_meta.bundles:
- print "Bundles:\t", list(client_meta.bundles)[0]
+ print("Bundles:\t", list(client_meta.bundles)[0])
for bnd in list(client_meta.bundles)[1:]:
- print '\t\t%s' % bnd
+ print('\t\t%s' % bnd)
if client_meta.connectors:
- print "Connector data"
- print "=" * 80
+ print("Connector data")
+ print("=" * 80)
for conn in client_meta.connectors:
if getattr(client_meta, conn):
- print "%s:\t" % (conn), getattr(client_meta, conn)
- print "=" * 80
+ print("%s:\t" % (conn), getattr(client_meta, conn))
+ print("=" * 80)
def do_mappings(self, args):
"""Print out mapping info."""
@@ -402,11 +406,11 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
interested = [(etype, [args.split()[1]])
for etype in etypes]
else:
- interested = [(etype, generator.Entries[etype])
- for etype in etypes
+ interested = [(etype, generator.Entries[etype])
+ for etype in etypes
if etype in generator.Entries]
for etype, names in interested:
- for name in [name for name in names if name in
+ for name in [name for name in names if name in
generator.Entries.get(etype, {})]:
data.append((generator.name, etype, name))
printTabular(data)
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
new file mode 100755
index 000000000..6bc34433e
--- /dev/null
+++ b/src/sbin/bcfg2-lint
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+
+"""This tool examines your Bcfg2 specifications for errors."""
+__revision__ = '$Revision$'
+
+import sys
+import inspect
+import logging
+import Bcfg2.Logger
+import Bcfg2.Options
+import Bcfg2.Server.Core
+import Bcfg2.Server.Lint
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import ConfigParser
+
+logger = logging.getLogger('bcfg2-lint')
+
+class Parser(ConfigParser.ConfigParser):
+ def get(self, section, option, default):
+ """ Override ConfigParser.get: If the request option is not in
+ the config file then return the value of default rather than
+ raise an exception. We still raise exceptions on missing
+ sections.
+ """
+ try:
+ return ConfigParser.ConfigParser.get(self, section, option)
+ except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+ return default
+
+def run_serverless_plugins(plugins, config=None, setup=None, errorhandler=None):
+ logger.debug("Running serverless plugins")
+ for plugin_name, plugin in list(plugins.items()):
+ run_plugin(plugin, plugin_name, errorhandler=errorhandler,
+ setup=setup, config=config, files=files)
+
+def run_server_plugins(plugins, config=None, setup=None, errorhandler=None):
+ core = load_server(setup)
+ logger.debug("Running server plugins")
+ for plugin_name, plugin in list(plugins.items()):
+ run_plugin(plugin, plugin_name, args=[core], errorhandler=errorhandler,
+ setup=setup, config=config, files=files)
+
+def run_plugin(plugin, plugin_name, setup=None, errorhandler=None,
+ args=None, config=None, files=None):
+ logger.debug(" Running %s" % plugin_name)
+ if args is None:
+ args = []
+
+ if errorhandler is None:
+ errorhandler = get_errorhandler(config)
+
+ if config is not None and config.has_section(plugin_name):
+ args.append(dict(config.items(plugin_name), **setup))
+ else:
+ args.append(setup)
+
+ # older versions of python do not support mixing *-magic and
+ # non-*-magic (e.g., "plugin(*args, files=files)", so we do this
+ # all with *-magic
+ kwargs = dict(files=files, errorhandler=errorhandler)
+
+ return plugin(*args, **kwargs).Run()
+
+def get_errorhandler(config):
+ """ get a Bcfg2.Server.Lint.ErrorHandler object """
+ if config.has_section("errors"):
+ conf = dict(config.items("errors"))
+ else:
+ conf = None
+ return Bcfg2.Server.Lint.ErrorHandler(config=conf)
+
+def load_server(setup):
+ """ load server """
+ core = Bcfg2.Server.Core.Core(setup['repo'], setup['plugins'],
+ setup['password'], setup['encoding'])
+ if setup['event debug']:
+ core.fam.debug = True
+ core.fam.handle_events_in_interval(4)
+ return core
+
+if __name__ == '__main__':
+ optinfo = {
+ 'configfile': Bcfg2.Options.CFILE,
+ 'help': Bcfg2.Options.HELP,
+ 'verbose': Bcfg2.Options.VERBOSE,
+ }
+ optinfo.update({
+ 'event debug': Bcfg2.Options.DEBUG,
+ 'encoding': Bcfg2.Options.ENCODING,
+ # Server options
+ 'repo': Bcfg2.Options.SERVER_REPOSITORY,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'mconnect': Bcfg2.Options.SERVER_MCONNECT,
+ 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR,
+ 'location': Bcfg2.Options.SERVER_LOCATION,
+ 'static': Bcfg2.Options.SERVER_STATIC,
+ 'key': Bcfg2.Options.SERVER_KEY,
+ 'cert': Bcfg2.Options.SERVER_CERT,
+ 'ca': Bcfg2.Options.SERVER_CA,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'protocol': Bcfg2.Options.SERVER_PROTOCOL,
+ # More options
+ 'logging': Bcfg2.Options.LOGGING_FILE_PATH,
+ 'stdin': Bcfg2.Options.FILES_ON_STDIN,
+ 'schema': Bcfg2.Options.SCHEMA_PATH,
+ 'config': Bcfg2.Options.Option('Specify bcfg2-lint configuration file',
+ '/etc/bcfg2-lint.conf',
+ cmd='--lint-config',
+ odesc='<conffile>',
+ long_arg=True),
+ 'showerrors': Bcfg2.Options.Option('Show error handling', False,
+ cmd='--list-errors',
+ long_arg=True),
+ })
+ setup = Bcfg2.Options.OptionParser(optinfo)
+ setup.parse(sys.argv[1:])
+
+ log_args = dict(to_syslog=False, to_console=logging.WARNING)
+ if setup['verbose']:
+ log_args['to_console'] = logging.DEBUG
+ Bcfg2.Logger.setup_logging('bcfg2-info', **log_args)
+
+ config = Parser()
+ config.read(setup['config'])
+
+ if setup['showerrors']:
+ if config.has_section("errors"):
+ econf = dict(config.items("errors"))
+ else:
+ econf = dict()
+
+ print("%-35s %-35s" % ("Error name", "Handler (Default)"))
+ for err, default in Bcfg2.Server.Lint.ErrorHandler._errors.items():
+ if err in econf and econf[err] != default:
+ handler = "%s (%s)" % (econf[err], default)
+ else:
+ handler = default
+ print("%-35s %-35s" % (err, handler))
+ raise SystemExit(0)
+
+ # get list of plugins to run
+ if setup['args']:
+ allplugins = setup['args']
+ elif "bcfg2-repo-validate" in sys.argv[0]:
+ allplugins = 'Duplicates,RequiredAttrs,Validate'.split(',')
+ else:
+ allplugins = config.get('lint', 'plugins',
+ ",".join(Bcfg2.Server.Lint.__all__)).split(',')
+
+ if setup['stdin']:
+ files = [s.strip() for s in sys.stdin.readlines()]
+ else:
+ files = None
+
+ # load plugins
+ serverplugins = {}
+ serverlessplugins = {}
+ for plugin_name in allplugins:
+ try:
+ mod = getattr(__import__("Bcfg2.Server.Lint.%s" %
+ (plugin_name)).Server.Lint, plugin_name)
+ except ImportError:
+ try:
+ mod = __import__(plugin_name)
+ except Exception:
+ err = sys.exc_info()[1]
+ logger.error("Failed to load plugin %s: %s" % (plugin_name,
+ err))
+ raise SystemExit(1)
+ plugin = getattr(mod, plugin_name)
+ if [c for c in inspect.getmro(plugin)
+ if c == Bcfg2.Server.Lint.ServerPlugin]:
+ serverplugins[plugin_name] = plugin
+ else:
+ serverlessplugins[plugin_name] = plugin
+
+ errorhandler = get_errorhandler(config)
+
+ run_serverless_plugins(serverlessplugins,
+ errorhandler=errorhandler,
+ config=config, setup=setup)
+
+ if serverplugins:
+ run_server_plugins(serverplugins, errorhandler=errorhandler,
+ config=config, setup=setup)
+
+ if errorhandler.errors or errorhandler.warnings or setup['verbose']:
+ print("%d errors" % errorhandler.errors)
+ print("%d warnings" % errorhandler.warnings)
+
+ if errorhandler.errors:
+ raise SystemExit(2)
+ elif errorhandler.warnings:
+ raise SystemExit(3)
diff --git a/src/sbin/bcfg2-ping-sweep b/src/sbin/bcfg2-ping-sweep
index 4082cad8b..70f718690 100755
--- a/src/sbin/bcfg2-ping-sweep
+++ b/src/sbin/bcfg2-ping-sweep
@@ -8,7 +8,7 @@ __revision__ = '$Revision$'
from os import dup2, execl, fork, uname, wait
import sys
import time
-import lxml.etree
+import lxml.etree
import Bcfg2.Options
@@ -20,9 +20,10 @@ if __name__ == '__main__':
cfpath = setup['configfile']
clientdatapath = "%s/Metadata/clients.xml" % setup['repo']
-
+
clientElement = lxml.etree.parse(clientdatapath)
- hostlist = [client.get('name') for client in clientElement.findall("Client")]
+ hostlist = [client.get('name')
+ for client in clientElement.findall("Client")]
pids = {}
null = open('/dev/null', 'w+')
@@ -31,9 +32,8 @@ if __name__ == '__main__':
#/bin/ping on linux /sbin/ping on os x
osname = uname()[0]
-
while hostlist or pids:
- if hostlist and len(pids.keys()) < 15:
+ if hostlist and len(list(pids.keys())) < 15:
host = hostlist.pop()
pid = fork()
if pid == 0:
@@ -47,7 +47,7 @@ if __name__ == '__main__':
execl('/sbin/ping', 'ping', '-t', '5', '-c', '1', host)
elif osname == 'SunOS':
execl('/usr/sbin/ping', 'ping', host, '56', '1')
- else: #default
+ else: # default
execl('/bin/ping', 'ping', '-w', '5', '-c', '1', host)
else:
pids[pid] = host
@@ -58,14 +58,15 @@ if __name__ == '__main__':
continue
chost = pids[cpid]
del pids[cpid]
- elm = clientElement.xpath("//Client[@name='%s']"%chost)[0]
+ elm = clientElement.xpath("//Client[@name='%s']" % chost)[0]
if status == 0:
- elm.set("pingable",'Y')
+ elm.set("pingable", 'Y')
elm.set("pingtime", str(time.time()))
else:
- elm.set("pingable",'N')
+ elm.set("pingable", 'N')
fout = open(clientdatapath, 'w')
- fout.write(lxml.etree.tostring(clientElement.getroot(), encoding='UTF-8', xml_declaration=True))
+ fout.write(lxml.etree.tostring(clientElement.getroot(),
+ encoding='UTF-8',
+ xml_declaration=True))
fout.close()
-
diff --git a/src/sbin/bcfg2-repo-validate b/src/sbin/bcfg2-repo-validate
index 554e4f72b..cea09cda3 100755..120000
--- a/src/sbin/bcfg2-repo-validate
+++ b/src/sbin/bcfg2-repo-validate
@@ -1,227 +1 @@
-#!/usr/bin/env python
-
-"""
-bcfg2-repo-validate checks all xml files in Bcfg2
-repos against their respective XML schemas.
-"""
-__revision__ = '$Revision$'
-
-import glob
-import lxml.etree
-import os
-import sys
-import Bcfg2.Options
-
-if __name__ == '__main__':
- opts = {'repo': Bcfg2.Options.SERVER_REPOSITORY,
- 'prefix': Bcfg2.Options.INSTALL_PREFIX,
- 'verbose': Bcfg2.Options.VERBOSE,
- 'configfile': Bcfg2.Options.CFILE}
- setup = Bcfg2.Options.OptionParser(opts)
- setup.parse(sys.argv[1:])
- verbose = setup['verbose']
- cpath = setup['configfile']
- prefix = setup['prefix']
- schemadir = "%s/share/bcfg2/schemas" % (prefix)
- os.chdir(schemadir)
- repo = setup['repo']
-
- # Get a list of all info.xml files in the bcfg2 repository
- info_list = []
- for infodir in ['Cfg', 'TGenshi', 'TCheetah']:
- for root, dirs, files in os.walk('%s/%s' % (repo, infodir)):
- for filename in files:
- if filename == 'info.xml':
- info_list.append(os.path.join(root, filename))
-
- # get metadata list (with all included files)
- metadata_list = glob.glob("%s/Metadata/groups.xml" % repo)
- ref_bundles = set()
- xdata = lxml.etree.parse("%s/Metadata/groups.xml" % repo)
- included = set([ent.get('href') for ent in \
- xdata.findall('./{http://www.w3.org/2001/XInclude}include')])
- while included:
- try:
- filename = included.pop()
- except KeyError:
- continue
- metadata_list.append("%s/Metadata/%s" % (repo, filename))
- groupdata = lxml.etree.parse("%s/Metadata/%s" % (repo, filename))
- group_ents = [ent.get('href') for ent in \
- groupdata.
- findall('./{http://www.w3.org/2001/XInclude}include')]
- for ent in group_ents:
- included.add(ent)
- included.discard(filename)
-
- # check for multiple default group definitions
- default_groups = []
- for grp in lxml.etree.parse("%s/Metadata/groups.xml" \
- % repo).findall('.//Group'):
- if grp.get('default') == 'true':
- default_groups.append(grp)
- if len(default_groups) > 1:
- print("*** Warning: Multiple default groups defined")
- for grp in default_groups:
- print(" %s" % grp.get('name'))
-
- # get all XIncluded bundles
- xdata.xinclude()
- for bundle in xdata.findall("//Bundle"):
- ref_bundles.add("%s/Bundler/%s" % (repo, bundle.get('name')))
-
- # get lists of all other xml files to validate
- clients_list = glob.glob("%s/Metadata/clients.xml" % repo)
- bundle_list = glob.glob("%s/Bundler/*.xml" % repo)
- genshibundle_list = glob.glob("%s/Bundler/*.genshi" % repo)
- pkg_list = glob.glob("%s/Pkgmgr/*.xml" % repo)
- base_list = glob.glob("%s/Base/*.xml" % repo)
- rules_list = glob.glob("%s/Rules/*.xml" % repo)
- imageinfo_list = glob.glob("%s/etc/report-configuration.xml" % repo)
- services_list = glob.glob("%s/Svcmgr/*.xml" % repo)
- deps_list = glob.glob("%s/Deps/*.xml" % repo)
- dec_list = glob.glob("%s/Decisions/*" % repo)
- pkgcfg_list = glob.glob("%s/Packages/config.xml" % repo)
- gp_list = glob.glob('%s/GroupPatterns/config.xml' % repo)
-
- # verify attributes for configuration entries
- # (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'],
- 'nonexistent': ['name'],
- 'permissions': ['name', 'owner', 'group', 'perms']}
- for rfile in rules_list:
- try:
- xdata = lxml.etree.parse(rfile)
- except lxml.etree.XMLSyntaxError, e:
- print("Failed to parse %s: %s" % (rfile, e))
- for posixpath in xdata.findall("//Path"):
- pathname = posixpath.get('name')
- pathtype = posixpath.get('type')
- pathset = set(posixpath.attrib.keys())
- try:
- required_attrs = set(required_configuration_attrs[pathtype] \
- + ['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:
- print("The following required attributes are missing for"
- " Path %s in %s: %s" % (pathname, rfile,
- [attr for attr in required_attrs.difference(pathset)]))
-
- # warn on duplicate Pkgmgr entries with the same priority
- pset = set()
- for plist in pkg_list:
- try:
- xdata = lxml.etree.parse(plist)
- except lxml.etree.XMLSyntaxError, e:
- print("Failed to parse %s: %s" % (plist, e))
- # get priority, type, group
- priority = xdata.getroot().get('priority')
- ptype = xdata.getroot().get('type')
- for pkg in xdata.findall("//Package"):
- if pkg.getparent().tag == 'Group':
- grp = pkg.getparent().get('name')
- if type(grp) is not str and grp.getparent().tag == 'Group':
- pgrp = grp.getparent().get('name')
- else:
- pgrp = 'none'
- else:
- grp = 'none'
- pgrp = 'none'
- ptuple = (pkg.get('name'), priority, ptype, grp, pgrp)
- # check if package is already listed with same priority,
- # type, grp
- if ptuple in pset:
- print("Duplicate Package %s, priority:%s, type:%s"\
- % (pkg.get('name'), priority, ptype))
- 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"),
- 'decisions': (dec_list, "%s/decisions.xsd"),
- 'packages': (pkgcfg_list, "%s/packages.xsd"),
- 'grouppatterns': (gp_list, "%s/grouppatterns.xsd"),
- }
-
- failures = 0
- for k, (filelist, schemaname) in list(filesets.items()):
- try:
- schema = lxml.etree.XMLSchema(lxml.etree.parse(open(schemaname%(schemadir))))
- except:
- print("Failed to process schema %s" % (schemaname%(schemadir)))
- failures = 1
- continue
- for filename in filelist:
- try:
- datafile = lxml.etree.parse(open(filename))
- except SyntaxError:
- print("%s ***FAILS*** to parse \t\t<----" % (filename))
- os.system("xmllint %s" % filename)
- failures = 1
- continue
- except IOError:
- print("Failed to open file %s \t\t<---" % (filename))
- failures = 1
- continue
- if schema.validate(datafile):
- if verbose:
- print("%s checks out" % (filename))
- else:
- rc = os.system("xmllint --noout --xinclude --schema \
- %s %s > /dev/null 2>/dev/null" % \
- (schemaname % schemadir, filename))
- if rc:
- failures = 1
- print("%s ***FAILS*** to verify \t\t<----" % (filename))
- os.system("xmllint --noout --xinclude --schema %s %s" % \
- (schemaname % schemadir, filename))
- elif verbose:
- print("%s checks out" % (filename))
-
- # print out missing bundle information
- if verbose:
- print("")
- for bundle in ref_bundles:
- # check for both regular and genshi bundles
- xmlbundle = "%s.xml" % bundle
- genshibundle = "%s.genshi" % bundle
- allbundles = bundle_list + genshibundle_list
- if xmlbundle not in allbundles and \
- genshibundle not in allbundles:
- print("*** Warning: Bundle %s referenced, but does not "
- "exist." % bundle)
- # verify bundle name attribute matches filename
- for bundle in (bundle_list + genshibundle_list):
- fname = bundle.split('Bundler/')[1].split('.')[0]
- xdata = lxml.etree.parse(bundle)
- bname = xdata.getroot().get('name')
- if fname != bname:
- print("The following names are inconsistent:")
- print(" Filename is %s" % fname)
- print(" Bundle name found in %s is %s" % (fname, bname))
-
-
- raise SystemExit, failures
+bcfg2-lint \ No newline at end of file
diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports
index d83e45e7c..20288fc5e 100755
--- a/src/sbin/bcfg2-reports
+++ b/src/sbin/bcfg2-reports
@@ -5,7 +5,13 @@ __revision__ = '$Revision$'
import os
import sys
-import Bcfg2.Server.Reports.settings
+try:
+ import Bcfg2.Server.Reports.settings
+except ConfigParser.NoSectionError:
+ print("Your bcfg2.conf is currently missing the statistics section which "
+ "is necessary for the reporting interface. Please see bcfg2.conf(5) "
+ "for more details.")
+ sys.exit(1)
project_directory = os.path.dirname(Bcfg2.Server.Reports.settings.__file__)
project_name = os.path.basename(project_directory)
@@ -87,13 +93,13 @@ def print_fields(fields, cli, max_name, entrydict):
if len(entrydict) > 0:
display += " "
display += str(entrydict[cli])
- print display
+ print(display)
def print_entry(item, max_name):
fmt = ("%%-%ds " % (max_name))
fdata = item.entry.kind + ":" + item.entry.name
display = fmt % (fdata)
- print display
+ print(display)
fields = ""
sort = ""
@@ -131,14 +137,14 @@ if expire != "":
if expire == c_inst.name:
if c_inst.expiration == None:
c_inst.expiration = datetime.datetime.now()
- print "Host expired."
+ print("Host expired.")
else:
c_inst.expiration = None
- print "Host un-expired."
+ print("Host un-expired.")
c_inst.save()
elif '-h' in args:
- print """Usage: bcfg2-reports [option] ...
+ print("""Usage: bcfg2-reports [option] ...
Options and arguments (and corresponding environment variables):
-a : shows all hosts, including expired hosts
@@ -164,13 +170,13 @@ Options and arguments (and corresponding environment variables):
(name,time,state)
--sort=ARG1,ARG2,... : sorts output on ARG1,ARG2,... (name,time,state)
--stale : shows hosts which haven't run in the last 24 hours
-"""
+""")
elif singlehost != "":
for c_inst in c_list:
if singlehost == c_inst.name:
baditems = c_inst.current_interaction.bad()
if len(baditems) > 0 and ('-b' in args or '-s' in args):
- print "Bad Entries:"
+ print("Bad Entries:")
max_name = -1
for item in baditems:
if len(item.entry.name) > max_name:
@@ -179,7 +185,7 @@ elif singlehost != "":
print_entry(item, max_name)
extraitems = c_inst.current_interaction.extra()
if len(extraitems) > 0 and ('-e' in args or '-s' in args):
- print "Extra Entries:"
+ print("Extra Entries:")
max_name = -1
for item in extraitems:
if len(item.entry.name) > max_name:
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index cf44f1699..f4bd5e5b7 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -69,7 +69,8 @@ if __name__ == '__main__':
certfile=setup['cert'],
ca=setup['ca'],
)
- except CoreInitError, msg:
+ except CoreInitError:
+ msg = sys.exc_info()[1]
logger.error(msg)
logger.error("exiting")
sys.exit(1)
diff --git a/testsuite/TestFrame.py b/testsuite/TestFrame.py
index 679e3ce27..a76c97eec 100644
--- a/testsuite/TestFrame.py
+++ b/testsuite/TestFrame.py
@@ -1,17 +1,21 @@
import lxml.etree
-import Bcfg2.Client.Frame, Bcfg2.Client.Tools
+import Bcfg2.Client.Frame
+import Bcfg2.Client.Tools
c1 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
c2 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
+
class DriverInitFail(object):
def __init__(self, *args):
raise Bcfg2.Client.Tools.toolInstantiationError
+
class DriverInventoryFail(object):
__name__ = 'dif'
+
def __init__(self, logger, setup, config):
self.config = config
self.handled = []
@@ -49,40 +53,48 @@ class TestFrame(object):
assert len(frame.tools) == 0
def test__Decide_Inventory(self):
- setup = {'remove':'none', 'bundle':[], 'interactive':False}
+ setup = {'remove': 'none',
+ 'bundle': [],
+ 'interactive': False}
times = {}
frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
[DriverInventoryFail], False)
assert len(frame.tools) == 1
frame.Inventory()
- assert len([x for x in frame.states.values() if x]) == 0
+ assert len([x for x in list(frame.states.values()) if x]) == 0
frame.Decide()
assert len(frame.whitelist)
def test__Decide_Bundle(self):
- setup = {'remove':'none', 'bundle':['bar'], 'interactive':False}
+ setup = {'remove': 'none',
+ 'bundle': ['bar'],
+ 'interactive': False}
times = {}
frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
[DriverInventoryFail], False)
assert len(frame.tools) == 1
frame.Inventory()
- assert len([x for x in frame.states.values() if x]) == 0
+ assert len([x for x in list(frame.states.values()) if x]) == 0
frame.Decide()
assert len(frame.whitelist) == 0
def test__Decide_Dryrun(self):
- setup = {'remove':'none', 'bundle':[], 'interactive':False}
+ setup = {'remove': 'none',
+ 'bundle': [],
+ 'interactive': False}
times = {}
frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
[DriverInventoryFail], True)
assert len(frame.tools) == 1
frame.Inventory()
- assert len([x for x in frame.states.values() if x]) == 0
+ assert len([x for x in list(frame.states.values()) if x]) == 0
frame.Decide()
assert len(frame.whitelist) == 0
def test__GenerateStats(self):
- setup = {'remove':'none', 'bundle':[], 'interactive':False}
+ setup = {'remove': 'none',
+ 'bundle': [],
+ 'interactive': False}
times = {}
frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
[DriverInventoryFail], False)
@@ -90,4 +102,3 @@ class TestFrame(object):
frame.Decide()
stats = frame.GenerateStats()
assert len(stats.findall('.//Bad')[0].getchildren()) != 0
-
diff --git a/testsuite/TestOptions.py b/testsuite/TestOptions.py
index e7d2aeff0..735455d45 100644
--- a/testsuite/TestOptions.py
+++ b/testsuite/TestOptions.py
@@ -1,6 +1,9 @@
-import os, sys
+import os
+import sys
+
import Bcfg2.Options
+
class TestOption(object):
def test__init(self):
o = Bcfg2.Options.Option('foo', False, cmd='-F')
@@ -22,11 +25,11 @@ class TestOption(object):
assert o.value == 'test3'
del os.environ['TEST2']
o.parse([], [])
- print o.value
+ print(o.value)
assert o.value == 'foobat'
o.cf = ('communication', 'pwd')
o.parse([], [])
- print o.value
+ print(o.value)
assert o.value == 'test4'
o.cf = False
o.parse([], [])
@@ -41,11 +44,13 @@ class TestOption(object):
o2.parse([('-F', '')], [])
assert o2.value == True
+
class TestOptionSet(object):
def test_buildGetopt(self):
opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H', odesc='1'))]
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
os = Bcfg2.Options.OptionSet(opts)
res = os.buildGetopt()
assert 'H:' in res and 'G' in res and len(res) == 3
@@ -57,13 +62,14 @@ class TestOptionSet(object):
odesc='1', long_arg=True))]
os = Bcfg2.Options.OptionSet(opts)
res = os.buildLongGetopt()
- print res
+ print(res)
assert 'H=' in res and len(res) == 1
def test_parse(self):
opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H', odesc='1'))]
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
os = Bcfg2.Options.OptionSet(opts)
try:
os.parse(['-G', '-H'])
@@ -80,11 +86,13 @@ class TestOptionSet(object):
os3.parse(['-G'])
assert os3['foo'] == True
+
class TestOptionParser(object):
def test__init(self):
opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-h')),
('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H', odesc='1'))]
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
os1 = Bcfg2.Options.OptionParser(opts)
assert Bcfg2.Options.Option.cfpath == '/etc/bcfg2.conf'
sys.argv = ['foo', '-C', '/usr/local/etc/bcfg2.conf']
diff --git a/testsuite/TestPlugin.py b/testsuite/TestPlugin.py
index a6affbec2..aa619249a 100644
--- a/testsuite/TestPlugin.py
+++ b/testsuite/TestPlugin.py
@@ -1,6 +1,11 @@
-import os, Bcfg2.Server.Core, gamin, lxml.etree
+import gamin
+import lxml.etree
+import os
+
+import Bcfg2.Server.Core
from Bcfg2.Server.Plugin import EntrySet
+
class es_testtype(object):
def __init__(self, name, properties, specific):
self.name = name
@@ -11,27 +16,30 @@ class es_testtype(object):
def handle_event(self, event):
self.handled += 1
-
+
def bind_entry(self, entry, metadata):
entry.set('bound', '1')
entry.set('name', self.name)
self.built += 1
-
+
+
class metadata(object):
def __init__(self, hostname):
self.hostname = hostname
self.groups = ['base', 'debian']
-
+
#FIXME add test_specific
+
class test_entry_set(object):
def __init__(self):
self.dirname = '/tmp/estest-%d' % os.getpid()
- os.path.isdir(self.dirname) or os.mkdir(self.dirname)
+ os.path.isdir(self.dirname) or os.mkdir(self.dirname)
self.metadata = metadata('testhost')
self.es = EntrySet('template', self.dirname, None, es_testtype)
self.e = Bcfg2.Server.Core.GaminEvent(1, 'template',
gamin.GAMExists)
+
def test_init(self):
es = self.es
e = self.e
@@ -39,27 +47,26 @@ class test_entry_set(object):
es.handle_event(e)
es.handle_event(e)
assert len(es.entries) == 1
- assert es.entries.values()[0].handled == 2
+ assert list(es.entries.values())[0].handled == 2
e.action = 'changed'
es.handle_event(e)
- assert es.entries.values()[0].handled == 3
-
-
- def test_info(self):
+ assert list(es.entries.values())[0].handled == 3
+
+ def test_info(self):
"""Test info and info.xml handling."""
es = self.es
- e = self.e
+ e = self.e
dirname = self.dirname
metadata = self.metadata
-
- # test 'info' handling
+
+ # test 'info' handling
assert es.metadata['group'] == 'root'
self.mk_info(dirname)
e.filename = 'info'
e.action = 'exists'
es.handle_event(e)
assert es.metadata['group'] == 'sys'
- e.action = 'deleted'
+ e.action = 'deleted'
es.handle_event(e)
assert es.metadata['group'] == 'root'
@@ -73,17 +80,15 @@ class test_entry_set(object):
e.action = 'deleted'
es.handle_event(e)
assert es.infoxml == None
-
def test_file_building(self):
"""Test file building."""
self.test_init()
ent = lxml.etree.Element('foo')
self.es.bind_entry(ent, self.metadata)
- print self.es.entries.values()[0]
- assert self.es.entries.values()[0].built == 1
-
-
+ print(list(self.es.entries.values())[0])
+ assert list(self.es.entries.values())[0].built == 1
+
def test_host_specific_file_building(self):
"""Add a host-specific template and build it."""
self.e.filename = 'template.H_%s' % self.metadata.hostname
@@ -93,9 +98,7 @@ class test_entry_set(object):
ent = lxml.etree.Element('foo')
self.es.bind_entry(ent, self.metadata)
# FIXME need to test that it built the _right_ file here
-
-
-
+
def test_deletion(self):
"""Test deletion of files."""
self.test_init()
@@ -103,9 +106,9 @@ class test_entry_set(object):
self.e.action = 'deleted'
self.es.handle_event(self.e)
assert len(self.es.entries) == 0
-
+
# TODO - how to clean up the temp dir & files after tests done?
-
+
def mk_info(self, dir):
i = open("%s/info" % dir, 'w')
i.write('owner: root\n')
@@ -117,6 +120,3 @@ class test_entry_set(object):
i = open("%s/info.xml" % dir, 'w')
i.write('<FileInfo><Info owner="root" group="other" perms="0600" /></FileInfo>\n')
i.close
-
-
-
diff --git a/tools/basebuilder.py b/tools/basebuilder.py
index 704769755..a9ab6b288 100644
--- a/tools/basebuilder.py
+++ b/tools/basebuilder.py
@@ -7,11 +7,11 @@ if __name__ == '__main__':
dir = argv[1]
imagename = dir.split('/')[-1]
e = Element("Image", name=imagename)
- for line in open("%s/base.ConfigFile"%(dir)).readlines():
+ for line in open("%s/base.ConfigFile" % (dir)).readlines():
SubElement(e, "ConfigFile", name=line.strip())
- for line in open("%s/base.Package"%(dir)).readlines():
+ for line in open("%s/base.Package" % (dir)).readlines():
SubElement(e, "Package", name=line.strip())
- for line in open("%s/base.Service"%(dir)).readlines():
+ for line in open("%s/base.Service" % (dir)).readlines():
SubElement(e, "Service", name=line.strip().split()[0])
- print tostring(e)
+ print(tostring(e))
diff --git a/tools/batchadd.py b/tools/batchadd.py
index ce47650a5..e8008b330 100755
--- a/tools/batchadd.py
+++ b/tools/batchadd.py
@@ -1,23 +1,37 @@
#!/usr/bin/python
-import sys, os
from datetime import date
+import os
+import sys
+
os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.Server.Hostbase.settings'
from Bcfg2.Server.Hostbase.hostbase.models import *
from Bcfg2.Server.Hostbase.settings import DEFAULT_MX, PRIORITY
import Bcfg2.Server.Hostbase.regex
-host_attribs = ['hostname', 'whatami', 'netgroup', 'security_class', 'support',
- 'csi', 'printq', 'outbound_smtp', 'primary_user',
- 'administrator', 'location', 'expiration_date', 'comments']
+host_attribs = ['administrator',
+ 'comments',
+ 'csi',
+ 'expiration_date',
+ 'hostname',
+ 'location',
+ 'netgroup',
+ 'outbound_smtp',
+ 'primary_user',
+ 'printq',
+ 'security_class',
+ 'support',
+ 'whatami']
+
def handle_error(field):
if '-f' in sys.argv:
return
- print "Error: %s is already defined in hostbase" % field
+ print("Error: %s is already defined in hostbase" % field)
if '-s' in sys.argv:
sys.exit(1)
+
def checkformat(values, indices):
"""Ensures file contains all necessary attributes in order """
filelist = [pair[0] for pair in values]
@@ -34,8 +48,8 @@ def checkformat(values, indices):
# process rest of host attributes
try:
next = filelist[1:].index('hostname')
- remaining = filelist[13:next+1]
- filelist = filelist[next+1:]
+ remaining = filelist[13:next + 1]
+ filelist = filelist[next + 1:]
except:
remaining = filelist[13:]
needfields = ['mac_addr', 'hdwr_type', 'ip_addr']
@@ -50,7 +64,7 @@ if __name__ == '__main__':
try:
fd = open(sys.argv[1], 'r')
except (IndexError, IOError):
- print "\nUsage: batchadd.py filename\n"
+ print("\nUsage: batchadd.py filename\n")
sys.exit()
lines = fd.readlines()
@@ -59,18 +73,20 @@ if __name__ == '__main__':
if line.lstrip(' ')[0] != '#' and line != '\n']
if info[0][0] == 'mx' and info[1][0] == 'priority':
- mx, created = MX.objects.get_or_create(mx=info[0][1], priority=info[1][1])
+ mx, created = MX.objects.get_or_create(mx=info[0][1],
+ priority=info[1][1])
info = info[2:]
-
else:
- mx, created = MX.objects.get_or_create(mx=DEFAULT_MX, priority=PRIORITY)
+ mx, created = MX.objects.get_or_create(mx=DEFAULT_MX,
+ priority=PRIORITY)
if created:
mx.save()
- hostindices = [num for num in range(0, len(info)) if info[num][0] == 'hostname']
+ hostindices = [num for num in range(0, len(info))
+ if info[num][0] == 'hostname']
if not checkformat(info, hostindices):
- print "Error: file format"
+ print("Error: file format")
sys.exit()
#################
@@ -83,7 +99,8 @@ if __name__ == '__main__':
# do something here
pass
- macindices = [num for num in range(0, len(info)) if info[num][0] == 'mac_addr']
+ macindices = [num for num in range(0, len(info))
+ if info[num][0] == 'mac_addr']
for mac_addr in macindices:
try:
host = Interface.objects.get(mac_addr=info[mac_addr][1])
@@ -103,7 +120,9 @@ if __name__ == '__main__':
blank.__dict__[pair[0]] = 0
elif pair[0] == 'expiration_date':
(year, month, day) = pair[1].split("-")
- blank.expiration_date = date(int(year), int(month), int(day))
+ blank.expiration_date = date(int(year),
+ int(month),
+ int(day))
else:
blank.__dict__[pair[0]] = pair[1]
blank.status = 'active'
@@ -113,7 +132,9 @@ if __name__ == '__main__':
while info and info[0][0] != 'hostname':
if info[0][0] == 'mac_addr':
pair = info.pop(0)
- inter = Interface.objects.create(host=blank, mac_addr=pair[1], hdwr_type='eth')
+ inter = Interface.objects.create(host=blank,
+ mac_addr=pair[1],
+ hdwr_type='eth')
if not pair[1]:
inter.dhcp = False
inter.save()
@@ -124,13 +145,17 @@ if __name__ == '__main__':
elif info[0][0] == 'ip_addr':
pair = info.pop(0)
ip = IP.objects.create(interface=inter, ip_addr=pair[1])
- hostnamenode = Name(ip=ip, name=blank.hostname, dns_view='global', only=False)
+ hostnamenode = Name(ip=ip,
+ name=blank.hostname,
+ dns_view='global',
+ only=False)
hostnamenode.save()
- namenode = Name(ip=ip, name=".".join([newhostname + "-" + inter.hdwr_type,
- newdomain]),
+ namenode = Name(ip=ip,
+ name=".".join([newhostname + "-" + inter.hdwr_type,
+ newdomain]),
dns_view="global", only=False)
namenode.save()
- subnetnode = Name(ip=ip, name=newhostname + "-" +
+ subnetnode = Name(ip=ip, name=newhostname + "-" +
ip.ip_addr.split(".")[2] + "." +
newdomain, dns_view="global", only=False)
subnetnode.save()
@@ -141,4 +166,3 @@ if __name__ == '__main__':
pair = info.pop(0)
cname = CName.objects.create(name=hostnamenode, cname=pair[1])
cname.save()
-
diff --git a/tools/create-debian-pkglist-gp.py b/tools/create-debian-pkglist-gp.py
index ae038e056..cefb8f3fb 100644
--- a/tools/create-debian-pkglist-gp.py
+++ b/tools/create-debian-pkglist-gp.py
@@ -1,26 +1,35 @@
#!/usr/bin/env python
'''Build debian/ubuntu package indexes'''
-__revision__ = '$Id: create-debian-pkglist.py 11778 2007-12-11 13:46:06Z guillaume $'
# Original code from Bcfg2 sources
-import gzip, os, urllib, cStringIO, sys, ConfigParser, commands
+import gzip
+import os
+import sys
+import subprocess
+
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import StringIO
+from Bcfg2.Bcfg2Py3k import ConfigParser
+from Bcfg2.Bcfg2Py3k import urlopen
def debug(msg):
'''print debug messages'''
if '-v' in sys.argv:
sys.stdout.write(msg)
+
def get_as_list(somestring):
""" Input : a string like this : 'a, g, f,w'
Output : a list like this : ['a', 'g', 'f', 'w'] """
return somestring.replace(' ', '').split(',')
+
def list_contains_all_the_same_values(l):
if len(l) == 0:
return True
- # The list contains all the same values if all elements in
+ # The list contains all the same values if all elements in
# the list are equal to the first element.
first = l[0]
for elem in l:
@@ -28,6 +37,7 @@ def list_contains_all_the_same_values(l):
return False
return True
+
class SourceURL:
def __init__(self, deb_url):
deb_url_tokens = deb_url.split()
@@ -35,13 +45,14 @@ class SourceURL:
self.url = deb_url_tokens[1]
self.distribution = deb_url_tokens[2]
self.sections = deb_url_tokens[3:]
-
+
def __str__(self):
return "deb %s %s %s" % (self.url, self.distribution, ' '.join(self.sections))
-
+
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, str(self))
+
class Source:
def __init__(self, confparser, section, bcfg2_repos_prefix):
self.filename = "%s/Pkgmgr/%s.xml" % (bcfg2_repos_prefix, section)
@@ -52,11 +63,11 @@ class Source:
self.source_urls = []
self.source_urls.append(SourceURL(confparser.get(section, "deb_url")))
# Agregate urls in the form of deb_url0, deb_url1, ... to deb_url9
- for i in range(10): # 0 to 9
+ for i in range(10): # 0 to 9
option_name = "deb_url%s" % i
if confparser.has_option(section, option_name):
self.source_urls.append(SourceURL(confparser.get(section, option_name)))
-
+
self.file = None
self.indent_level = 0
@@ -66,13 +77,13 @@ Groups: %s
Priority: %s
Architectures: %s
Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectures, self.source_urls)
-
+
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, str(self))
def _open_file(self):
self.file = open(self.filename + '~', 'w')
-
+
def _close_file(self):
self.file.close()
@@ -88,7 +99,8 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
# Avoid forking a new process if the two strings are equals
if version1 == version2:
return False
- (status, output) = commands.getstatusoutput("/usr/bin/dpkg --compare-versions %s lt %s" % (version1, version2))
+ (status, output) = subprocess.getstatusoutput("/usr/bin/dpkg --compare-versions %s lt %s" % (version1,
+ version2))
#print "%s dpkg --compare-versions %s lt %s" % (status, version1, version2)
return status == 0
@@ -98,8 +110,8 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
url = "%s/dists/%s/%s/binary-%s/Packages.gz" % (source_url.url, source_url.distribution, section, arch)
debug("Processing url %s\n" % (url))
try:
- data = urllib.urlopen(url)
- buf = cStringIO.StringIO(''.join(data.readlines()))
+ data = urlopen(url)
+ buf = StringIO(''.join(data.readlines()))
reader = gzip.GzipFile(fileobj=buf)
for line in reader.readlines():
if line[:8] == 'Package:':
@@ -119,16 +131,17 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
pkgdata[pkgname][arch] = version
else:
# First entry for this package
- pkgdata[pkgname] = {arch:version}
+ pkgdata[pkgname] = {arch: version}
else:
continue
except:
- raise Exception("Could not process URL %s\n%s\nPlease verify the URL." % (url, sys.exc_value))
+ raise Exception("Could not process URL %s\n%s\nPlease "
+ "verify the URL." % (url, sys.exc_info()[1]))
return pkgdata
-
+
def _get_sorted_pkg_keys(self, pkgdata):
pkgs = []
- for k in pkgdata.keys():
+ for k in list(pkgdata.keys()):
pkgs.append(k)
pkgs.sort()
return pkgs
@@ -142,7 +155,7 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
# (There is exactly one version per architecture)
archdata = pkgdata[pkg]
# List of versions for all architectures of this package
- pkgversions = archdata.values()
+ pkgversions = list(archdata.values())
# If the versions for all architectures are the same
if list_contains_all_the_same_values(pkgversions):
# Write the package data
@@ -153,7 +166,7 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
del pkgdata[pkg]
def _write_perarch_entries(self, pkgdata):
- # Write entries that are left, i.e. packages that have different
+ # Write entries that are left, i.e. packages that have different
# versions per architecture
#perarch = 0
if pkgdata:
@@ -170,29 +183,29 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
def process(self):
'''Build package indices for source'''
-
+
# First, build the pkgdata structure without touching the file,
# so the file does not contain incomplete informations if the
# network in not reachable.
pkgdata = {}
for source_url in self.source_urls:
pkgdata = self._update_pkgdata(pkgdata, source_url)
-
+
# Construct the file.
self._open_file()
for source_url in self.source_urls:
self._write_to_file('<!-- %s -->' % source_url)
-
+
self._write_to_file('<PackageList priority="%s" type="deb">' % self.priority)
-
+
self.indent_level = self.indent_level + 1
for group in self.groups:
self._write_to_file('<Group name="%s">' % group)
self.indent_level = self.indent_level + 1
-
+
self._write_common_entries(pkgdata)
self._write_perarch_entries(pkgdata)
-
+
for group in self.groups:
self.indent_level = self.indent_level - 1
self._write_to_file('</Group>')
@@ -205,10 +218,10 @@ if __name__ == '__main__':
main_conf_parser = ConfigParser.SafeConfigParser()
main_conf_parser.read(['/etc/bcfg2.conf'])
repo = main_conf_parser.get('server', 'repository')
-
+
confparser = ConfigParser.SafeConfigParser()
confparser.read(os.path.join(repo, "etc/debian-pkglist.conf"))
-
+
# We read the whole configuration file before processing each entries
# to avoid doing work if there is a problem in the file.
sources_list = []
diff --git a/tools/create-debian-pkglist.py b/tools/create-debian-pkglist.py
index 450c6aba6..8e1210582 100755
--- a/tools/create-debian-pkglist.py
+++ b/tools/create-debian-pkglist.py
@@ -5,7 +5,17 @@ __revision__ = '$Id$'
# Original code from Bcfg2 sources
-import glob, gzip, lxml.etree, os, re, urllib, cStringIO, sys, ConfigParser, apt_pkg
+import apt_pkg
+import gzip
+import os
+import re
+import sys
+
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import StringIO
+from Bcfg2.Bcfg2Py3k import ConfigParser
+from Bcfg2.Bcfg2Py3k import urlopen
+
apt_pkg.init()
@@ -14,15 +24,17 @@ def debug(msg):
if '-v' in sys.argv:
sys.stdout.write(msg)
+
def get_as_list(somestring):
""" Input : a string like this : 'a, g, f,w'
Output : a list like this : ['a', 'g', 'f', 'w'] """
return somestring.replace(' ', '').split(',')
+
def list_contains_all_the_same_values(l):
if len(l) == 0:
return True
- # The list contains all the same values if all elements in
+ # The list contains all the same values if all elements in
# the list are equal to the first element.
first = l[0]
for elem in l:
@@ -30,6 +42,7 @@ def list_contains_all_the_same_values(l):
return False
return True
+
class SourceURL:
def __init__(self, deb_url, arch):
deb_url_tokens = deb_url.split()
@@ -38,13 +51,14 @@ class SourceURL:
self.distribution = deb_url_tokens[2]
self.sections = deb_url_tokens[3:]
self.arch = arch
-
+
def __str__(self):
return "deb %s %s %s" % (self.url, self.distribution, ' '.join(self.sections))
-
+
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, str(self))
+
class Source:
def __init__(self, confparser, section, bcfg2_repos_prefix):
self.filename = "%s/Pkgmgr/%s.xml" % (bcfg2_repos_prefix, section)
@@ -58,25 +72,32 @@ class Source:
self.arch_specialurl = set()
self.source_urls = []
- self.source_urls.append(SourceURL(confparser.get(section, "deb_url"),"all"))
+ self.source_urls.append(SourceURL(confparser.get(section, "deb_url"),
+ "all"))
# Agregate urls in the form of deb_url0, deb_url1, ... to deb_url9
- for i in range(10): # 0 to 9
+ for i in range(10): # 0 to 9
option_name = "deb_url%s" % i
if confparser.has_option(section, option_name):
- self.source_urls.append(SourceURL(confparser.get(section, option_name),"all"))
+ self.source_urls.append(SourceURL(confparser.get(section,
+ option_name),
+ "all"))
# Aggregate architecture specific urls (if present)
for arch in self.architectures:
- if not confparser.has_option(section, "deb_"+arch+"_url"):
+ if not confparser.has_option(section, "deb_" + arch + "_url"):
continue
- self.source_urls.append(SourceURL(confparser.get(section, "deb_"+arch+"_url"),arch))
+ self.source_urls.append(SourceURL(confparser.get(section,
+ "deb_" + arch + "_url"),
+ arch))
# Agregate urls in the form of deb_url0, deb_url1, ... to deb_url9
- for i in range(10): # 0 to 9
- option_name = "deb_"+arch+"_url%s" % i
+ for i in range(10): # 0 to 9
+ option_name = "deb_" + arch + "_url%s" % i
if confparser.has_option(section, option_name):
- self.source_urls.append(SourceURL(confparser.get(section, option_name),arch))
+ self.source_urls.append(SourceURL(confparser.get(section,
+ option_name),
+ arch))
self.arch_specialurl.add(arch)
-
+
self.file = None
self.indent_level = 0
@@ -86,13 +107,13 @@ Groups: %s
Priority: %s
Architectures: %s
Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectures, self.source_urls)
-
+
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, str(self))
def _open_file(self):
self.file = open(self.filename + '~', 'w')
-
+
def _close_file(self):
self.file.close()
@@ -118,19 +139,22 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
continue
if source_url.arch == "all" and arch in self.arch_specialurl:
continue
- url = "%s/dists/%s/%s/binary-%s/Packages.gz" % (source_url.url, source_url.distribution, section, arch)
+ url = "%s/dists/%s/%s/binary-%s/Packages.gz" % (source_url.url,
+ source_url.distribution,
+ section,
+ arch)
debug("Processing url %s\n" % (url))
try:
- data = urllib.urlopen(url)
- buf = cStringIO.StringIO(''.join(data.readlines()))
+ data = urlopen(url)
+ buf = StringIO(''.join(data.readlines()))
reader = gzip.GzipFile(fileobj=buf)
for line in reader.readlines():
if line[:8] == 'Package:':
pkgname = line.split(' ')[1].strip()
elif line[:8] == 'Version:':
version = line.split(' ')[1].strip()
- if pkgdata.has_key(pkgname):
- if pkgdata[pkgname].has_key(arch):
+ if pkgname in pkgdata:
+ if arch in pkgdata[pkgname]:
# The package is listed twice for the same architecture
# We keep the most recent version
old_version = pkgdata[pkgname][arch]
@@ -142,20 +166,21 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
pkgdata[pkgname][arch] = version
else:
# First entry for this package
- pkgdata[pkgname] = {arch:version}
+ pkgdata[pkgname] = {arch: version}
else:
continue
except:
- raise Exception("Could not process URL %s\n%s\nPlease verify the URL." % (url, sys.exc_value))
- return dict((k,v) for (k,v) in pkgdata.items() \
+ raise Exception("Could not process URL %s\n%s\nPlease "
+ "verify the URL." % (url, sys.exc_info()[1]))
+ return dict((k, v) for (k, v) in list(pkgdata.items()) \
if re.search(self.pattern, k))
-
+
def _get_sorted_pkg_keys(self, pkgdata):
- pkgs = []
- for k in pkgdata.keys():
- pkgs.append(k)
- pkgs.sort()
- return pkgs
+ pkgs = []
+ for k in list(pkgdata.keys()):
+ pkgs.append(k)
+ pkgs.sort()
+ return pkgs
def _write_common_entries(self, pkgdata):
# Write entries for packages that have the same version
@@ -166,7 +191,7 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
# (There is exactly one version per architecture)
archdata = pkgdata[pkg]
# List of versions for all architectures of this package
- pkgversions = archdata.values()
+ pkgversions = list(archdata.values())
# If the versions for all architectures are the same
if len(self.architectures) == len(pkgversions) and list_contains_all_the_same_values(pkgversions):
# Write the package data
@@ -177,7 +202,7 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
del pkgdata[pkg]
def _write_perarch_entries(self, pkgdata):
- # Write entries that are left, i.e. packages that have different
+ # Write entries that are left, i.e. packages that have different
# versions per architecture
#perarch = 0
if pkgdata:
@@ -185,7 +210,7 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
self._write_to_file('<Group name="%s">' % (arch))
self.indent_level = self.indent_level + 1
for pkg in self._get_sorted_pkg_keys(pkgdata):
- if pkgdata[pkg].has_key(arch):
+ if arch in pkgdata[pkg]:
self._write_to_file('<Package name="%s" version="%s"/>' % (pkg, pkgdata[pkg][arch]))
#perarch += 1
self.indent_level = self.indent_level - 1
@@ -194,29 +219,29 @@ Source URLS: %s""" % (self.filename, self.groups, self.priority, self.architectu
def process(self):
'''Build package indices for source'''
-
+
# First, build the pkgdata structure without touching the file,
# so the file does not contain incomplete informations if the
# network in not reachable.
pkgdata = {}
for source_url in self.source_urls:
pkgdata = self._update_pkgdata(pkgdata, source_url)
-
+
# Construct the file.
self._open_file()
for source_url in self.source_urls:
self._write_to_file('<!-- %s -->' % source_url)
-
+
self._write_to_file('<PackageList priority="%s" type="deb">' % self.priority)
-
+
self.indent_level = self.indent_level + 1
for group in self.groups:
self._write_to_file('<Group name="%s">' % group)
self.indent_level = self.indent_level + 1
-
+
self._write_common_entries(pkgdata)
self._write_perarch_entries(pkgdata)
-
+
for group in self.groups:
self.indent_level = self.indent_level - 1
self._write_to_file('</Group>')
@@ -229,10 +254,10 @@ if __name__ == '__main__':
# Prefix is relative to script path
complete_script_path = os.path.join(os.getcwd(), sys.argv[0])
prefix = complete_script_path[:-len('etc/create-debian-pkglist.py')]
-
+
confparser = ConfigParser.SafeConfigParser()
confparser.read(prefix + "etc/debian-pkglist.conf")
-
+
# We read the whole configuration file before processing each entries
# to avoid doing work if there is a problem in the file.
sources_list = []
diff --git a/tools/create-rpm-pkglist.py b/tools/create-rpm-pkglist.py
index e88de4191..f9dc258ab 100644
--- a/tools/create-rpm-pkglist.py
+++ b/tools/create-rpm-pkglist.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright (c) 2010 Fabian Affolter, Bernewireless.net.
+# Copyright (c) 2010 Fabian Affolter, Bernewireless.net.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@@ -27,17 +27,17 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Author: Fabian Affolter <fabian at bernewireless.net>
-#
+#
-import yum
-import os
-import sys
from lxml import etree
from optparse import OptionParser
+import os
+import yum
__author__ = 'Fabian Affolter <fabian@bernewireless.net>'
__version__ = '0.1'
+
def retrievePackages():
"""Getting the installed packages with yum."""
yb = yum.YumBase()
@@ -46,10 +46,11 @@ def retrievePackages():
pkglist = []
for pkg in sorted(pl.installed):
pkgdata = pkg.name, pkg.version
- pkglist.append(pkgdata)
-
+ pkglist.append(pkgdata)
+
return pkglist
+
def parse_command_line_parameters():
"""Parses command line arguments."""
usage = "usage: %prog [options]"
@@ -62,22 +63,23 @@ def parse_command_line_parameters():
parser.add_option("-f", "--filename", dest="filename",
type="string",
metavar="FILE", default="packages.xml",
- help="Write the output to an XML FILE" )
+ help="Write the output to an XML FILE")
(options, args) = parser.parse_args()
num_args = 1
return options, args
+
def indent(elem, level=0):
"""Helps clean up the XML."""
# Stolen from http://effbot.org/zone/element-lib.htm
- i = "\n" + level*" "
+ i = "\n" + level * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
for e in elem:
- indent(e, level+1)
+ indent(e, level + 1)
if not e.tail or not e.tail.strip():
e.tail = i + " "
if not e.tail or not e.tail.strip():
@@ -86,17 +88,19 @@ def indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
+
def transformXML():
"""Transform the package list to an XML file."""
packagelist = retrievePackages()
root = etree.Element("PackageList")
- for i,j in packagelist:
- root.append( etree.Element("Package", name = i, version = j) )
+ for i, j in packagelist:
+ root.append(etree.Element("Package", name=i, version=j))
#Print the content
#print(etree.tostring(root, pretty_print=True))
tree = etree.ElementTree(root)
return tree
+
def main():
options, args = parse_command_line_parameters()
filename = options.filename
@@ -105,14 +109,14 @@ def main():
if options.show == True:
tree = etree.parse(filename)
for node in tree.findall("//Package"):
- print node.attrib["name"]
+ print(node.attrib["name"])
indent(packagelist.getroot())
packagelist.write(filename, encoding="utf-8")
if options.pkgversion == True:
tree = etree.parse(filename)
for node in tree.findall("//Package"):
- print "%s-%s" % (node.attrib["name"], node.attrib["version"])
+ print("%s-%s" % (node.attrib["name"], node.attrib["version"]))
#FIXME : This should be changed to the standard way of optparser
#FIXME : Make an option available to strip the version number of the pkg
diff --git a/tools/export.py b/tools/export.py
index 971102290..d637c166c 100755
--- a/tools/export.py
+++ b/tools/export.py
@@ -4,25 +4,37 @@
First attempt to make our export script more portable than export.sh
"""
-from email.Utils import formatdate
import fileinput
from subprocess import Popen, PIPE
import sys
+# Compatibility import
+from Bcfg2.Bcfg2Py3k import formatdate
+
pkgname = 'bcfg2'
ftphost = 'terra.mcs.anl.gov'
ftpdir = '/mcs/ftp/pub/bcfg'
-version = raw_input("Please enter the version you are tagging (e.g. 1.0.0): ")
+# py3k compatibility
+try:
+ version = raw_input("Please enter the version you are tagging (e.g. 1.0.0): ")
+except NameError:
+ version = input("Please enter the version you are tagging (e.g. 1.0.0): ")
tarname = '/tmp/%s-%s.tar.gz' % (pkgname, version)
+
def run(command):
return Popen(command, shell=True, stdout=PIPE).communicate()
# update the version
majorver = version[:5]
minorver = version[5:]
-name = raw_input("Your name: ")
-email = raw_input("Your email: ")
+# py3k compatibility
+try:
+ name = raw_input("Your name: ")
+ email = raw_input("Your email: ")
+except NameError:
+ name = input("Your name: ")
+ email = input("Your email: ")
newchangelog = \
"""bcfg2 (%s-0.0%s) unstable; urgency=low
@@ -61,14 +73,15 @@ for line in fileinput.input('solaris/Makefile', inplace=1):
sys.stdout.write(line)
for line in fileinput.input('solaris/pkginfo.bcfg2', inplace=1):
if line.startswith('VERSION='):
- line = line.replace(line, 'VERSION=%s\n' % version)
+ line = line.replace(line, 'VERSION="%s"\n' % version)
sys.stdout.write(line)
for line in fileinput.input('solaris/pkginfo.bcfg2-server', inplace=1):
if line.startswith('VERSION='):
- line = line.replace(line, 'VERSION=%s\n' % version)
+ line = line.replace(line, 'VERSION="%s"\n' % version)
sys.stdout.write(line)
# update the version in reports
-for line in fileinput.input('src/lib/Server/Reports/reports/templates/base.html', inplace=1):
+for line in fileinput.input('src/lib/Server/Reports/reports/templates/base.html',
+ inplace=1):
if 'Bcfg2 Version' in line:
line = line.replace(line, ' <span>Bcfg2 Version %s</span>\n' % version)
sys.stdout.write(line)
diff --git a/tools/hostbase.py b/tools/hostbase.py
index 974577e69..7474e68b7 100755
--- a/tools/hostbase.py
+++ b/tools/hostbase.py
@@ -1,36 +1,50 @@
#!/usr/bin/python
-import sys, os
-os.environ['DJANGO_SETTINGS_MODULE'] = 'Hostbase.settings'
-from Hostbase.hostbase.models import Host, Interface
+import os
from getopt import getopt, GetoptError
from re import split
+import sys
+
+os.environ['DJANGO_SETTINGS_MODULE'] = 'Hostbase.settings'
+from Hostbase.hostbase.models import Host
-attribs = ['hostname', 'whatami', 'netgroup', 'security_class', 'support',
- 'csi', 'printq', 'dhcp', 'outbound_smtp', 'primary_user',
- 'administrator', 'location', 'expiration_date', 'comments',
- 'status', 'last']
+attribs = ['administrator',
+ 'comments',
+ 'csi',
+ 'dhcp',
+ 'expiration_date',
+ 'hostname',
+ 'last',
+ 'location',
+ 'netgroup',
+ 'outbound_smtp',
+ 'primary_user',
+ 'printq',
+ 'security_class',
+ 'support',
+ 'status',
+ 'whatami']
already_exists = None
#here's my attempt at making the command line idiot proof
#you must supply and arugument and hostname for hostbase.py to run
try:
- (opts, args) = getopt(sys.argv[1:],'l:c:')
+ (opts, args) = getopt(sys.argv[1:], 'l:c:')
sys.argv[1]
if len(split("\.", opts[0][1])) == 1:
hosttouse = opts[0][1] + ".mcs.anl.gov"
else:
hosttouse = opts[0][1]
except (GetoptError, IndexError):
- print "\nUsage: hostbase.py -flag (hostname)\n"
- print "Flags:"
- print "\t-l look (hostname)\n"
-# print "\t-c copy (hostname)\n"
+ print("\nUsage: hostbase.py -flag (hostname)\n")
+ print("Flags:")
+ print("\t-l look (hostname)\n")
+# print("\t-c copy (hostname)\n")
sys.exit()
try:
host = Host.objects.get(hostname=hosttouse)
except:
- print "Error: host %s not in hostbase" % hosttouse
+ print("Error: host %s not in hostbase" % hosttouse)
sys.exit(1)
interfaces = []
for interface in host.interface_set.all():
@@ -53,7 +67,7 @@ for interface in interfaces:
if opts[0][0] == '-l':
"""Displays general host information"""
- print hostinfo
+ print(hostinfo)
if opts[0][0] == '-c':
"""Provides pre-filled template to copy a host record"""
diff --git a/tools/hostbasepush.py b/tools/hostbasepush.py
index 61f2e046b..070711c82 100755
--- a/tools/hostbasepush.py
+++ b/tools/hostbasepush.py
@@ -2,15 +2,15 @@
__revision__ = "$Revision: $"
-import Bcfg2.Client.Proxy, os
+import os
+import Bcfg2.Client.Proxy
if not os.getuid() == 0:
- print 'this command must be run as root'
+ print("this command must be run as root")
raise SystemExit
proxy = Bcfg2.Client.Proxy.bcfg2()
-print 'building files...'
+print("building files...")
proxy.run_method('Hostbase.rebuildState', ())
-print 'running bcfg...'
+print("running bcfg...")
os.system('bcfg2 -q -d -v')
-
diff --git a/tools/hostinfo.py b/tools/hostinfo.py
index d2b3628a3..8ae5c4df6 100755
--- a/tools/hostinfo.py
+++ b/tools/hostinfo.py
@@ -12,9 +12,14 @@ host_attribs = ["hostname", "whatami", "netgroup", "security_class",
"support", "csi", "memory", "printq", "dhcp", "outbound_smtp",
"primary_user", "administrator", "location",
"comments", "last", "expiration_date"]
-dispatch = {'mac_addr':' i.', 'hdwr_type':' i.', 'ip_addr':' p.',
- 'name':' n.', 'dns_view':' n.',
- 'cname':' c.', 'mx':' m.', 'priority':' m.'}
+dispatch = {'mac_addr': ' i.',
+ 'hdwr_type': ' i.',
+ 'ip_addr': ' p.',
+ 'name': ' n.',
+ 'dns_view': ' n.',
+ 'cname': ' c.',
+ 'mx': ' m.',
+ 'priority': ' m.'}
def pinger(hosts):
@@ -25,6 +30,7 @@ def pinger(hosts):
system("fping -r 1" + hostnames)
sys.exit()
+
def get_query(arguments):
"""Parses the command line options and returns the necessary
data for an SQL query"""
@@ -51,28 +57,36 @@ def get_query(arguments):
operator = "<>"
querysplit = arguments[querypos].split("==")
if querysplit[0] in host_attribs:
- querystring = " h.%s%s\'%s\'" % (querysplit[0], operator, querysplit[1])
+ querystring = " h.%s%s\'%s\'" % (querysplit[0],
+ operator,
+ querysplit[1])
elif querysplit[0] in dispatch:
querystring = dispatch[querysplit[0]]
- querystring += "%s%s\'%s\'" % (querysplit[0], operator, querysplit[1])
+ querystring += "%s%s\'%s\'" % (querysplit[0],
+ operator,
+ querysplit[1])
elif len(arguments[querypos].split("=")) > 1:
notstring = ''
if notflag:
notstring = 'NOT '
querysplit = arguments[querypos].split("=")
if querysplit[0] in host_attribs:
- querystring = " h.%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0], notstring, querysplit[1])
+ querystring = " h.%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0],
+ notstring,
+ querysplit[1])
elif querysplit[0] in dispatch:
querystring = dispatch[querysplit[0]]
- querystring += "%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0], notstring, querysplit[1])
+ querystring += "%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0],
+ notstring,
+ querysplit[1])
else:
- print "ERROR: bad query format"
+ print("ERROR: bad query format")
sys.exit()
if not querystring:
- print "ERROR: bad query format"
+ print("ERROR: bad query format")
sys.exit()
resultset.append((querystring, logic))
- arguments = arguments[querypos+1:]
+ arguments = arguments[querypos + 1:]
if arguments == [] or arguments[0] not in logic_ops:
break
return resultset
@@ -82,12 +96,12 @@ try:
'q:', ['showfields', 'fields', 'ping', 'summary'])
cursor = connection.cursor()
if ('--showfields', '') in opts:
- print "\nhost fields:\n"
+ print("\nhost fields:\n")
for field in host_attribs:
- print field
+ print(field)
for field in dispatch:
- print field
- print ''
+ print(field)
+ print("")
sys.exit()
if opts[0][0] == '-q':
results = get_query(sys.argv[2:])
@@ -113,19 +127,19 @@ try:
cursor.execute(query)
results = cursor.fetchall()
if not results:
- print "No matches were found for your query"
+ print("No matches were found for your query")
sys.exit()
- print '\n%-32s %-10s %-10s %-10s' % ('Hostname', 'Type', 'Location', 'User')
- print '================================ ========== ========== =========='
+ print("\n%-32s %-10s %-10s %-10s" % ('Hostname', 'Type', 'Location', 'User'))
+ print("================================ ========== ========== ==========")
for host in results:
- print '%-32s %-10s %-10s %-10s' % (host)
- print ''
+ print("%-32s %-10s %-10s %-10s" % (host))
+ print("")
elif ('--fields', '') in opts:
tolook = [arg for arg in args if arg in host_attribs or arg in dispatch]
fields = ""
fields = ", ".join(tolook)
if not fields:
- print "No valid fields were entered. exiting..."
+ print("No valid fields were entered. exiting...")
sys.exit()
query = """SELECT DISTINCT %s FROM (((((hostbase_host h
INNER JOIN hostbase_interface i ON h.id = i.host_id)
@@ -137,19 +151,18 @@ try:
WHERE %s ORDER BY h.hostname
""" % (fields, queryoptions)
-
cursor.execute(query)
results = cursor.fetchall()
last = results[0]
for field in results[0]:
- print repr(field) + "\t",
+ print(repr(field) + "\t")
for host in results:
if not host == last:
for field in host:
- print repr(field) + "\t",
+ print(repr(field) + "\t")
last = host
- print ''
+ print("")
else:
basequery = """SELECT DISTINCT h.hostname FROM (((((hostbase_host h
INNER JOIN hostbase_interface i ON h.id = i.host_id)
@@ -164,21 +177,21 @@ try:
results = cursor.fetchall()
if not results:
- print "No matches were found for your query"
+ print("No matches were found for your query")
sys.exit()
if ("--ping", '') in opts:
pinger(results)
for host in results:
- print host[0]
+ print(host[0])
+
-
except (GetoptError, IndexError):
- print "\nUsage: hostinfo.py -q <field>=[=]<value> [and/or <field>=<value> [--long option]]"
- print " hostinfo.py --showfields\tshows all data fields"
- print "\n long options:"
- print "\t --fields f1 f2 ...\tspecifies the fields displayed from the queried hosts"
- print "\t --summary\t\tprints out a predetermined set of fields"
- print "\t --ping\t\t\tuses fping to ping all queried hosts\n"
+ print("\nUsage: hostinfo.py -q <field>=[=]<value> [and/or <field>=<value> [--long option]]")
+ print(" hostinfo.py --showfields\tshows all data fields")
+ print("\n long options:")
+ print("\t --fields f1 f2 ...\tspecifies the fields displayed from the queried hosts")
+ print("\t --summary\t\tprints out a predetermined set of fields")
+ print("\t --ping\t\t\tuses fping to ping all queried hosts\n")
sys.exit()
diff --git a/tools/nagiosgen-convert.py b/tools/nagiosgen-convert.py
new file mode 100755
index 000000000..2c2142735
--- /dev/null
+++ b/tools/nagiosgen-convert.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import lxml.etree
+
+import Bcfg2.Options
+
+def main():
+ opts = {'repo': Bcfg2.Options.SERVER_REPOSITORY}
+ setup = Bcfg2.Options.OptionParser(opts)
+ setup.parse(sys.argv[1:])
+ repo = setup['repo']
+ oldconfigfile = os.path.join(repo, 'Properties', 'NagiosGen.xml')
+ newconfigpath = os.path.join(repo, 'NagiosGen')
+ newconfigfile = os.path.join(newconfigpath, 'config.xml')
+ parentsfile = os.path.join(newconfigpath, 'parents.xml')
+
+ if not os.path.exists(oldconfigfile):
+ print("%s does not exist, nothing to do" % oldconfigfile)
+ return 1
+
+ if not os.path.exists(newconfigpath):
+ print("%s does not exist, cannot write %s" %
+ (newconfigpath, newconfigfile))
+ return 2
+
+ newconfig = lxml.etree.XML("<NagiosGen/>")
+
+ oldconfig = lxml.etree.parse(oldconfigfile)
+ for host in oldconfig.getroot().getchildren():
+ if host.tag == lxml.etree.Comment:
+ # skip comments
+ continue
+
+ if host.tag == 'default':
+ print("default tag will not be converted; use a suitable Group tag instead")
+ continue
+
+ newhost = lxml.etree.Element("Client", name=host.tag)
+ for opt in host:
+ newopt = lxml.etree.Element("Option", name=opt.tag)
+ newopt.text = opt.text
+ newhost.append(newopt)
+ newconfig.append(newhost)
+
+ # parse the parents config, if it exists
+ if os.path.exists(parentsfile):
+ parentsconfig = lxml.etree.parse(parentsfile)
+ for el in parentsconfig.xpath("//Depend"):
+ newhost = newconfig.find("Client[@name='%s']" % el.get("name"))
+ if newhost is not None:
+ newparents = newhost.find("Option[@name='parents']")
+ if newparents is not None:
+ newparents.text += "," + el.get("on")
+ else:
+ newparents = lxml.etree.Element("Option", name="parents")
+ newparents.text = el.get("on")
+ newhost.append(newparents)
+ else:
+ newhost = lxml.etree.Element("Client", name=el.get("name"))
+ newparents = lxml.etree.Element("Option", name="parents")
+ newparents.text = el.get("on")
+ newhost.append(newparents)
+ newconfig.append(newhost)
+
+ try:
+ open(newconfigfile, 'w').write(lxml.etree.tostring(newconfig,
+ pretty_print=True))
+ print("%s written" % newconfigfile)
+ except IOError:
+ print("Failed to write %s" % newconfigfile)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/pkgmgr_gen.py b/tools/pkgmgr_gen.py
index d318e46c0..03d36dfc0 100755
--- a/tools/pkgmgr_gen.py
+++ b/tools/pkgmgr_gen.py
@@ -1,40 +1,45 @@
#!/usr/bin/python
-""" Program to generate a bcfg2 Pkgmgr configuration file from a list
- of directories that contain RPMS.
+"""Program to generate a bcfg2 Pkgmgr configuration file from a list
+ of directories that contain RPMS.
- All versions or only the latest may be included in the output.
- rpm.labelCompare is used to compare the package versions, so that
- a proper rpm version comparison is done (epoch:version-release).
+ All versions or only the latest may be included in the output.
+ rpm.labelCompare is used to compare the package versions, so that
+ a proper rpm version comparison is done (epoch:version-release).
- The output file may be formated for use with the RPM or Yum
- bcfg2 client drivers. The output can also contain the PackageList
- and nested group headers.
+ The output file may be formated for use with the RPM or Yum
+ bcfg2 client drivers. The output can also contain the PackageList
+ and nested group headers.
"""
__revision__ = '$Revision: $'
-import sys
-import os
-import rpm
-import optparse
+import collections
import datetime
import glob
-import urllib
import gzip
-import urlparse
+import optparse
+import os
+import rpm
+import sys
from lxml.etree import parse
import xml.sax
from xml.sax.handler import ContentHandler
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import urljoin
+
+
def info(object, spacing=10, collapse=1):
"""Print methods and doc strings.
Takes module, class, list, dictionary, or string.
"""
methodList = [method for method in dir(object)
- if callable(getattr(object, method))]
+ if isinstance(getattr(object, method),
+ collections.Callable)]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
- print "\n".join(["%s %s" %
+ print("\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
- for method in methodList])
+ for method in methodList]))
+
def readRpmHeader(ts, filename):
"""
@@ -43,19 +48,21 @@ def readRpmHeader(ts, filename):
try:
fd = os.open(filename, os.O_RDONLY)
except:
- print 'Failed to open RPM file %s' % filename
+ print("Failed to open RPM file %s" % filename)
h = ts.hdrFromFdno(fd)
os.close(fd)
return h
+
def sortedDictValues(adict):
"""
Sort a dictionary by its keys and return the items in sorted key order.
"""
- keys = adict.keys()
+ keys = list(adict.keys())
keys.sort()
- return map(adict.get, keys)
+ return list(map(adict.get, keys))
+
def cmpRpmHeader(a, b):
"""
@@ -79,6 +86,7 @@ def cmpRpmHeader(a, b):
ret = rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
return ret
+
def loadRpms(dirs):
"""
dirs is a list of directories to search for rpms.
@@ -114,18 +122,18 @@ def loadRpms(dirs):
for dir in dirs:
if options.verbose:
- print 'Scanning directory: %s' % dir
+ print("Scanning directory: %s" % dir)
for file in [files for files in os.listdir(dir)
if files.endswith('.rpm')]:
- filename = os.path.join( dir, file )
+ filename = os.path.join(dir, file)
# Get the mtime of the RPM file.
file_mtime = datetime.date.fromtimestamp(os.stat(filename).st_mtime)
# Get the RPM header
- header = readRpmHeader( ts, filename )
+ header = readRpmHeader(ts, filename)
# Get what we are interesting in out of the header.
name = header[rpm.RPMTAG_NAME]
@@ -138,9 +146,13 @@ def loadRpms(dirs):
if subarch in subarchs or 'all' in subarchs:
# Store what we want in our structure.
- packages.setdefault(name, []).append({'filename':file, 'mtime':file_mtime, 'name':name, \
- 'arch':subarch, 'epoch':epoch, 'version':version, \
- 'release':release})
+ packages.setdefault(name, []).append({'filename': file,
+ 'mtime': file_mtime,
+ 'name': name,
+ 'arch': subarch,
+ 'epoch': epoch,
+ 'version': version,
+ 'release': release})
# Print '.' for each package. stdio is line buffered, so have to flush it.
if options.verbose:
@@ -151,6 +163,7 @@ def loadRpms(dirs):
return packages
+
class pkgmgr_URLopener(urllib.FancyURLopener):
"""
Override default error handling so that we can see what the errors are.
@@ -159,57 +172,62 @@ class pkgmgr_URLopener(urllib.FancyURLopener):
"""
Override default error handling so that we can see what the errors are.
"""
- print "ERROR %s: Unable to retrieve %s" % (errcode, url)
+ print("ERROR %s: Unable to retrieve %s" % (errcode, url))
+
class PrimaryParser(ContentHandler):
def __init__(self, packages):
- self.inPackage = 0
- self.inName = 0
- self.inArch = 0
- self.packages = packages
+ self.inPackage = 0
+ self.inName = 0
+ self.inArch = 0
+ self.packages = packages
def startElement(self, name, attrs):
- if name == "package":
- self.package = {'file': None, 'name': '', 'subarch': '',
- 'epoch': None, 'version': None, 'release': None}
- self.inPackage = 1
- elif self.inPackage:
- if name == "name":
- self.inName = 1
- elif name == "arch":
- self.inArch = 1
- elif name == "version":
- self.package['epoch'] = attrs.getValue('epoch')
- self.package['version'] = attrs.getValue('ver')
- self.package['release'] = attrs.getValue('rel')
- elif name == "location":
- self.package['file'] = attrs.getValue('href')
+ if name == "package":
+ self.package = {'file': None, 'name': '', 'subarch': '',
+ 'epoch': None, 'version': None, 'release': None}
+ self.inPackage = 1
+ elif self.inPackage:
+ if name == "name":
+ self.inName = 1
+ elif name == "arch":
+ self.inArch = 1
+ elif name == "version":
+ self.package['epoch'] = attrs.getValue('epoch')
+ self.package['version'] = attrs.getValue('ver')
+ self.package['release'] = attrs.getValue('rel')
+ elif name == "location":
+ self.package['file'] = attrs.getValue('href')
def endElement(self, name):
- if name == "package":
- self.inPackage = 0
- # Only load RPMs with subarchitectures as calculated from the --archs option.
- if self.package['subarch'] in subarchs or 'all' in subarchs:
- self.packages.setdefault(self.package['name'], []).append(
- {'filename':self.package['file'], 'name':self.package['name'],
- 'arch':self.package['subarch'], 'epoch':self.package['epoch'],
- 'version':self.package['version'], 'release':self.package['release']})
- # Print '.' for each package. stdio is line buffered, so have to flush it.
- if options.verbose:
- sys.stdout.write('.')
- sys.stdout.flush()
- elif self.inPackage:
- if name == "name":
- self.inName = 0
- elif name == "arch":
- self.inArch = 0
+ if name == "package":
+ self.inPackage = 0
+ # Only load RPMs with subarchitectures as calculated from the --archs option.
+ if self.package['subarch'] in subarchs or 'all' in subarchs:
+ self.packages.setdefault(self.package['name'], []).append(
+ {'filename': self.package['file'],
+ 'name': self.package['name'],
+ 'arch': self.package['subarch'],
+ 'epoch': self.package['epoch'],
+ 'version': self.package['version'],
+ 'release': self.package['release']})
+ # Print '.' for each package. stdio is line buffered, so have to flush it.
+ if options.verbose:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ elif self.inPackage:
+ if name == "name":
+ self.inName = 0
+ elif name == "arch":
+ self.inArch = 0
def characters(self, content):
- if self.inPackage:
- if self.inName:
- self.package['name'] += content
- if self.inArch:
- self.package['subarch'] += content
+ if self.inPackage:
+ if self.inName:
+ self.package['name'] += content
+ if self.inArch:
+ self.package['subarch'] += content
+
def loadRepos(repolist):
'''
@@ -239,10 +257,10 @@ def loadRepos(repolist):
'''
packages = {}
for repo in repolist:
- url = urlparse.urljoin(repo, './repodata/repomd.xml')
+ url = urljoin(repo, './repodata/repomd.xml')
if options.verbose:
- print 'Loading repo metadata : %s' % url
+ print("Loading repo metadata : %s" % url)
try:
opener = pkgmgr_URLopener()
@@ -253,7 +271,7 @@ def loadRepos(repolist):
try:
tree = parse(file)
except IOError:
- print "ERROR: Unable to parse retrieved repomd.xml."
+ print("ERROR: Unable to parse retrieved repomd.xml.")
sys.exit()
repomd = tree.getroot()
@@ -263,10 +281,10 @@ def loadRepos(repolist):
if property.tag.endswith('location'):
primaryhref = property.get('href')
- url = urlparse.urljoin(repo, './' + primaryhref)
+ url = urljoin(repo, './' + primaryhref)
if options.verbose:
- print 'Loading : %s' % url
+ print("Loading : %s" % url)
try:
opener = pkgmgr_URLopener()
@@ -277,7 +295,7 @@ def loadRepos(repolist):
try:
repo_file = gzip.open(file)
except IOError:
- print "ERROR: Unable to parse retrieved file."
+ print("ERROR: Unable to parse retrieved file.")
sys.exit()
parser = xml.sax.make_parser()
@@ -289,6 +307,7 @@ def loadRepos(repolist):
repo_file.close()
return packages
+
def printInstance(instance, group_count):
"""
Print the details for a package instance with the appropriate indentation and
@@ -303,16 +322,17 @@ def printInstance(instance, group_count):
output_line = ''
if options.format == 'rpm':
- output_line = '%s<Instance simplefile=\'%s\' ' % ( indent * group_count , instance['filename'])
+ output_line = '%s<Instance simplefile=\'%s\' ' % (indent * group_count, instance['filename'])
else:
output_line = '%s<Instance ' % (indent * group_count)
if epoch:
output_line += 'epoch=\'%s\' ' % (epoch)
- output_line += 'version=\'%s\' release=\'%s\' arch=\'%s\'/>\n' % ( version, release, arch)
+ output_line += 'version=\'%s\' release=\'%s\' arch=\'%s\'/>\n' % (version, release, arch)
output.write(output_line)
+
def printPackage(entry, group_count):
"""
Print the details of a package with the appropriate indentation.
@@ -321,7 +341,7 @@ def printPackage(entry, group_count):
entry is a single package entry as created in loadRpms().
"""
output.write('%s<Package name=\'%s\' type=\'%s\'>\n' \
- % ( group_count * indent, entry[0]['name'], options.format) )
+ % (group_count * indent, entry[0]['name'], options.format))
subarch_dict = {}
arch_dict = {}
@@ -333,17 +353,17 @@ def printPackage(entry, group_count):
if instance['arch'] in subarch_dict:
subarch_dict[instance['arch']].append(instance)
else:
- subarch_dict[instance['arch']] = [ instance ]
+ subarch_dict[instance['arch']] = [instance]
# Keep track of the subarchitectures we have found in each architecture.
if subarch_mapping[instance['arch']] in arch_dict:
if instance['arch'] not in arch_dict[subarch_mapping[instance['arch']]]:
arch_dict[subarch_mapping[instance['arch']]].append(instance['arch'])
else:
- arch_dict[subarch_mapping[instance['arch']]] = [ instance['arch'] ]
+ arch_dict[subarch_mapping[instance['arch']]] = [instance['arch']]
# Only keep the 'highest' subarchitecture in each architecture.
- for arch in arch_dict.iterkeys():
+ for arch in list(arch_dict.keys()):
if len(arch_dict[arch]) > 1:
arch_dict[arch].sort()
for s in arch_dict[arch][:-1]:
@@ -361,12 +381,13 @@ def printPackage(entry, group_count):
# Output the latest
printInstance(subarch_dict[arch][-1], group_count)
- output.write('%s</Package>\n' % ( group_count * indent ) )
+ output.write('%s</Package>\n' % (group_count * indent))
+
def main():
if options.verbose:
- print 'Loading package headers'
+ print("Loading package headers")
if options.rpmdirs:
package_dict = loadRpms(search_dirs)
@@ -374,18 +395,18 @@ def main():
package_dict = loadRepos(repos)
if options.verbose:
- print 'Processing package headers'
+ print("Processing package headers")
if options.pkgmgrhdr:
if options.format == "rpm":
- output.write("<PackageList uri='%s' priority='%s' type='rpm'>\n" % ( options.uri, options.priority ))
+ output.write("<PackageList uri='%s' priority='%s' type='rpm'>\n" % (options.uri, options.priority))
else:
- output.write("<PackageList priority='%s' type='yum'>\n" %( options.priority ))
+ output.write("<PackageList priority='%s' type='yum'>\n" % (options.priority))
group_count = 1
if groups_list:
for group in groups_list:
- output.write("%s<Group name='%s'>\n" % ( indent * group_count , group ) )
+ output.write("%s<Group name='%s'>\n" % (indent * group_count, group))
group_count = group_count + 1
# Process packages in name order
@@ -395,14 +416,14 @@ def main():
if groups_list:
group_count = group_count - 1
while group_count:
- output.write( '%s</Group>\n' % ( indent * group_count ) )
+ output.write('%s</Group>\n' % (indent * group_count))
group_count = group_count - 1
if options.pkgmgrhdr:
- output.write( '</PackageList>\n' )
+ output.write('</PackageList>\n')
if options.verbose:
- print '%i package instances were processed' % len(package_dict)
+ print("%i package instances were processed" % len(package_dict))
if __name__ == "__main__":
@@ -434,7 +455,7 @@ if __name__ == "__main__":
p.add_option('--format', '-f', action='store', \
default='yum', \
type='choice', \
- choices=('yum','rpm'), \
+ choices=('yum', 'rpm'), \
help='''Format of the Output. Choices are yum or rpm.
(Default: yum)
''')
@@ -469,7 +490,7 @@ if __name__ == "__main__":
p.add_option('--release', '-r', action='store', \
default='latest', \
type='choice', \
- choices=('all','latest'), \
+ choices=('all', 'latest'), \
help='''Which releases to include in the output. Choices are
all or latest. (Default: latest).''')
@@ -492,11 +513,12 @@ if __name__ == "__main__":
options, arguments = p.parse_args()
if options.pkgmgrhdr and options.format == 'rpm' and not options.uri:
- print "Option --uri must be specified to produce a PackageList Tag for rpm formatted files."
+ print("Option --uri must be specified to produce a PackageList Tag "
+ "for rpm formatted files.")
sys.exit(1)
if not options.rpmdirs and not options.yumrepos:
- print "One of --rpmdirs and --yumrepos must be specified"
+ print("One of --rpmdirs and --yumrepos must be specified")
sys.exit(1)
# Set up list of directories to search
@@ -505,9 +527,9 @@ if __name__ == "__main__":
for d in options.rpmdirs.split(','):
search_dirs += glob.glob(d)
if options.verbose:
- print 'The following directories will be scanned:'
+ print("The following directories will be scanned:")
for d in search_dirs:
- print ' %s' % d
+ print(" %s" % d)
# Setup list of repos
if options.yumrepos:
@@ -515,26 +537,30 @@ if __name__ == "__main__":
for r in options.yumrepos.split(','):
repos.append(r)
if options.verbose:
- print 'The following repositories will be scanned:'
+ print("The following repositories will be scanned:")
for d in repos:
- print ' %s' % d
+ print(" %s" % d)
# Set up list of architectures to include and some mappings
# to use later.
- arch_mapping = {'x86':['i686', 'i586', 'i486', 'i386', 'athlon'],
- 'x86_64':['x86_64'],
- 'ia64':['ia64'],
- 'ppc':['ppc'],
- 'ppc64':['ppc64'],
- 'sparc':['sparc'],
- 'noarch':['noarch']}
- subarch_mapping = {'i686':'x86', 'i586':'x86', 'i486':'x86', 'i386':'x86', 'athlon':'x86',
- 'x86_64':'x86_64',
- 'ia64':'ia64',
- 'ppc':'ppc',
- 'ppc64':'ppc64',
- 'sparc':'sparc',
- 'noarch':'noarch' }
+ arch_mapping = {'x86': ['i686', 'i586', 'i486', 'i386', 'athlon'],
+ 'x86_64': ['x86_64'],
+ 'ia64': ['ia64'],
+ 'ppc': ['ppc'],
+ 'ppc64': ['ppc64'],
+ 'sparc': ['sparc'],
+ 'noarch': ['noarch']}
+ subarch_mapping = {'i686': 'x86',
+ 'i586': 'x86',
+ 'i486': 'x86',
+ 'i386': 'x86',
+ 'athlon': 'x86',
+ 'x86_64': 'x86_64',
+ 'ia64': 'ia64',
+ 'ppc': 'ppc',
+ 'ppc64': 'ppc64',
+ 'sparc': 'sparc',
+ 'noarch': 'noarch'}
commandline_subarchs = options.archs.split(',')
arch_list = []
subarchs = []
@@ -543,7 +569,7 @@ if __name__ == "__main__":
else:
for s in commandline_subarchs:
if s not in subarch_mapping:
- print 'Error: Invalid subarchitecture specified: ', s
+ print("Error: Invalid subarchitecture specified: ", s)
sys.exit(1)
# Only allow one subarchitecture per architecture to be specified.
if s not in arch_list:
@@ -556,7 +582,8 @@ if __name__ == "__main__":
#if i != len(arch_mapping[subarch_mapping[s]]):
subarchs += arch_mapping[subarch_mapping[s]][i:]
else:
- print 'Error: Multiple subarchitecutes of the same architecture specified.'
+ print("Error: Multiple subarchitecutes of the same "
+ "architecture specified.")
sys.exit(1)
indent = ' ' * options.indent
@@ -572,4 +599,3 @@ if __name__ == "__main__":
output = sys.stdout
main()
-
diff --git a/tools/pkgmgr_update.py b/tools/pkgmgr_update.py
index 3d13b8e4a..05d645786 100755
--- a/tools/pkgmgr_update.py
+++ b/tools/pkgmgr_update.py
@@ -13,24 +13,38 @@
__version__ = '0.1'
-import sys
-import os
-import rpm
-import optparse
import datetime
import glob
+import gzip
+import optparse
+import os
+import rpm
+import sys
+
+# Compatibility imports
+from Bcfg2.Bcfg2Py3k import urljoin
+
try:
- from lxml.etree import parse, XML, tostring
+ from lxml.etree import parse, tostring
except:
- from elementtree.ElementTree import parse, XML, tostring
-import urlparse, urllib, gzip
-
-installOnlyPkgs = ['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp',
- 'kernel-modules', 'kernel-debug', 'kernel-unsupported',
- 'kernel-source', 'kernel-devel', 'kernel-default',
- 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen',
+ from elementtree.ElementTree import parse, tostring
+
+installOnlyPkgs = ['kernel',
+ 'kernel-bigmem',
+ 'kernel-enterprise',
+ 'kernel-smp',
+ 'kernel-modules',
+ 'kernel-debug',
+ 'kernel-unsupported',
+ 'kernel-source',
+ 'kernel-devel',
+ 'kernel-default',
+ 'kernel-largesmp-devel',
+ 'kernel-largesmp',
+ 'kernel-xen',
'gpg-pubkey']
+
def readRpmHeader(ts, filename):
"""
Read an rpm header from an RPM file.
@@ -38,19 +52,21 @@ def readRpmHeader(ts, filename):
try:
fd = os.open(filename, os.O_RDONLY)
except:
- print 'Failed to open RPM file %s' % filename
+ print("Failed to open RPM file %s" % filename)
h = ts.hdrFromFdno(fd)
os.close(fd)
return h
+
def sortedDictValues(adict):
"""
Sort a dictionary by its keys and return the items in sorted key order.
"""
- keys = adict.keys()
+ keys = list(adict.keys())
keys.sort()
- return map(adict.get, keys)
+ return list(map(adict.get, keys))
+
def cmpRpmHeader(a, b):
"""
@@ -67,6 +83,7 @@ def cmpRpmHeader(a, b):
return rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
+
def loadRpms(dirs):
"""
dirs is a list of directories to search for rpms.
@@ -103,18 +120,18 @@ def loadRpms(dirs):
for dir in dirs:
if options.verbose:
- print 'Scanning directory: %s' % dir
+ print("Scanning directory: %s" % dir)
for file in [files for files in os.listdir(dir)
if files.endswith('.rpm')]:
- filename = os.path.join( dir, file )
+ filename = os.path.join(dir, file)
# Get the mtime of the RPM file.
file_mtime = datetime.date.fromtimestamp(os.stat(filename).st_mtime)
# Get the RPM header
- header = readRpmHeader( ts, filename )
+ header = readRpmHeader(ts, filename)
# Get what we are interesting in out of the header.
name = header[rpm.RPMTAG_NAME]
@@ -124,9 +141,13 @@ def loadRpms(dirs):
subarch = header[rpm.RPMTAG_ARCH]
if name not in installOnlyPkgs:
- packages.setdefault(name, {}).setdefault(subarch, []).append({'filename':file, \
- 'mtime':file_mtime, 'name':name, 'arch':subarch, \
- 'epoch':epoch, 'version':version, 'release':release})
+ packages.setdefault(name, {}).setdefault(subarch, []).append({'filename': file,
+ 'mtime': file_mtime,
+ 'name': name,
+ 'arch': subarch,
+ 'epoch': epoch,
+ 'version': version,
+ 'release': release})
if options.verbose:
sys.stdout.write('.')
sys.stdout.flush()
@@ -135,6 +156,7 @@ def loadRpms(dirs):
return packages
+
class pkgmgr_URLopener(urllib.FancyURLopener):
"""
Override default error handling so that we can see what the errors are.
@@ -143,7 +165,8 @@ class pkgmgr_URLopener(urllib.FancyURLopener):
"""
Override default error handling so that we can see what the errors are.
"""
- print "ERROR %s: Unable to retrieve %s" % (errcode, url)
+ print("ERROR %s: Unable to retrieve %s" % (errcode, url))
+
def loadRepos(repolist):
"""
@@ -175,18 +198,18 @@ def loadRepos(repolist):
"""
packages = {}
for repo in repolist:
- url = urlparse.urljoin(repo, './repodata/repomd.xml')
+ url = urljoin(repo, './repodata/repomd.xml')
try:
opener = pkgmgr_URLopener()
file, message = opener.retrieve(url)
except:
- sys.exit();
+ sys.exit()
try:
tree = parse(file)
except IOError:
- print "ERROR: Unable to parse retrieved repomd.xml."
+ print("ERROR: Unable to parse retrieved repomd.xml.")
sys.exit()
repomd = tree.getroot()
@@ -196,10 +219,10 @@ def loadRepos(repolist):
if property.tag.endswith('location'):
primaryhref = property.attrib['href']
- url = urlparse.urljoin(repo, './' + primaryhref)
+ url = urljoin(repo, './' + primaryhref)
if options.verbose:
- print 'Loading : %s' % url
+ print("Loading : %s" % url)
try:
opener = pkgmgr_URLopener()
@@ -211,7 +234,7 @@ def loadRepos(repolist):
repo_file = gzip.open(file)
tree = parse(repo_file)
except IOError:
- print "ERROR: Unable to parse retrieved file."
+ print("ERROR: Unable to parse retrieved file.")
sys.exit()
root = tree.getroot()
@@ -230,9 +253,12 @@ def loadRepos(repolist):
file = property.get('href')
if name not in installOnlyPkgs:
- packages.setdefault(name, {}).setdefault(subarch, []).append({'filename':file, \
- 'name':name, 'arch':subarch, \
- 'epoch':epoch, 'version':version, 'release':release})
+ packages.setdefault(name, {}).setdefault(subarch, []).append({'filename': file,
+ 'name': name,
+ 'arch': subarch,
+ 'epoch': epoch,
+ 'version': version,
+ 'release': release})
if options.verbose:
sys.stdout.write('.')
sys.stdout.flush()
@@ -241,6 +267,7 @@ def loadRepos(repolist):
return packages
+
def str_evra(instance):
"""
Convert evra dict entries to a string.
@@ -252,6 +279,7 @@ def str_evra(instance):
return '%s:%s-%s.%s' % (instance.get('epoch', '*'), instance.get('version', '*'),
instance.get('release', '*'), instance.get('arch', '*'))
+
def updatepkg(pkg):
"""
"""
@@ -266,8 +294,9 @@ def updatepkg(pkg):
latest = package_dict[name][arch][-1]
if cmpRpmHeader(inst, latest) == -1:
if options.verbose:
- print 'Found newer version of package %s' % name
- print ' Updating %s to %s' % (str_evra(inst), str_evra(latest))
+ print("Found newer version of package %s" % name)
+ print(" Updating %s to %s" % (str_evra(inst),
+ str_evra(latest)))
if latest['epoch'] != None:
inst.attrib['epoch'] = str(latest['epoch'])
inst.attrib['version'] = latest['version']
@@ -279,30 +308,32 @@ def updatepkg(pkg):
# if we find Ignore tags, then assume they're correct;
# otherwise, check the altconfigfile
if not ignoretags:
- altpkgs = alttree.xpath(".//Package[@name='%s'][Ignore]"%name)
+ altpkgs = alttree.xpath(".//Package[@name='%s'][Ignore]" % name)
if (len(altpkgs) == 1):
for ignoretag in altpkgs[0].xpath(".//Ignore"):
if options.verbose:
print(" Found Ignore tag in altconfigfile for package %s" % name)
pkg.append(ignoretag)
+
def main():
global package_dict
global alttree
if options.verbose:
- print 'Loading Pkgmgr config file %s.' % (options.configfile)
+ print("Loading Pkgmgr config file %s." % (options.configfile))
tree = parse(options.configfile)
config = tree.getroot()
if options.altconfigfile:
if options.verbose:
- print 'Loading Pkgmgr alternate config file %s.' % (options.altconfigfile)
+ print("Loading Pkgmgr alternate config file %s." %
+ (options.altconfigfile))
alttree = parse(options.altconfigfile)
if options.verbose:
- print 'Loading package headers'
+ print("Loading package headers")
if options.rpmdirs:
package_dict = loadRpms(search_dirs)
@@ -310,7 +341,7 @@ def main():
package_dict = loadRepos(repos)
if options.verbose:
- print 'Processing package headers'
+ print("Processing package headers")
for pkg in config.getiterator('Package'):
updatepkg(pkg)
@@ -348,11 +379,12 @@ if __name__ == "__main__":
options, arguments = p.parse_args()
if not options.configfile:
- print "An existing Pkgmgr configuration file must be specified with the -c option."
+ print("An existing Pkgmgr configuration file must be specified with "
+ "the -c option.")
sys.exit()
if not options.rpmdirs and not options.yumrepos:
- print "One of --rpmdirs and --yumrepos must be specified"
+ print("One of --rpmdirs and --yumrepos must be specified")
sys.exit(1)
# Set up list of directories to search
@@ -361,9 +393,9 @@ if __name__ == "__main__":
for d in options.rpmdirs.split(','):
search_dirs += glob.glob(d)
if options.verbose:
- print 'The following directories will be scanned:'
+ print("The following directories will be scanned:")
for d in search_dirs:
- print ' %s' % d
+ print(" %s" % d)
# Setup list of repos
if options.yumrepos:
@@ -371,9 +403,9 @@ if __name__ == "__main__":
for r in options.yumrepos.split(','):
repos.append(r)
if options.verbose:
- print 'The following repositories will be scanned:'
+ print("The following repositories will be scanned:")
for d in repos:
- print ' %s' % d
+ print(" %s" % d)
if options.outfile:
output = file(options.outfile, "w")
@@ -383,4 +415,3 @@ if __name__ == "__main__":
package_dict = {}
main()
-
diff --git a/tools/rpmlisting.py b/tools/rpmlisting.py
index 41b4772a0..141cae172 100644
--- a/tools/rpmlisting.py
+++ b/tools/rpmlisting.py
@@ -2,7 +2,7 @@
import os
import sys
-import commands
+import subprocess
import getopt
import re
import datetime
@@ -11,7 +11,7 @@ from socket import gethostname
def run_or_die(command):
"""run a command, returning output. raise an exception if it fails."""
- (status, stdio) = commands.getstatusoutput(command)
+ (status, stdio) = subprocess.getstatusoutput(command)
if status != 0:
raise Exception("command '%s' failed with exit status %d and output '%s'" %
(command, status, stdio))
@@ -62,11 +62,15 @@ def verstr_cmp(a, b):
else:
return len(a_parts) - len(b_parts)
return ret
-
-
+
def subdivide(verstr):
- """subdivide takes a version or release string and attempts to subdivide it into components to facilitate sorting. The string is divided into a two level hierarchy of sub-parts. The upper level is subdivided by periods, and the lower level is subdivided by boundaries between digit, alpha, and other character groupings."""
+ """subdivide takes a version or release string and attempts to subdivide
+ it into components to facilitate sorting. The string is divided into a
+ two level hierarchy of sub-parts. The upper level is subdivided by
+ periods, and the lower level is subdivided by boundaries between digit,
+ alpha, and other character groupings.
+ """
parts = []
# parts is a list of lists representing the subsections which make up a version string.
# example:
@@ -102,22 +106,39 @@ def subdivide(verstr):
return parts
-subarch_mapping = {'athlon':'x86', 'i686':'x86', 'i586':'x86', 'i486':'x86', 'i386':'x86', 'x86_64':'x86_64', 'noarch':'noarch'}
-arch_mapping = {'x86':['athlon','i686','i586','i486','i386'], 'x86_64':['x86_64'], 'noarch':['noarch']}
+subarch_mapping = {'athlon': 'x86',
+ 'i686': 'x86',
+ 'i586': 'x86',
+ 'i486': 'x86',
+ 'i386': 'x86',
+ 'x86_64': 'x86_64',
+ 'noarch': 'noarch'}
+arch_mapping = {'x86': ['athlon',
+ 'i686',
+ 'i586',
+ 'i486',
+ 'i386'],
+ 'x86_64': ['x86_64'],
+ 'noarch': ['noarch']}
def parse_rpm(path, filename):
- """read the name, version, release, and subarch of an rpm. this version reads the rpm headers."""
+ """read the name, version, release, and subarch of an rpm.
+ this version reads the rpm headers.
+ """
cmd = 'rpm --nosignature --queryformat \'%%{NAME} %%{VERSION} %%{RELEASE} %%{ARCH}\' -q -p %s/%s' % (path, filename)
output = run_or_die(cmd)
(name, version, release, subarch) = output.split()
- if subarch not in subarch_mapping.keys():
+ if subarch not in list(subarch_mapping.keys()):
raise Exception("%s/%s has invalid subarch %s" % (path, filename, subarch))
return (name, version, release, subarch)
-
+
def parse_rpm_filename(path, filename):
- """read the name, version, release, and subarch of an rpm. this version tries to parse the filename directly, and calls 'parse_rpm' as a fallback."""
+ """read the name, version, release, and subarch of an rpm.
+ this version tries to parse the filename directly, and calls
+ 'parse_rpm' as a fallback.
+ """
name, version, release, subarch = None, None, None, None
try:
(major, minor) = sys.version_info[:2]
@@ -129,7 +150,7 @@ def parse_rpm_filename(path, filename):
(blob, subarch, extension) = (rblob[::-1], rsubarch[::-1], rextension[::-1])
(rrelease, rversion, rname) = blob[::-1].split('-', 2)
(name, version, release) = (rname[::-1], rversion[::-1], rrelease[::-1])
- if subarch not in subarch_mapping.keys():
+ if subarch not in list(subarch_mapping.keys()):
raise "%s/%s has invalid subarch %s." % (path, filename, subarch)
except:
# for incorrectly named rpms (ie, sun's java rpms) we fall back to reading the rpm headers.
@@ -139,7 +160,9 @@ def parse_rpm_filename(path, filename):
def get_pkgs(rpmdir):
- """scan a dir of rpms and generate a pkgs structure. first try parsing the filename. if that fails, try parsing the rpm headers."""
+ """scan a dir of rpms and generate a pkgs structure. first try parsing
+ the filename. if that fails, try parsing the rpm headers.
+ """
pkgs = {}
"""
pkgs structure:
@@ -161,7 +184,11 @@ pkgs = {
rpms = [item for item in os.listdir(rpmdir) if item.endswith('.rpm')]
for filename in rpms:
(name, version, release, subarch) = parse_rpm_filename(rpmdir, filename)
- rpmblob = {'file':filename, 'name':name, 'version':version, 'release':release, 'subarch':subarch}
+ rpmblob = {'file': filename,
+ 'name': name,
+ 'version': version,
+ 'release': release,
+ 'subarch': subarch}
if name in pkgs:
pkgs[name].append(rpmblob)
else:
@@ -170,9 +197,11 @@ pkgs = {
def prune_pkgs_latest(pkgs):
- """prune a pkgs structure to contain only the latest version of each package (includes multiarch results)."""
+ """prune a pkgs structure to contain only the latest version
+ of each package (includes multiarch results).
+ """
latest_pkgs = {}
- for rpmblobs in pkgs.values():
+ for rpmblobs in list(pkgs.values()):
(major, minor) = sys.version_info[:2]
if major >= 2 and minor >= 4:
rpmblobs.sort(rpmblob_cmp, reverse=True)
@@ -180,16 +209,18 @@ def prune_pkgs_latest(pkgs):
rpmblobs.sort(rpmblob_cmp)
rpmblobs.reverse()
pkg_name = rpmblobs[0]['name']
- all_archs = [blob for blob in rpmblobs if blob['version'] == rpmblobs[0]['version'] and
+ all_archs = [blob for blob in rpmblobs if blob['version'] == rpmblobs[0]['version'] and
blob['release'] == rpmblobs[0]['release']]
latest_pkgs[pkg_name] = all_archs
return latest_pkgs
def prune_pkgs_archs(pkgs):
- """prune a pkgs structure to contain no more than one subarch per architecture for each set of packages."""
+ """prune a pkgs structure to contain no more than one subarch
+ per architecture for each set of packages.
+ """
pruned_pkgs = {}
- for rpmblobs in pkgs.values():
+ for rpmblobs in list(pkgs.values()):
pkg_name = rpmblobs[0]['name']
arch_sifter = {}
for challenger in rpmblobs:
@@ -203,13 +234,16 @@ def prune_pkgs_archs(pkgs):
incumbent_index = subarchs.index(incumbent['subarch'])
if challenger_index < incumbent_index:
arch_sifter[arch] = challenger
- pruned_pkgs[pkg_name] = arch_sifter.values()
+ pruned_pkgs[pkg_name] = list(arch_sifter.values())
return pruned_pkgs
def get_date_from_desc(date_desc):
- """calls the unix 'date' command to turn a date description into a python date object.
-example: get_date_from_desc("last sunday 1 week ago")"""
+ """calls the unix 'date' command to turn a date
+ description into a python date object.
+
+ example: get_date_from_desc("last sunday 1 week ago")
+ """
stdio = run_or_die('date -d "' + date_desc + '" "+%Y %m %d"')
(year_str, month_str, day_str) = stdio.split()
year = int(year_str)
@@ -225,7 +259,9 @@ def get_mtime_date(path):
def prune_pkgs_timely(pkgs, start_date_desc=None, end_date_desc=None, rpmdir='.'):
- """prune a pkgs structure to contain only rpms with an mtime within a certain temporal window."""
+ """prune a pkgs structure to contain only rpms with
+ an mtime within a certain temporal window.
+ """
start_date = None
if start_date_desc != None:
start_date = get_date_from_desc(start_date_desc)
@@ -235,7 +271,7 @@ def prune_pkgs_timely(pkgs, start_date_desc=None, end_date_desc=None, rpmdir='.'
if start_date == None and end_date == None:
return pkgs
if start_date != None:
- for rpmblobs in pkgs.values():
+ for rpmblobs in list(pkgs.values()):
pkg_name = rpmblobs[0]['name']
timely_blobs = [blob for blob in rpmblobs if start_date < get_mtime_date(rpmdir + '/' + blob['file'])]
if len(timely_blobs) == 0:
@@ -243,7 +279,7 @@ def prune_pkgs_timely(pkgs, start_date_desc=None, end_date_desc=None, rpmdir='.'
else:
pkgs[pkg_name] = timely_blobs
if end_date != None:
- for rpmblobs in pkgs.values():
+ for rpmblobs in list(pkgs.values()):
pkg_name = rpmblobs[0]['name']
timely_blobs = [blob for blob in rpmblobs if get_mtime_date(rpmdir + '/' + blob['file']) <= end_date]
if len(timely_blobs) == 0:
@@ -256,7 +292,7 @@ def prune_pkgs_timely(pkgs, start_date_desc=None, end_date_desc=None, rpmdir='.'
# from http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52306
def sorted_values(adict):
"""return a list of values from a dict, sorted by key."""
- items = adict.items()
+ items = list(adict.items())
items.sort()
return [value for key, value in items]
@@ -278,9 +314,9 @@ def scan_rpm_dir(rpmdir, uri, group, priority=0, output=sys.stdout, start_date_d
subarchs = [blob['subarch'] for blob in rpmblobs]
subarchs.sort()
multiarch_string = ' '.join(subarchs)
- pattern_string = '\.(%s)\.rpm$' % '|'.join(subarchs) # e.g., '\.(i386|x86_64)\.rpm$'
+ pattern_string = '\.(%s)\.rpm$' % '|'.join(subarchs) # e.g., '\.(i386|x86_64)\.rpm$'
pattern = re.compile(pattern_string)
- multiarch_file = pattern.sub('.%(arch)s.rpm', rpmblob['file']) # e.g., 'foo-1.0-1.%(arch)s.rpm'
+ multiarch_file = pattern.sub('.%(arch)s.rpm', rpmblob['file']) # e.g., 'foo-1.0-1.%(arch)s.rpm'
output.write(' <Package name="%s" file="%s" version="%s-%s" multiarch="%s"/>\n' %
(rpmblob['name'], multiarch_file, rpmblob['version'], rpmblob['release'], multiarch_string))
output.write(' </Group>\n')
@@ -300,7 +336,7 @@ if __name__ == "__main__":
sys.exit(1)
group = "base"
- uri = "http://"+gethostname()+"/rpms"
+ uri = "http://" + gethostname() + "/rpms"
rpmdir = "."
priority = "0"
output = None
@@ -320,6 +356,6 @@ if __name__ == "__main__":
if output == None:
output = sys.stdout
else:
- output = file(output,"w")
+ output = file(output, "w")
scan_rpm_dir(rpmdir, uri, group, priority, output)
diff --git a/tools/yum-listpkgs-xml.py b/tools/yum-listpkgs-xml.py
index c324d0889..2df5abbcd 100644
--- a/tools/yum-listpkgs-xml.py
+++ b/tools/yum-listpkgs-xml.py
@@ -3,41 +3,42 @@ import sys
sys.path.append('/usr/bin/')
sys.path.append('/usr/share/yum-cli')
-import yum
import yummain
+
def mySimpleList(self, pkg):
- print "<Package name='%s' version='%s'/>" % (pkg.name, pkg.printVer())
-
+ print("<Package name='%s' version='%s'/>" % (pkg.name, pkg.printVer()))
+
+
def myListPkgs(self, lst, description, outputType):
- """outputs based on whatever outputType is. Current options:
- 'list' - simple pkg list
- 'info' - similar to rpm -qi output"""
-
- if outputType in ['list', 'info']:
- thingslisted = 0
- if len(lst) > 0:
- thingslisted = 1
- #print '%s' % description
- from yum.misc import sortPkgObj
- lst.sort(sortPkgObj)
- for pkg in lst:
- if outputType == 'list':
- self.simpleList(pkg)
- elif outputType == 'info':
- self.infoOutput(pkg)
- else:
- pass
-
- if thingslisted == 0:
- return 1, ['No Packages to list']
+ """outputs based on whatever outputType is. Current options:
+ 'list' - simple pkg list
+ 'info' - similar to rpm -qi output"""
+
+ if outputType in ['list', 'info']:
+ thingslisted = 0
+ if len(lst) > 0:
+ thingslisted = 1
+ #print '%s' % description
+ from yum.misc import sortPkgObj
+ lst.sort(sortPkgObj)
+ for pkg in lst:
+ if outputType == 'list':
+ self.simpleList(pkg)
+ elif outputType == 'info':
+ self.infoOutput(pkg)
+ else:
+ pass
+
+ if thingslisted == 0:
+ return 1, ['No Packages to list']
yummain.cli.output.YumOutput.listPkgs = myListPkgs
yummain.cli.output.YumOutput.simpleList = mySimpleList
try:
- sys.argv = [sys.argv[0],'-d','0','list']
+ sys.argv = [sys.argv[0], '-d', '0', 'list']
yummain.main(sys.argv[1:])
-except KeyboardInterrupt, e:
- print >> sys.stderr, "\n\nExiting on user cancel."
+except KeyboardInterrupt:
+ print("\n\nExiting on user cancel.", file=sys.stderr)
sys.exit(1)