Tags:
create new tag
view all tags

A Guide to the LCFG Client

1. Basics

1.1 Node name

A machine managed by LCFG has a node name which is the identifier by the LCFG client when referring to the local machine. Typically this will be identical to the fully-qualified domain name for the machine (e.g. foo.example.org) but there is no requirement for the two to match and you must not assume that they will. If a client is roaming (typically a laptop) with no fixed IP address or host name then the two will definitely be different. It is also possible to manage a whole group of identical machines using a single LCFG profile by setting the LCFG node name to be the same on all the machines (note that this makes spanning maps useless but that might not be an issue). The LCFG client component has a setnode method which can be used to modify the node name, it is also possible to set it at install time via the installer kernel command-line using the lcfg.node parameter (e.g. lcfg.node=foo.example.org)

1.2 XML Profile

An LCFG node has an associated LCFG XML profile which represents the desired state of the machine. In fact, when LCFG contexts are considered (see below for a discussion of contexts), the XML profile actually represents all possible desired states. An XML profile contains both the required configuration state and a list of required software packages. The XML profile is generated by an LCFG server from a source profile. Typically the LCFG server is a separate host from the client and the XML profile is served via http or https and when required is fetched by the LCFG client.

The profile is not just a set of configuration files which should be applied to the machine, it's a higher level description of the intended state of the system.

1.3 Component

In the LCFG profile the desired configuration state is grouped into logical units called components. Typically this grouping is based on the service being configured (e.g. there are components for dns and apache) or alternatively others are grouped by shared purpose (e.g. auth, pam). Each component has a set of resources which hold the information related to various aspects of the configuration (e.g. on which port apache should listen). Typically a component manages a configuration by combining the resources with a template to generate a desired configuration file.

1.4 Resource

