On DICE machines, components live in the
/usr/lib/lcfg/components
directory, and are manipulated
(stopped, started) by the "om
" command. Note that a
component may be said to be "running" even if there is not an active
daemon associated with it. Running components are flagged by
"component.run
" files in the
/var/lcfg/tmp
directory.
A component consists of a set of files used to generate the run-time script, and there are LCFG tools and templates available to create these scripts and associated files.
The basic progression is from script to component to RPM: a script is created from scratch (or using a template), and then incorporated into a component framework by extracting configurable information into separate files, these files are then built into an RPM, which is distributed and installed as required. Resources are incorporated into the profile or other header files, and these are used by the relevant component to configure and control the appropriate daemon or subsystem on a per-machine basis.
Note that if the new component is to perform a function that was
already catered for in some other way (rc
or
init
script, ad-hoc invocation, cron
job, etc) then some integration of the new component will be required
and some additional questions will need to be considered:
rc
run-level S
and K
scripts) need to be disabled?
ngeneric
, offering ways of starting,
stopping, and configuring the daemon or subsystem. However, it should
be possible to create a script and run it as a normal shell (or perl)
process:
./component start- but in practice, this is not ideal (and is actively discouraged) because the environment in which the component runs may not match the environment in which it will normally be run (root permissions, available resources, etc).
For a more detailed discussion of writing a component, see the LCFG documentation.
buildtools
. The absolute minimum for a
(shell-scripted) component is just:
#!/bin/bash . /usr/lib/lcfg/components/ngeneric Dispatch "$@"To get even this running successfully, additional steps are required, as there is no integration into the LCFG environment
In order to convert a standalone script into a standard-form LCFG component, various transformations are required, resulting in various constituent files:
.def
, is
required to define any types and default values of the
resources and additional methods to be used by the component
(to be installed on LCFG master server,
lcfg-master.inf
, generated from
component.def.cin
).
.cin
script,
containing code and variables to be instantiated.
.pod.cin
(used to generate component.pod
).
config.mk
file, containing component-specific
variable assignments to be used in the various
.cin
files (and used to produce the
all-variable-inclusive config.sh
file).
specfile
for RPM generation
template
file, generated from
template.cin
. This is used if configuration or
other files need to be generated.
./test
sub-directory structure.
Makefile
has rules to generate a
file from the abstracted .cin
version.
ngeneric
ngeneric
component. This provides the building blocks which are used by all other
components, and creates a uniform operating environment with a fixed
set of basic methods (which other components will assume to exist, so
it really does need to be included).
ngeneric
provides a set of wrapper-methods, and defaults for
the standard methods. For each standard method there is a
wrapper-method, Method_
methodname:
Start
method is called from within
Method_Start()
, and is initially defined to do nothing
(when called, it just returns true). The wrapper method,
Method_Start()
, does basic initialisation and
housekeeping - and then invokes the Start
method. If the
new component has re-defined the Start
method, then this
code will get run instead. Any component must use
ngeneric
, and must use the Dispatch
method
to handle arguments.
ngeneric
basic
":
#!/bin/bash . /usr/lib/lcfg/components/ngeneric Dispatch "$@"- we can abstract from this certain values, and add some comments. These values & comments can then be included in the
config.mk
file:
COMP=basic NAME=lcfg-$(COMP) DESCR=A Simple LCFG component V=1.1.1 R=1 SCHEMA=1 VERSION=$(V) GROUP=LCFG AUTHOR=Simple Simon <simple.simon@pieman.com> PLATFORMS=Solaris9, Fedora3, Fedora5, Fedora6 ORGANIZATION=University of Edinburgh CONFIGDIR=$(LCFGCONF)/$(COMP) MANDIR=$(LCFGMAN)/man$(MANSECT) DATE=23/05/07 17:28- and the script can then be generalised (conventionally named as <component
.cin
>, which in this example
becomes "basic.cin
"):
#!@SHELL@ ########################################################## # # Simple LCFG Component # # @AUTHOR@ # Version @VERSION@ : @DATE@ # # @MSG@ # ########################################################## @TESTSHELLV@ . @LCFGCOMP@/ngeneric ########################################################## # Dispatch methods ########################################################## Dispatch "$@"- the two files,
basic.cin
and config.mk
,
are used together to recreate the (original) basic
script.
Note that the @TESTSHELLV@
variable is only used during
testing (to define local, test directories of system locations) and
when a non-develop version is built, the variable is not set.
To automate the process, a generic makefile
can be used
(copied from one of the dice-example
,
lcfg-example
or lcfg-skeleton
components),
which should contain no component-specific references - it just
includes the buildtools.mk
file, and provides some basic
configure and install targets).
A documentation page can also be constructed, using the (slightly
modified) .pod.cin
file from one of the sample components
above as a template.
Once these files have been created, they need to be included in a CVS
repository (the DICE buildtools do make some assumptions, and one of
them is that it can access files via CVS). After this has been done
(using CVS import) the configure process should work, creating the
script "basic
", plus documentation
(basic.pod
& lcfg-basic.8
), and the
variable-substitution file, config.sh
:
% touch TIMESTAMP % make configure >>>> configuring basic ... >>>> configuring basic.pod ... >>>> creating lcfg-basic.8 ... % ls -1t basic basic.pod config.sh lcfg-basic.8 TIMESTAMP CVS config.mk basic.cin basic.pod.cin Makefile %The next step is to build this into an RPM which can be submitted and then installed on other clients via their profile (or included header file). This can be done using "
make rpm
", but at this
stage we're still testing/developing, and so should use "make
devrpm
". Trying to use "make rpm
" without
generating a new release first means that no tags will have been
assigned - and there will be a subsequent error:
% make rpm >>>> packing distribution ... cvs [export aborted]: no such tag lcfg_basic_1_1_1 make: *** [pack] Error 1 %Since we're still testing, we should use "
make devrpm
".
Note, however, that make
objects to the non-existence of
a spec file (the location of which is determined by evaluating
"rpm --eval %_specdir
" in buildtools.mk
).
% make devrpm >>>> packing development distribution make[1]: Entering directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' make[1]: Leaving directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' make[1]: Entering directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' make[1]: Nothing to be done for `devprep'. make[1]: Leaving directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' >>>> generating development specfile ... make[1]: Entering directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' make[1]: Leaving directory `~/linux/BUILD/lcfg-basic-1.1.1_dev' /bin/bash: specfile: No such file or directory >>>> building development rpm ... error: Name field must be present in package: (main package) error: Version field must be present in package: (main package) error: Release field must be present in package: (main package) error: Summary field must be present in package: (main package) error: Group field must be present in package: (main package) error: License field must be present in package: (main package) make: *** [devrpm] Error 1 %A spec file can be created using the version in
lcfg-example
, but this cannot be used as-is, because it
contains component-specific entries.
As we're only constructing a simple component, only a few changes are necessary:
make
now objects to the lack of a
"template
" file. This can be created by copying
"template.cin
" from lcfg-example
, and
editing as required:
% cat template.cin ############################################################### # # Basic LCFG Component Template File # # @AUTHOR@ # Version @VERSION@ : @DATE@ # # @MSG@ # ############################################################### server = <%server%> %Using this template file, the building of the test RPM gets a little further, but
make
now objects to the lack of a
"component.def
" file. This can be created by using
the .def.cin
file from lcfg-example
, and
modifying as required:
% cat ../lcfg-example/example.def.cin /* * LCFG example component : default resources * * @AUTHOR@ * Version: @VERSION@ @DATE@ (Schema @SCHEMA@) * * @MSG@ * */ #include "ngeneric-1.def" #include "om-1.def" schema @SCHEMA@ server foo.bar.com template @LCFGDATA@/@COMP@/template configfile @CONFIGDIR@/config %Also, at this point, added two README files (referred to in the %files section of the spec file). These are not absolutely essential, although their use is recommended, but - in a full component - should provide information for the build.
lcfg-example
RPM
(but see also the dice-example
and
lcfg-skeleton
RPMs). This supplies:
% rpm -ql lcfg-example-1.2.5 /usr/lib/lcfg/components/example /usr/lib/lcfg/conf/example /usr/lib/lcfg/conf/example/template /usr/lib/lcfg/defaults/client/example-1.def /usr/lib/lcfg/doc/pod/example.pod /usr/share/doc/lcfg-example-1.2.5 /usr/share/doc/lcfg-example-1.2.5/ChangeLog /usr/share/doc/lcfg-example-1.2.5/Makefile /usr/share/doc/lcfg-example-1.2.5/README /usr/share/doc/lcfg-example-1.2.5/README.BUILD /usr/share/doc/lcfg-example-1.2.5/config.mk /usr/share/doc/lcfg-example-1.2.5/example.cin /usr/share/doc/lcfg-example-1.2.5/example.pod /usr/share/doc/lcfg-example-1.2.5/specfile /usr/share/man/man8/lcfg-example.8.gz /var/lcfg/conf/example %To create a new component,
simple
, for testing, we can
rename a copy of the lcfg-example directory to lcfg-simple:
% cvs co lcfg-example cvs checkout: Updating lcfg-example U lcfg-example/ChangeLog U lcfg-example/Makefile U lcfg-example/README U lcfg-example/README.BUILD U lcfg-example/config.mk U lcfg-example/example.cin U lcfg-example/example.pod U lcfg-example/specfile % mv lcfg-example lcfg-simple % mv lcfg-simple/example.cin lcfg-simple/simple.cin % mv lcfg-simple/example.pod lcfg-simple/simple.pod %and edit the
simple.cin
file. In this example, the
component-specific variable names in the sxprof
command
are amended, and the default start method is redefined to just give
simple run-time information:
Start() { Info "Starting my component" Info "My arguments are $*" Info "My server resource is $LCFG foo server" Info "The verbose flag is $_VERBOSE" }Other files that need to be edited are:
config.mk |
variable substitution file |
---|---|
|
|
simple.pod |
man-page documentation |
|
|
specfile |
RPM build file |
|
|
README |
general info (if appropriate) |
|
The config.mk
file needs to be edited, to supply revised
values for COMP, DESCR,
& TARFILE
(Version, Revision, and Schema definitions might also need to be
changed to match whatever component is being created - although not in
this example).
The simple.pod
file needs to be edited to reflect
documentation for the new component.
The specfile
file needs to be edited to reflect the RPM
build constraints. In this example, "%description" and "%doc" sections
are modified. Other build parameters are generated by variable
substitution from config.sh
.
Using the edited files, the Makefile target "configure" generates the
component script from the .cin
file, using a variable
substitution file (config.sh
) created by the
Makefile-included /usr/include/buildtools.mk
file.
% make configure >>>> configuring simple ... >>>> creating lcfg-simple.8 ... %A manpage is similarly created (from a
.pod
file), also
by the included /usr/include/buildtools.mk
).
Once the configuration is completed, the Makefile target "install" moves the generated files into place:
% make install echo installing ... mkdir -p /usr/lib/lcfg/components mkdir -p /usr/lib/lcfg/doc/pod mkdir -p /usr/lib/lcfg/defaults/server mkdir -p /usr/lib/lcfg/defaults/client mkdir -p /usr/share/man/man8 mkdir -p /usr/lib/lcfg/conf/simple mkdir -p /var/lcfg/conf/simple install -m 0555 simple /usr/lib/lcfg/components/simple install -m 0555 template /usr/lib/lcfg/conf/simple/template install -m 0444 lcfg-simple.8 \ /usr/share/man/man8/lcfg-simple.8 install -m 0444 simple.pod /usr/lib/lcfg/doc/pod/simple.pod install -m 0444 simple.def \ /usr/lib/lcfg/defaults/server/simple-1.def install -m 0444 simple.def \ /usr/lib/lcfg/defaults/client/simple-1.def %
.cin
file. This new method needs both a
method definition and a Method_
method
definition. Consider the following simple example:
###################################################################### Method_HelloWorld() { ###################################################################### HelloWorld } export -f Method_HelloWorld ###################################################################### HelloWorld() { ###################################################################### # simplistic new method Info "Hello World!" }- note that this is not sufficient, however. It is also necessary to add the new method name to the list of allowable methods for that component, which is held in the component
.om_methods
resource. To do this, an
entry needs to be made in the component.def.cin
file (and hence component.def
):
!om_methods mADD(helloworld)- and, since the mutation (mutex) syntax is not available to the component, this needs to be explicitly added to component
.def.cin
too:
#include "mutate.h"(note that any changes to the defaults file will require the copy on the LCFG master server to be updated - usually done by re-installing the defaults RPM).
This should be sufficient to add the new method to the component:
% qxprof basic.om_methods om_methods=configure start stop restart run logrotate monitor \ reset unlock status helloworld %- which can then be invoked in the usual way:
% om basic helloworld [INFO] basic: Hello World! [OK] basic: helloworld %
% rpm -qpl ~/linux/RPMS/noarch/lcfg-basic-defaults-s1-1.1.1_dev-3.noarch.rpm /usr/lib/lcfg/defaults/server/basic-1.def % % rpm -qpl ~/linux/RPMS/noarch/lcfg-basic-1.1.1_dev-3.noarch.rpm /usr/lib/lcfg/components/basic /usr/lib/lcfg/conf/basic /usr/lib/lcfg/conf/basic/template /usr/lib/lcfg/defaults/client/basic-1.def /usr/lib/lcfg/doc/pod/basic.pod /usr/share/doc/lcfg-basic-1.1.1_dev /usr/share/doc/lcfg-basic-1.1.1_dev/README /usr/share/doc/lcfg-basic-1.1.1_dev/README.BUILD /usr/share/man/man8/lcfg-basic.8.gz /var/lcfg/conf/basic %When the testing version is complete, a new CVS release can be generated (which will create tags on the server) and "
make
rpm
" can be used to create the distribution RPMs. Note that
only distribution RPMs should be submitted (those built with
"make rpm
", and that development RPMs (those built with
"make devrpm
" and having "_dev
" in the name)
should not be submitted.
While testing, the make target "devinst
" (which bumps-up
the RPM release number) can be used to build and install the component
RPM. Note that - even for testing - the defaults file needs to be
installed on the LCFG master server (currently pointed to by the
lcfg-master
DNS alias).
% ssh lcfg-master tobermory% nsu tobermory# rpm -ivh ~/linux/RPMS/noarch/lcfg-basic-defaults-s1-1.1.1_dev-3.noarch.rpm Preparing... ########################################### [100%] 1:lcfg-basic-defaults-s1 ########################################### [100%] tobermory# ls -o /usr/lib/lcfg/defaults/server/basic-1.def -r--r--r-- 1 root 349 May 30 13:03 /usr/lib/lcfg/defaults/server/basic-1.def tobermory#While testing, the component and version resources need to be added to the profile (once testing is completed, they can be moved to the appropriate header file):
!profile.components mADD(basic) /* test component */ !profile.version_basic mSET(1)Once the RPMs are submitted and installed, the component RPM name should be added to the
profile.packages
resource usually
via rpmcfg
, but possibly by mADD
ing it via a
machine's profile (if, for example, in the final stages of testing).
Note that profile.packages
is a "logical" resource, and
is not accessible via qxprof
- it is constructed from RPM
package lists in the lcfg/core/packages
section of the
SVN repository.
Once installed, the component should be invoked via "om
":
% om basic start [OK] basic: start % ls -o /var/lcfg/tmp/basic.run -rw-r--r-- 1 root 0 May 31 08:48 /var/lcfg/tmp/basic.run % % om basic stop [OK] basic: stop %Note that version numbers (when using "
make release
") are
significant (see
DICE
Guidelines document for more details), and should consist of three
components (X.Y.Z), which are: