Generators
Generators are modules are are loaded by the Bcfg2 server,
based on directives in /etc/bcfg2.conf. They
provide concrete, fully-specified configuration entries for
clients. This chapter documents the function and usage of
generators bundled with Bcfg2 releases. It also describes the
interface used to communicate with generators; modeles
implementing this interface can provide configuration elements for
clients based on any representation or requirements that may
exist.
Bundled Generators
This section describes the generators that come bundled with
Bcfg2. As a general rule, generators requiring more than one
configuration file will use a generator specific directory in the
configuration repository.
Cfg
The Cfg generator provides a configuration file repository
that uses literal file contents to provide client-tailored
configuration file entries. The Cfg generator chooses which
data to provide for a given client based on the aspect-based
metadata system used for high-level client configuration.
The Cfg repository is structured much like the filesystem
hierarchy being configured. Each configuration file being
served has a corresponding directory in the configuration
repository. These directories have the same relative path as
the absolute path of the configuration file on the target
system. For example, if Cfg was serving data for the
configuration file /etc/services, then
its directory would be in the relative path
./etc/services inside of the Cfg
repository.
Inside of this file-specific directory, three types of files
may exist. Base files are complete instances of configuration
file. Deltas are differences between a base file and the
target file contents. Base files and deltas are tagged with
metadata specifiers, which describe which groups of clients
the fragment pertains to. Configuration files are constructed
by finding the most specific base file and applying any more
specific deltas.
Specifiers are embedded in fragment filenames. For example, in
the fragment services.C99_webserver,
"C99_webserver" is the specifier. This specifier applies to
the class (C) webserver with a priority of 99. Other metadata
categories which can be used include bundle (B), profile (P),
hostname (H), attribute (A), and image (I). These are ordered
from least to most specific: image, profile, class, bundle,
and hostname. Global files are the least specific. Priorities
are used as to break ties.
Info files, named :info are used to
specify target configuration file metadata, such as owner,
group and permissions. If no :info is
provided, targets are installed with default
information. Default metadata is root ownership, root group
memberships, and 0644 file permissions.
Cfg generator :info files
owner:root
group:root
perms:0755
Cfg file repository example
$ ls
:info passwd passwd.C99_chiba-login
passwd.H_bio-debian passwd.H_cvstest passwd.H_foxtrot
passwd.H_reboot passwd.H_rudy2 passwd.C99_netserv
passwd.B99_tacacs-server.cat passwd.H_adenine
In the previous example, there exists files with each of the
characteristics mentioned above. All files ending in ".cat"
are deltas; ones with ".H_" are host specific files. There
exists a base file, a :info file, two
class-specified base files, and a bundle-specified base file.
The Scoped XML File Group: Servicemgr
The generator Servicemgr uses files formatted similarly to the
metadata files. It
works based on a single file, which contains definitions
ordered by increasing specificity.
foo
]]>
This set of service definitions is intrepreted in the
following way. Webservers run httpd, the host mailhost runs
sendmail, and all machines run ssh, and the ntp-server.
The Generator API
The Bcfg2 core has a well-formed API used to call
generators. This mechanism allows all stock generators to be
runtime selected; no stock generators are required. The
generator API has two main functions. The first is communication
to the Bcfg2 core: the list of entries a particular generator
can bind must be communicated to the core so that the proper
generator can be called. The second function is the actual
production of client-specific configuration element data; this
data is then included in client configurations.
The inventory function is provided by a python dictionary,
called __provides__ in each generator object. This dictionary
has a key for each type of configuration entry (ConfigFile,
Package, Directory, SymLink, Service), whose value is a
dictionary indexed by configuration element name. For example,
the data path to information about the service "sshd" could be
reached at __provides__['Service']['sshd']. The value of each of
these keys is a function that can be called to bind
client-specific values to a configuration entry. This function
is used in the next section.
The handler function located by the __provides__ dictionary is
called with a static API. The function prototype for each of
these handlers is:
The Generator handler API
def Handler(self, entry, metadata):
generator logic here
The data supplied upon handler invokation includes two
parts. The first is the entry. This is a ElementTree.Element
object, which already contains the configuration element type
(ie Service) and name. All other data is bound into this object
in this function. The range of data bound depends on the data
type. The other data provided to handlers is client metadata,
information about the current client, including hostname, image,
profile, classes and bundles. The metadata is typically used to
choose entry contents.
Writing a Generator
Writing a generator is a fairly straightforward task. At a high
level, generators are instantiated by the Bcfg2 core, and then
used to provide configuration entry contents. This means that
the two points where control passes into a generator from Bcfg2
are during initial object instantiation, and every time a
generator-provided configuration entry is bound.
Currently, generators must be written in python. They can
perform arbitrary operations, hence, a generator could be
written that executed logic in another language, but this
functionality is currently not implemented.
Simple Generator
from socket import gethostbyname, gaierror
from syslog import syslog, LOG_ERR
from Bcfg2.Server.Generator import Generator, DirectoryBacked, SingleXMLFileBacked, GeneratorError
class Chiba(Generator):
'''the Chiba generator builds the following files:
-> /etc/network/interfaces'''
__name__ = 'Chiba'
__version__ = '$Id: Chiba.py 1.12 05/01/15 11:05:02-06:00 desai@topaz.mcs.anl.gov $'
__author__ = 'bcfg-dev@mcs.anl.gov'
__provides__ = {'ConfigFile':{}}
def __init__(self, core, datastore):
Generator.__init__(self, core, datastore)
self.repo = DirectoryBacked(self.data, self.core.fam)
self.__provides__['ConfigFile']['/etc/network/interfaces'] = self.build_interfaces
def build_interfaces(self, entry, metadata):
'''build network configs for clients'''
entry.attrib['owner'] = 'root'
entry.attrib['group'] = 'root'
entry.attrib['perms'] = '0644'
try:
myriaddr = gethostbyname("%s-myr" % metadata.hostname)
except gaierror:
syslog(LOG_ERR, "Failed to resolve %s-myr"% metadata.hostname)
raise GeneratorError, ("%s-myr" % metadata.hostname, 'lookup')
entry.text = self.repo.entries['interfaces-template'].data % myriaddr
Generators must subclass the Bcfg2.Server.Generator.Generator
class. Generator constructors must take two arguments: an
instance of a Bcfg2.Core object, and a location for a
datastore. __name__, __version__, __author__, and __provides__
are used to describe what the generator is and how it
works. __provides__ describes a set of configuration entries
that can be provided by the generator, and a set of handlers
that can bind in the proper data. build_interfaces is an example
of a handler. It gets client metadata and an configuration entry
passed in, and binds data into entry as appropriate.