diff options
-rw-r--r-- | doc/server/plugins/connectors/properties.txt | 17 | ||||
-rw-r--r-- | doc/server/plugins/structures/bundler/index.txt | 44 | ||||
-rw-r--r-- | schemas/bundle.xsd | 16 | ||||
-rw-r--r-- | schemas/info.xsd | 3 | ||||
-rw-r--r-- | src/lib/Server/Plugin.py | 34 | ||||
-rw-r--r-- | src/lib/Server/Plugins/Properties.py | 10 |
6 files changed, 84 insertions, 40 deletions
diff --git a/doc/server/plugins/connectors/properties.txt b/doc/server/plugins/connectors/properties.txt index ef408916e..9fb0dba87 100644 --- a/doc/server/plugins/connectors/properties.txt +++ b/doc/server/plugins/connectors/properties.txt @@ -41,9 +41,20 @@ 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/structures/bundler/index.txt b/doc/server/plugins/structures/bundler/index.txt index 0d0054a2c..c84d6cdbe 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 ================ diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd index 1ea44c991..b226e1078 100644 --- a/schemas/bundle.xsd +++ b/schemas/bundle.xsd @@ -93,6 +93,14 @@ </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"/> @@ -207,6 +215,14 @@ </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"/> diff --git a/schemas/info.xsd b/schemas/info.xsd index 983513d66..96ccbe56c 100644 --- a/schemas/info.xsd +++ b/schemas/info.xsd @@ -22,6 +22,8 @@ <xsd:element name='Info' type='InfoType'/> <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:boolean' name='negate' /> @@ -31,6 +33,7 @@ <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/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index ac9479d44..69b38c4ff 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -457,17 +457,29 @@ class StructFile(XMLFileBacked): 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.""" diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py index 2888ef1d1..c5d2acc44 100644 --- a/src/lib/Server/Plugins/Properties.py +++ b/src/lib/Server/Plugins/Properties.py @@ -4,15 +4,9 @@ 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) + pass class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked): |