At its most basic an LCFG resource has a name and a value (effectively it's just a key-value pair). It also may have a simplistic type (e.g. integer or boolean) and it might have information on the derivation of its value (in which files and on which lines the value was modified in the source). Each resource is used to hold information on one small aspect of the service being configured by the component. This might be used to represent the port on which apache should listen or a list of users who are allowed to login.

The more complicated view is that an LCFG resource is in fact multi-valued with each value being context specific (see below for discussion of contexts). At any time a resource will have a single current value and a set of potential values, if there is more than one possible value then the current value might change when the local contexts change. If an LCFG resource has context-specific values then the LCFG server will ensure there is also a value for the default null-context case. Contexts are not heavily used so most of the time the only value a resource will ever have is the null-context value.

1.5 Context

A context is a simple key-value pair which is set on the local system to denote that the system is currently in a particular situation. An LCFG resource value may have an associated context specification which declares the situations in which it is valid. Packages may also have an associated specification of the context in which they should be installed or a particular version should be used. Simple comparisons and boolean logic is all that is accepted (e.g. foo=bar, !installing).

Currently contexts are only really used at install time to denote that the system is currently being installed, in that case the key is install and the value is true. Consequently, any resources could have values which are dependent on the install context being true or false.

As a further example, contexts have previously been used to control the network configuration of roaming devices depending on the current location. So, when at work you would set the net context on your laptop to work, when at home you would set the context to home, etc. In each case, changing the context forces the LCFG client to reinterpret the LCFG XML profile, any resources which had values for the new context would see their current values changed (and the network component would then be notified to handle the change). This sort of manual approach to handling network change has now been mostly superceded by networkmanager.

It is possible to have multiple contexts enabled simultaneously, each context will be allocated a numerical "priority" which is based on the order in which they are set. If a resource has different possible values for each current context the conflict is resolved using the priorities.

2. Modes of Operation

The LCFG client can be used in two different modes. It can be run manually in what is termed one-shot mode where it will do a one-off download and processing of a specified LCFG profile. Alternatively it can be run in daemon mode where it runs continuously and manages the local machine using the LCFG profile for that node.

2.1 One-Shot Mode

In one-shot mode the LCFG client will fetch a specified LCFG profile and process it to generate a resource DB and updaterpms rpmcfg file. It does not check for any changes since the last time the profile was processed and it does not signal any LCFG components to reconfigure. In this mode profiles for any node can be downloaded (providing you have sufficient access permissions). The profile will always be downloaded and processed even if it is not newer than the currently downloaded profile for that node.

2.2 Daemon Mode

In daemon mode the LCFG client operates autonomously to manage the configuration of the local machine using the LCFG profile for the current node. This mode is enabled by specifying the --daemon option (which is done by the LCFG client component when it starts the client). The client listens for notifications from the LCFG servers which indicate that a new profile is available. The client also polls the server on a regular basis (configurable via the --polltime option, see the client.poll resource) to ensure that it still notices a new profile even if it has missed a notification. If a profile is available which is newer than the one stored locally then it is fetched and processed. This results in a new resource DB and updaterpms rpmcfg being generated. If there are any differences in the values of the resources since the previous run then the relevant LCFG components are notified (typically by calling the configure method). Similarly it is possible to have the package manager run if the package list has been altered. After a profile has been processed an acknowledgement is sent to the server along with various status information for the client and the current set of LCFG components.

3. Fetching XML profiles

Although it is possible to configure the client to fetch the LCFG XML profile from a path in the local filesystem it is far more common for the client to be configured to use one or more remote servers. In the case of remote servers the XML profile will be fetched via http or https, in that case there is built-in support for http basic auth (i.e. with a username and password).

When attempting to fetch an XML profile from a remote server the client will iterate through the list of servers until it receives a valid response. Prior to doing the fetch, the order of the list is randomised by allocating each server a random numerical "priority" and then sorting the list based on that priority. This approach provides a very simplistic load balancing.

The checking for new-ness of the XML profile on a remote server relies on a feature of HTTP/1.1 which is that it sets the If-Modified-Since header (see section 14.25) with the value of the current copy of the profile. If the remote server supports that feature and the remote file is not newer it will send a 304 (not modified) response. This means that the client is reliant on the remote web server supporting this feature (and giving the correct answer). The failure mode, where the XML profile is always downloaded, is benign though so this does not represent any risk to the client.

The fetching of an XML profile is done using the LWP::UserAgent Perl module.

The behaviour of the fetch process is different depending on the current mode of operation.

3.1 One-Shot Mode

Under one-shot mode the client will always fetch the XML profile. It does not matter if the locally stored copy is the same age (or newer) it will be replaced with the version from the remote server.

3.2 Daemon Mode

Under daemon mode the client will only ever fetch an XML profile if it is newer than the locally stored copy (or there is not currently a local copy).

Before any attempt is made to actually fetch a profile this method will fork a child process. This protects the main daemon process from bad behaviour of the fetch process. The forked process has a special signal handler for the INT signal to handle the case where the client is told to stop whilst the fetching is in process.

There are three reasons why the client might attempt to fetch a new XML profile in daemon mode:

3.2.1 Notification

When an LCFG server has rebuilt an XML profile it sends a notification to the client so that it can (hopefully) collect the new profile as soon as possible. To be able to receive notifications the client daemon must be able to listen for UDP packets on the lcfg port (which is typically 732).

The notification message contains a timestamp for when the new XML profile was generated. It may be that the client receives several notifications from different servers. This will only fork and run the fetch process if there is a newer profile advertised by one (or more) of the servers.

In this case the list of servers used is just the subset which have sent notifications (there may still be several). This list of servers is sorted based on the timestamp of the XML profile being offered and the (random) priority which was allocated to the server.

Notifications from unknown servers are ignored (but logged).

There are a number of situations in which notifications will not be received. If there is a firewall in the way (either blocking egress from the server or ingress to the client) the packet might be dropped. Also, if the LCFG node does not match the host name then the server may not know the correct address to which to send the notification. Finally, since UDP is being used the packets just might not arrive.

The notification message contains the following information: remote host, token, mtime, data. These fields are joined with a : (colon) separator.

3.2.2 Poll Timeout

To avoid any issues related to missing notifications the client has an internal timer which tracks how long it is since the remote server was checked to see if a new XML profile is available. Once that timer has expired the client will look for a new XML profile.

3.2.3 Manual Prod

At any time a local administrator can send a signal to the client to indicate that it should immediately check for a new profile. This is done by sending a HUP to the client process, typically that signal is sent using the run method of the LCFG client component.

4. XML Profile Processing

The structure of the LCFG XML Profile is described on XMLProfileNotes.

The XML Profile will be processed whenever a file is fetched which has a newer timestamp, it will be reprocessed if there has been a change of contexts. It is also possibly to force the reprocessing even if nothing has changed (this occurs in one-shot mode, for instance).

As well as the main XML profile it is possible to have local override and context-specific override files. Both files use the same XML structure as the main XML profile but the two types of overrides are handled slightly differently.

local overrides
Any file in the /var/lcfg/conf/profile/local/ directory with a .xml suffix will be processed every time the main XML profile is processed. These local override files are used to completely transplant (insert or replace) the configuration for components in the main profile. Note that no trace of any data for that component coming from the main XML profile will be retained. This is likely to only be useful for testing a new component configuration. It is not possible to alter the package list using local override files.

context-specific overrides
For each current context the client will look for an associated XML override profile in the /var/lcfg/conf/profile/context/ directory. The file must be in a sub-directory which has the same name as the context and the file basename should be the value of the context, the suffix should be .xml. Using the example of the current network settings being handled with a context named net and values being home and work, the override files would be /var/lcfg/conf/profile/context/net/home.xml and /var/lcfg/conf/profile/context/net/work.xml. These context-specific override files transplant (insert or replace) resources in the existing components, components which are not in the main XML profile are ignored. It is possible to include additional packages but not alter the versions of packages in the main list.

The processing stage converts the data in the XML profile into two products - the DB of current component/resource state and the package list (which is used by updaterpms).

4.1 Components

The LCFG component data in the LCFG XML profile contains information on all possible context-specific states of the various LCFG resources. The act of processing this data reduces the multi-valued information down into the set of (single-valued) current resources which are stored as a flat key-value hash structure in a Berkeley DB file for the node (e.g. /var/lcfg/conf/profile/dbm/foo.example.org.DB2.db). The application of local-overrides and context-specific overrides is described in the previous section.

Each time an LCFG XML profile is processed or a context change occurs the new current state of the resources is compared with the previous state to identify any changes to the values. It is also checked for new resources which have been added and resources which have been removed. If there are any differences between the two states then the relevant LCFG components are notified (unless secure mode is enabled, see below for details).

When storing the various aspects of each LCFG resource into the Berkeley DB file for the node keys are used which are like nodename.component_name.resource_name with a different single-character prefix for each aspect of the resource. The prefixes are:

resource aspect prefix
derivation #
context =
priority ^
type %

the key which points to the value of the resource does not have a prefix. This means that for each resource there is at least one key and may be up to 5. For example:

          'mynode.client.timeout' => '',
          '%mynode.client.timeout' => 'string(timeout)',
          '#mynode.client.timeout' => '/var/lcfg/conf/server/defaults/client-4.def:56',

If you want to inspect all the data you can dump the DB like this:

perl -MDB_File -MData::Dumper \
  -E 'tie( %h, "DB_File", "/var/lcfg/conf/profile/dbm/columba.inf.ed.ac.uk.DB2.db", O_RDONLY); say Dumper \%h

4.1.1 Component Change Notification

Full details of the ngeneric resources (ng_*) are available in the lcfg-ngeneric manual page.

If the current values of any resources for a component have changed then when the client is in daemon mode the component will be notified so that it can reconfigure. The method which is called when reconfiguring a component is specified in the ng_reconfig resource for the component, if nothing is specified then the component will not be notified of any changes.

It is also possible to provoke a method call for a component every time the profile is generated by setting the special ng_prod and ng_prodmethod resources. Every time the value of ng_prod resource (which could be something like the current timestamp) changes the method specified in the ng_prodmethod resource will be called.

If multiple components are to be notified then the order in which this will occur is controlled by the ng_cforder resources for each component. When generating a new profile the LCFG server orders the list of components in the client.components resource so that the various partial-ordering constraints are satisfied.

Dependencies between components are also supported so that whenever one component is reconfigured another also gets notified. This is done with the ng_cforder resource for each component, this can be used to specify, from the perspective of each component, if foo depends on bar or if bar depends on foo.

If there are resource changes for the client component itself then notification of the change is held back until all other components have been notified. The LCFG client component will restart the daemonised process so that it picks up the new configuration.

4.1.2 Server Acknowledgements

When the client processes an XML profile it sends an acknowledgement to the LCFG server. It does this by sending a message over UDP to the lcfgack port (which is typically 733) on the LCFG server.

The acknowledgement message contains the following information (in this order): the node name, the current time in seconds since epoch, the modification time of the LCFG XML profile, component data, the system boot time, and some software version information, all combined with a : (colon) separator. The software version is the LCFG client version, the platform name - which is taken from the LCFG release file (/etc/LCFG-RELEASE)- and the modification time of the release file all combined with a / (forward slash) separator (e.g. 3.5.9/sl6_64/1396245124).

The component data is a comma-separated list of component names each of which has a string of single-character status flags which are encased in square-brackets. For example, foo[EWRB],bar[RB]. The flags are:

Flag Definition
E errors have been recorded for component
W warnings have been recorded for component
R component is running
S component status file is present
L component log file is present
B component started at boot time
F component has requested a reboot
P component has requested a power-cycle

Additionally this string of component status information has a format version prefix which is encased in % signs (e.g. %2%).

The best guide to parsing the client acknowledgement messages is the code in the LCFG::Server::Status Perl module.

4.1.3 Secure Mode

The LCFG client has a rather basic "secure" mode which holds all changes to resource values until they are manually verified and applied by the administrator. The changes are stored for review in a hold file in /var/lcfg/log/client.hold, this file also contains the hex digest of the MD5 checksum for the changes. The change can be applied by setting the secure context to be the value of the hex digest. For example:

context secure=bb64253c2dda058f8cb7e41c32d17915

4.2 Packages

The list of packages in the XML profile is processed in the order in which it is specified by the server. After processing the list in the main profile any context-specific overrides will be applied. A package may have different versions specified for different contexts. Unlike an LCFG resource it is possible that a package may not be specified for the null-context situation, i.e. the package will only need to be installed when some particular context is enabled. If there are currently multiple contexts enabled and this results in multiple possible versions for a package then the version selected will be based on the numerical priority of each context.

Once the list of packages has been assembled it is written out to a node-specific file in the /var/lcfg/conf/profile/rpmcfg/ directory. This file is formatted using CPP macros, this is designed to be processed by the LCFG updaterpms component before running the updaterpms tool itself. As well as the package version specifications each package will have some metadata defined, for example:

#ifdef INCLUDE_META
#pragma LCFG derive "/var/lcfg/conf/server/releases/develop/core/packages/dice/dice_sl6_env.rpms:86"
#endif
AdobeReader-fix-7-1/noarch

All possible versions of the packages are included in this package list, those which are not applicable for the current contexts are included in a section at the end which is protected by a CPP condition based on the definition of the ALL_CONTEXTS macro. This macro is only normally enabled when the rpmcache tool is used to fetch packages into a local cache so that they can still be installed when a machine is offline.

As well as the context-specific overrides it is possible to extend this list using local package lists. The paths to any local package lists must be specified via the client.rpminc resource. If the file exists then the main package list will "include" it at the end. For example, with /var/lcfg/conf/local_packages.h, at the end of the main package list there would be the following:

#include "/var/lcfg/conf/local_packages.h"

A local package file can just be a list of package specifications, it is not necessary to specify any metadata.

The assembled list is sorted on the package name (case-insensitively) so that after it has been generated it can be compared with the previous file. The comparison is done very simply, if there are any differences (including metadata and versions not applicable to the current contexts) then the client will still consider the list to have changed. When the package list is updated, the timestamp will match that of the XML profile.

It is possible to have the LCFG client notify the package manager when the package list has been altered, this requires the --notify option (see the client.notify resource) to be enabled which is the default in daemon mode. The --updaterpms option (see the client.runupdate resource) must also specify the name of the LCFG component which should be called, a component method can also be specified (the default method is run).

The client places some limitations on what packages can be specified, these limits may not match with the local package manager. For instance, the client does not currently have any knowledge of the package architecture so, even though the RPM package manager allows multiple architectures of the same package to be installed simultaneously, it is not possible to specify that explicitly. To solve this issue, the updaterpms tool supports a workaround where the architecture is specified as part of the package name instead.

Package managers may, in certain cases, permit multiple versions of the same package to be installed simultaneously (e.g. the kernel package on Redhat machines), this is not supported by the LCFG client.

-- Main.squinney - 2014-03-26

Topic revision: r4 - 2019-06-18 - squinney
 
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback