Writing Bcfg2 Specifications
The Bcfg2 specification is a set of directives that describe how
hosts should be configured. This information is used to generate
client configurations. This section describes the steps taken
during the composition of bcfg2 specifications.
All parts of the specification will correspond to some
subset of the clients that bcfg2 has record of. Find or create a
group corresponding to the target of this specification.
Add each new configuration entry to the "abstract
configuration" for the target group. This can be done in one
of two ways. If the new configuration has to do with a
service, or some other piece of inter-dependent configuration,
write a bundle. If not, add the extra configuration entries to
the base.
Add configuration data for the above entries that apply only
to the target group.
Verify that clients not in the target group remain unaffected
by these changes.
Re-validate the bcfg2 repository by running
bcfg2-repo-validate.
Interacting with Client Groups in Bcfg2
Bcfg2 uses an aspect-based classing mechanism to describe
configuration patterns in its specifications. Each class
describes particular aspects of client configurations. The Bcfg2 metadata
mechanism has two types of information, client metadata and
group metadata. Client metadata describes what top level group a
client is associated with. Its configuration is derived from a
combination of its host information and this group. Group
definitions describe groups in terms of what bundles they
include and other groups they include. Groups have a set of
properties that describe how they can be used. (Starred values
are defaults)
Bcfg2 Group Parameters
NameDescription
Values
profileIf a client can be
directly associated with this group(True|False*)
publicIf a client can freely
associate itself with this group(True|False*)
toolset
Describes which client-side logic should be used to
make configuration
changes(rh|debian|solaris|aix|auto)
category
A group can only contain one instance of a group in
any category. This provides the basis for representing
groups which are conjugates of one another in a rigorous
way. It also provides the basis for negation.string
When a client's configuration is generated, its metadata is
fetched. This includes a list of all groups recursively
dereferenced, and all bundles included by those groups. This
collection has already been processed using the group category
rules, so only one instance from each group category is
included. This metadata is used throughout the rest of the
configuration generation process; it defines the client's abstract
configuration and specifies all literal contents of all
configuration entities.
Adding to the Abstract Configuration
When writing bcfg2 specification, administrators primarily
perform one of two operations: addition of new configuration
entities or the modification of existing entries. If new
entities need to be added, then they must be added to the
abstract configuration. This is the inventory of configuration
entities that should be installed on a client. Two plugins
provide the basis for the abstract configuration, the bundler
and base. The bundler builds descriptions of interrelated
configuration entities. These are typically used for the
representation of services, or other complex groups of
entities. Base provides a laundry list of configuration entities
that need to be installed on hosts. These entities are
independent from one another, and can be installed individually
without worrying about the impact on other entities.
Entities in the abstract configuration (and correspondingly in
the literal configuration) can have one of several types. In the
abstract configuration, each of these entities only has a tag
and the name attribute set.
Bcfg2 Configuration Entity Types
NameDescription
PackageSoftware Package
ConfigFileConfiguration File
Service
Persistent system services and daemons
DirectoryFilesystem Directories
SymLinkSymbolic links
Permissions
The permissions (not contents) of a POSIX path
Writing Bundles
Bundles consist of a set of configuration entities. These
entities are grouped together due to a configuration-time
interdependency. Basic services tend to be the simplest
example of these: they consist of some software package(s)
some configuration files and an indication that some service
should be activated. If any of these pieces are installed or
updated, all should be re-checked and any associated services
should be restarted.
Bundles can also contain conditonal entries that are only used
for hosts in some particular groups. This is useful when a
service name varies from platform to platform. A group is
defined for each platform, hence a different service can be
associated with the bundle for clients in the different
groups. Conditional additions can also add extra functionality
to services as needed. This has proven useful in the case of
configuring webservers; php and ssl can be configured as extra
features that some webservers have and others do not. At the
same time, all configuration-time interdependencies are
maintained.
Using Base
The Base plugin provides a mechanism to add independent
configuration entities to a client's abstract
configuration. All files in the Base/ subdirectory of the
respository are processed, and all entries that fall within
the scope of the client metadata are included in its abstract
configuration. These files are similar to those used by the
Bundler, Svcmgr, and Pkgmgr, without the need for
prioritization used by the later two.
Adding to the Literal Configuration
During the construction of the literal configuration, first the
abstract configuration is built, and then explicit data is bound
in to each entity. The previous section describes how the first
stage works, and this section describes the second stage. Each
entity will be served by one plugin. This plugin will decide
what explicit data should be bound in to a particular entity for
a given client. Each of these plugins has a specific area of the
configuration repository, corresponding to its name. This
section describes how several of the basic plugins works.
Cfg
The Cfg plugin provides a configuration file repository that
uses literal file contents to provide client-tailored
configuration file entries. It 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.G99_webserver,
"G99_webserver" is the specifier. This specifier applies to
the group (G) webserver with a priority of 99. Files can also
be tagged with a host-specific (H) specifier.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. This file can also
contain an encoding parameter (ascii|base64) and a paranoid
flag that causes diffs to be logged on clients.
Cfg/filepath/:info
owner:root
group:root
perms:0755
Cfg/etc/passwd/
$ ls
passwd.H_adenine passwd passwd.G99_chiba
passwd.H_bio-debian passwd.H_cvstest passwd.H_foxtrot
passwd.H_reboot passwd.H_rudy2 passwd.G98_netserv
passwd.G99_tacacs-server.cat :info
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.
Pkgmgr
The Pkgmgr plugin is responsible for providing package version
and installation information. In the case of each "Package"
entity in the configuration, it binds in information needed to
detect, verify and install the package. It has a similar
format to the files used by Base and Bundler, but with a few
differences. First, each file has a priority. This allows the
same entity to be served by multiple files. The priorities can
be used to break ties in the case that multiple files serve
data for the same package. The other difference is that
automatic deriviation of package information from the file
attribute. The Pkgmgr has a set of regular expressions that
can split package names for several formats. The filenames are
used to construct installation URLs, and set several important
fields like package name and version.
Package Entity Attributes
NameDescription
namePackage Name
versionPackage Version
uri
URL-style location of file repository (typically http)
filePackage file name. Several
other attributes (name, version, url) can be automatically
defined based on regular expressions definied in the
Pkgmgr plugin.
simplefilePackage file name. No
name parsing is performed, so no extra fields get
set
Svcmgr
The Svcmgr plugin describes where services should be active
and inactive. Its files have a similar form to those used by
the Pkgmgr. Several files in the Svcmgr repository can contain
overlapping definitions, and a per-file priority is used to
determine precedence, the highest priority file serving data
for a particular service wins, on a service by service basis.
These files also have a similar set of semantics to those used
by the Pkgmgr. Entries in the top level element (Services) are
global definitions. Group elements describe additional
conditions that must be matched for that definition to
supercede less specific ones. Deeply nested definitions must
have their parent condition matched, plus all parent
conditions as well. For example, the following declaration
turns ssh on by default, disables it if the client is a part
of group a, and reenables it if the client is a part of both
groups a and b. Group nesting provides a conjunctive
function.
Svcmgr/ssh.xml
]]>
The files used by this plugin can be structured in a number of
ways. The most common method is to use one large file, but
this can be inconvenience due to the large size of the
file. The data can also be split up using any convenient
mechanism: per-service, per-administrator, etc.
Rules
The Rules plugin works like Pkgmgr and Svcmgr, but can be used
for any entries, including literal packages and services,
directories, permissions and symlinks.
SSHbase
The SSHbase plugin implements ssh public and private key
management functionality. This means that a central record of
ssh host keys is maintained. Also, a correct ssh_known_hosts
file is maintained. This means that the keys for new hosts are
added to this configuration, and also that a correct line for
localhost is created. SSHbase will generate a new key for any
hosts that doesn't already have a key stored in the
repository, so it should be pre-seeded with the keys (public
and private) of pre-existing clients.
Checking Group-External Clients for Unintended
Changes
Any configuration change will apply to some set of clients.
Often, repository changes can have unintended consequences to
clients not included in the target group. To address this issue,
consider the changes performed, and if they can affect clients
in unexpected ways.
Validating the Bcfg2 Repository
Bcfg2 includes a repository validation tool that will check all
XML files in the repository against included XML schemas. It is
critical to run this command,
bcfg2-repo-valdate after any modifications to
XML files. If all files validate properly, then no output will
be returned. It takes a "-v" option that prints out a line for
each file that is validated. This can be used to ensure that all
files are checked.
Annotated Configuration Examples
In addition to the description of the abstract process
above, we present several examples of the thought process and
actions taken to achieve a particular configuration goal. These
will start simple, but become more complex.
Configuring /etc/motd on all hosts
The goal for this example is to install a uniform copy of
a specified /etc/motd on all hosts.
In this case, the target group is all clients, since we want
this version of /etc/motd. As
mentioned earlier, the global group is handled specially,
so that all new clients, even newly created ones, are in it.
Since /etc/motd is not interdependent
with any other configuration entities, it can be installed
using Base instead of using Bundler. The ConfigFile
entity should be placed in the globally scoped section of
a base file. This adds the configuration file to the
abstract configuration.
A file with the correct contents for
/etc/motd should be installed in the
Cfg repository area as a global file. This will provide
the right literal configuration specification for each
client.
Since this change is globally scoped, there are not any
clients that should not be affected.
Finally, bcfg2-repo-validate should be
run to catch typos.
Configuring NTP for a network
The goal for this example is to configure NTP for an entire
network. This implies several things. All clients should run
NTP as clients. Some hosts should run NTP as a server, and
other hosts should use local NTP service instead of an
external server.
Two discrete groups are used for the different parts of
the desired configuration state. The first is the global
group, handling the "all clients run ntp" part of the
configuration specification. The other part corresponds to
a new group "ntp-server", which contains only clients that
should function as an ntp server.
This configuration specification describes configuration
entities that have interdependencies, so a bundle should
be used. This bundle should contain the ntp software, the
ntp configuration file, and the ntpd service.
The literal portions of three different configuration
entities need to be represented. First, the Pkgmgr needs
to be configured to bind a package entity named ntp. Next,
the Svcmgr needs to have a global declaration that the
service ntpd should be on. Finally, two different
configuration files should be added to Cfg. The global
version of /etc/ntp.conf should have
an ntp configuration that points hosts at the local ntp
server. The group "ntp-server" specific version of this
file should have the proper configuration for local ntp
servers.
A quick inspection of these configuration changes show
only minor possibilities for bad interactions. All
machines should run ntp, and the only scope where bad
interactions can occur is on ntp servers.
Finally, run bcfg2-repo-validate. It
will validate the new bundle that has been added, and
changes to the Svcmgr and Pkgmgr indices.