From af98f300fdf7362eb792df1456bf9f4a2fbc90bb Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 28 Mar 2013 15:24:16 -0400 Subject: new GroupLogic plugin --- doc/server/plugins/connectors/grouplogic.txt | 122 +++++++++++++++++++++++++++ schemas/grouplogic.xsd | 110 ++++++++++++++++++++++++ src/lib/Bcfg2/Server/Lint/Validate.py | 3 +- src/lib/Bcfg2/Server/Plugins/GroupLogic.py | 39 +++++++++ 4 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 doc/server/plugins/connectors/grouplogic.txt create mode 100644 schemas/grouplogic.xsd create mode 100644 src/lib/Bcfg2/Server/Plugins/GroupLogic.py diff --git a/doc/server/plugins/connectors/grouplogic.txt b/doc/server/plugins/connectors/grouplogic.txt new file mode 100644 index 000000000..b9a5b00d6 --- /dev/null +++ b/doc/server/plugins/connectors/grouplogic.txt @@ -0,0 +1,122 @@ +.. -*- mode: rst -*- + +.. _server-plugins-connectors-grouplogic: + +========== +GroupLogic +========== + +.. versionadded:: 1.3.2 + +GroupLogic is a connector plugin that lets you use an XML Genshi +template to dynamically set additional groups for clients. + +Usage +===== + +To use the GroupLogic plugin, first do ``mkdir +/var/lib/bcfg2/GroupLogic``. Add ``GroupLogic`` to your ``plugins`` +line in ``/etc/bcfg2.conf``. Next, create +``/var/lib/bcfg2/GroupLogic/groups.xml``: + +.. code-block:: xml + + + + +``groups.xml`` is structured very similarly to the +:ref:`server-plugins-grouping-metadata` ``groups.xml``. A Group tag +that contains no children is a declaration of membership; a Group or +Client tag that does contain children is a conditional. + +Unlike ``Metadata/groups.xml``, GroupLogic supports genshi templating, +so you can dynamically create groups. ``GroupLogic/groups.xml`` is +rendered for each client, and the groups set in it are added to the +client metadata. + +.. note:: + + Also unlike ``Metadata/groups.xml``, GroupLogic can not be used to + associate bundles with clients directly, or to negate groups. But + you can use GroupLogic to assign a group that is associated with a + bundle in Metadata. + +Consider the case where you have four environments -- dev, test, +staging, and production -- and four components to a web application -- +the frontend, the API, the database server, and the caching proxy. In +order to make files specific to the component *and* to the +environment, you need groups to describe each combination: +webapp-frontend-dev, webapp-frontend-test, and so on. You *could* do +this in ``Metadata/groups.xml``: + +.. code-block:: xml + + + + + + + + + + ... + + + ... + + ... + + +Creating the sixteen groups this way is incredibly tedious, and this +is a quite *small* site. GroupLogic can automate this process. + +Assume that we've declared the groups thusly in +``Metadata/groups.xml``: + +.. code-block:: xml + + + + + + + + + + + + +One way to automate the creation of the groups would be to simply +generate the tedious config: + +.. code-block:: xml + + + + + + + + + + + + + +But, since ``GroupLogic/groups.xml`` is rendered for each client +individually, there's a more elegant way to accomplish the same thing: + +.. code-block:: xml + + + + + + + + +This gets only the component and environment for the current client, +and, if both are set, sets the single appropriate group. diff --git a/schemas/grouplogic.xsd b/schemas/grouplogic.xsd new file mode 100644 index 000000000..bf43bceb3 --- /dev/null +++ b/schemas/grouplogic.xsd @@ -0,0 +1,110 @@ + + + + + GroupLogic schema for bcfg2 + + + + + + + + + A **GroupLogicDeclarationType** declares a Group to be added + to a client. + + + + + + The group name + + + + + + + + + + The top-level tag of a GroupLogic configuration file. + + + + + + + + + Elements within Group tags only apply to clients that are + members of that group (or vice-versa; see #element_negate + below) + + + + + + + Elements within Client tags only apply to the named client + (or vice-versa; see #element_negate below) + + + + + + + Nesting GroupLogic tags is allowed in order to support + XInclude. + + + + + + + + + + + A **GroupLogicContainerType** is a tag used to provide logic. + Child entries of a GroupLogicContainerType tag only apply to + machines that match the condition specified -- either + membership in a group, or a matching client name. + :xml:attribute:`GroupLogicContainerType:negate` can be set to + negate the sense of the match. + + + + + + + + The group name + + + + + + + Negate the sense of this group or client; i.e., entries + within this tag are only used on clients that are not + members of the group, or that have hostnames that do not + match. + + + + + + + + + + + A GroupLogic file is a genshi file that can be used to + dynamically add additional groups to a client. + + + + diff --git a/src/lib/Bcfg2/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py index a5f41c7af..14d17a1e6 100644 --- a/src/lib/Bcfg2/Server/Lint/Validate.py +++ b/src/lib/Bcfg2/Server/Lint/Validate.py @@ -40,7 +40,8 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin): "NagiosGen/config.xml": "nagiosgen.xsd", "FileProbes/config.xml": "fileprobes.xsd", "SSLCA/**/cert.xml": "sslca-cert.xsd", - "SSLCA/**/key.xml": "sslca-key.xsd" + "SSLCA/**/key.xml": "sslca-key.xsd", + "GroupLogic/groups.xml": "grouplogic.xsd" } self.filelists = {} diff --git a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py new file mode 100644 index 000000000..c572fee2b --- /dev/null +++ b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py @@ -0,0 +1,39 @@ +import os +import lxml.etree +import Bcfg2.Server.Plugin +try: + from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile +except ImportError: + # BundleTemplateFile missing means that genshi is missing. we + # import genshi to get the _real_ error + import genshi # pylint: disable=W0611 + + +class GroupLogicConfig(BundleTemplateFile): + create = lxml.etree.Element("GroupLogic", + nsmap=dict(py="http://genshi.edgewall.org/")) + + def __init__(self, name, fam): + BundleTemplateFile.__init__(self, name, + Bcfg2.Server.Plugin.Specificity(), None) + self.fam = fam + self.should_monitor = True + self.fam.AddMonitor(self.name, self) + + def _match(self, item, metadata): + if item.tag == 'Group' and not len(item.getchildren()): + return [item] + return BundleTemplateFile._match(self, item, metadata) + + +class GroupLogic(Bcfg2.Server.Plugin.Plugin, + Bcfg2.Server.Plugin.Connector): + def __init__(self, core, datastore): + Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) + Bcfg2.Server.Plugin.Connector.__init__(self) + self.config = GroupLogicConfig(os.path.join(self.data, "groups.xml"), + core.fam) + + def get_additional_groups(self, metadata): + return [el.get("name") + for el in self.config.get_xml_value(metadata).findall("Group")] -- cgit v1.2.3-1-g7c22