8ef2456e95
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@6785 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
441 lines
16 KiB
Perl
Executable File
441 lines
16 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
# Builds the xCAT database man pages from the descriptions that are contained
|
|
# in Schema.pm. This script is run during the build of the perl-xCAT rpm, but
|
|
# is not packaged in the binary form of that rpm.
|
|
|
|
# We assume that this script is run in the perl-xCAT-2.0 dir, so everything is
|
|
# done relative to that.
|
|
|
|
use strict;
|
|
use lib '.';
|
|
|
|
use xCAT::Schema;
|
|
use xCAT::Table;
|
|
use Pod::Man;
|
|
use Pod::Html;
|
|
|
|
my $poddir = 'pods';
|
|
my $mandir = 'share/man';
|
|
my $htmldir = 'share/doc';
|
|
my $cachedir = '/tmp';
|
|
|
|
my $poddir5 = 'pods/man5';
|
|
my $poddir7 = 'pods/man7';
|
|
if (system("mkdir -p $poddir5")) { die "Error: could not create $poddir5.\n"; }
|
|
if (system("mkdir -p $poddir7")) { die "Error: could not create $poddir7.\n"; }
|
|
|
|
# Build the DB overview page.
|
|
print "Building PODs pages for the database tables...\n";
|
|
writesummarypage("$poddir5/xcatdb.5.pod", xCAT::Table->getDescriptions(), \%{xCAT::Schema::defspec});
|
|
|
|
# Build the pad man page for each object definition
|
|
my $defspecref = \%{xCAT::Schema::defspec};
|
|
foreach my $defkey (keys %$defspecref) {
|
|
my $def = $defspecref->{$defkey};
|
|
my $attrs = $def->{'attrs'};
|
|
writedefmanpage("$poddir7/$defkey.7.pod", $defkey, $attrs);
|
|
}
|
|
|
|
# Build the pod man page for each table.
|
|
my $tabspecref = \%xCAT::Schema::tabspec;
|
|
foreach my $tablekey (keys %$tabspecref) {
|
|
my $table = $tabspecref->{$tablekey};
|
|
my $summary = $table->{table_desc};
|
|
my $colorder = $table->{cols};
|
|
my $descriptions = $table->{descriptions};
|
|
writepodmanpage("$poddir5/$tablekey.5.pod", $tablekey, $summary, $colorder, $descriptions);
|
|
}
|
|
|
|
my @pods = getPodList($poddir);
|
|
#foreach (@pods) { print "$_\n"; } exit;
|
|
|
|
# Build the man page for each pod.
|
|
#mkdir($mandir) or die "Error: could not create $mandir.\n";
|
|
print "Converting PODs to man pages...\n";
|
|
foreach my $podfile (@pods) {
|
|
my $manfile = $podfile;
|
|
$manfile =~ s/^$poddir/$mandir/; # change the beginning of the path
|
|
$manfile =~ s/\.pod$//; # change the ending
|
|
my $mdir = $manfile;
|
|
$mdir =~ s|/[^/]*$||; # get rid of the basename part
|
|
if (system("mkdir -p $mdir")) { die "Error: could not create $mdir.\n"; }
|
|
my ($section) = $podfile =~ /\.(\d+)\.pod$/;
|
|
convertpod2man($podfile, $manfile, $section);
|
|
}
|
|
|
|
# Build the html page for each pod.
|
|
#mkdir($htmldir) or die "Error: could not create $htmldir.\n";
|
|
print "Converting PODs to HTML pages...\n";
|
|
# have to clear the cache, because old entries can cause a problem
|
|
unlink("$cachedir/pod2htmd.tmp", "$cachedir/pod2htmi.tmp");
|
|
foreach my $podfile (@pods) {
|
|
my $htmlfile = $podfile;
|
|
$htmlfile =~ s/^$poddir/$htmldir/; # change the beginning of the path
|
|
$htmlfile =~ s/\.pod$/\.html/; # change the ending
|
|
my $hdir = $htmlfile;
|
|
$hdir =~ s|/[^/]*$||; # get rid of the basename part
|
|
if (system("mkdir -p $hdir")) { die "Error: could not create $hdir.\n"; }
|
|
convertpod2html($podfile, $htmlfile, $poddir, $htmldir);
|
|
}
|
|
|
|
exit;
|
|
|
|
|
|
# Recursively get the list of pod man page files.
|
|
sub getPodList {
|
|
my $poddir = shift;
|
|
my @files;
|
|
|
|
# 1st get toplevel dir listing
|
|
opendir(DIR, $poddir) or die "Error: could not read $poddir.\n";
|
|
my @topdir = grep !/^\./, readdir(DIR); # /
|
|
close(DIR);
|
|
|
|
# Now go thru each subdir (these are man1, man3, etc.)
|
|
foreach my $mandir (@topdir) {
|
|
opendir(DIR, "$poddir/$mandir") or die "Error: could not read $poddir/$mandir.\n";
|
|
my @dir = grep !/^\./, readdir(DIR); # /
|
|
close(DIR);
|
|
foreach my $file (@dir) {
|
|
push @files, "$poddir/$mandir/$file";
|
|
}
|
|
}
|
|
return sort @files;
|
|
}
|
|
|
|
|
|
# Create the html page for one pod.
|
|
sub convertpod2html {
|
|
my ($podfile, $htmlfile, $poddir, $htmldir) = @_;
|
|
|
|
#TODO: use --css=<stylesheet> and --title=<pagetitle> to make the pages look better
|
|
pod2html($podfile,
|
|
"--outfile=$htmlfile",
|
|
"--podpath=man5:man7",
|
|
"--podroot=$poddir",
|
|
"--htmldir=$htmldir",
|
|
"--recurse",
|
|
"--cachedir=$cachedir",
|
|
);
|
|
|
|
}
|
|
|
|
|
|
# Create the man page for one pod.
|
|
sub convertpod2man {
|
|
my ($podfile, $manfile, $section) = @_;
|
|
|
|
my $parser = Pod::Man->new(section => $section);
|
|
$parser->parse_from_file($podfile, $manfile);
|
|
}
|
|
|
|
|
|
# Create the xcatdb man page that gives a summary description of each table.
|
|
sub writesummarypage {
|
|
my $file = shift; # relative path file name of the man page
|
|
my $descriptions = shift; # a hash containing the description of each table
|
|
my $defdescriptions = shift; # a hash containing the description of each object definition
|
|
|
|
open(FILE, ">$file") or die "Error: could not open $file for writing.\n";
|
|
|
|
print FILE <<'EOS1';
|
|
=head1 NAME
|
|
|
|
An overview of the xCAT database.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The xCAT database contains user settings for the cluster and information gathered from the cluster.
|
|
It consists of a series of tables, which are described below. To get more information about a
|
|
particular table, run man for that table name. The tables can be manipulated directly using the
|
|
B<tabedit> or B<chtab> commands. They can be viewed using B<nodels> or B<tabdump>.
|
|
|
|
Alternatively, the xCAT database can be viewed and edited as logical objects, instead of flat tables.
|
|
In this mode, xCAT takes care of which table each attribute should go in. To treat the database
|
|
as logical object definitions, use the commands: lsdef, mkdef, chdef, rmdef. See Object Definitions
|
|
below.
|
|
|
|
xCAT allows the use of different database applications, depending on the needs of your cluster.
|
|
The default database is SQLite, which is a daemonless, zero-config database. But you could instead
|
|
choose to use something like postgresql for greater scalability and remote access in the
|
|
hierarchical/service node case. To use a different database or a different location, create
|
|
the file /etc/xcat/cfgloc.
|
|
See the appropriate xCAT docuementation for the format of the file for the database you choose.
|
|
The following example is for PostgreSQL:
|
|
|
|
|
|
=over 4
|
|
|
|
=item SQLite:/var/xcat/cfgloc
|
|
|
|
=item Pg:dbname=xcat;host=<mgmtnode>|<pgadminuserid>|<pgadminpasswd>
|
|
|
|
where mgmtnode is the hostname of the management node adapter on the cluster side, and the pgadminuserid and pgadminpasswd are the database admin and password.
|
|
|
|
=back
|
|
|
|
The xCAT database spans a number of tables, some with records associated with particular nodes
|
|
(such as nodelist and nodehm) and others that do not have a direct relationship with any given node.
|
|
The tables not associated with a given node are straightforward, the data is stored and retrieved
|
|
as-is from the database without interpretation, and without any generic inheritance
|
|
(though some calling code may implement inheritance for specific fields, for example
|
|
nodehm.power inheriting from nodehm.mgt).
|
|
|
|
The tables with records typically retrieved by node name have some extra features to enable a more
|
|
template-based style to be used:
|
|
|
|
Any group name can be used in lieu of a node name in the node field, and that record will then
|
|
be taken to be applicable to any node in that group. If a field is requested for a specific node,
|
|
and either a record doesn't exist specifically for that nodename or a record exists, but has no
|
|
definition for the requested field, that node's groups are then used to search for
|
|
records. If multiple records could apply from two different groups, the precedence is
|
|
the order the groups are specified in the nodelist table for that node. This is nearly identical to
|
|
most xCAT 1.x tab file conventions. This is useful in tables such as noderes, where typical
|
|
configurations have exactly the same field values for large sets of nodes.
|
|
|
|
xCAT 2 extends the above to be made useful where a field will vary for every node with a given tag,
|
|
but in ways that would be trivial to describe. If a field is of the format /I<pattern>/I<replacement>/,
|
|
it is taken to be a perl regular expression, to be performed on the nodename. For example, the bmc field
|
|
of the ipmi table might be B</\z/-bmc/> for a record with node=ipmi to specify that the BMC hostname is derived
|
|
by appending B<-bmc> to the end of the nodename of every node in the ipmi group.
|
|
|
|
As an extension to the above, a regular expression extended with arithmetic operators is available,
|
|
by using the format |I<pattern>|I<replacement>|. This behaves similarly to the above, but () enclosed parts
|
|
in I<replacement> are taken to signify arithmetic operations and substituted in. All operations are integer
|
|
arithmetic, so 5/4 would come out as 1. The typical perl positional variables are available in such expressions.
|
|
|
|
For example, if you have many blades in your cluster and their hostnames have a regular
|
|
pattern of blade1, blade2, etc., and your BladeCenter management modules also have a hostname
|
|
pattern of amm1, amm2, etc., then your B<mp> table could be expressed by the following single row:
|
|
|
|
"blade","|\D+(\d+)|amm(($1-1)/14+1)|","|\D+(\d+)|(($1-1)%14+1)|",,
|
|
|
|
Before you panic, let me explain each column:
|
|
|
|
=over 4
|
|
|
|
=item B<blade>
|
|
|
|
This is a group name. In this example, we are assuming that all of your blades belong to this
|
|
group. Each time the xCAT software accesses the B<mp> table to get the management module and slot number
|
|
of a specific blade (e.g. B<blade20>), this row will match (because B<blade20> is in the B<blade> group).
|
|
Once this row is matched for B<blade20>, then the processing described in the following items will take
|
|
place.
|
|
|
|
=item B<|\D+(\d+)|amm(($1-1)/14+1)|>
|
|
|
|
This is a perl substitution pattern that will produce the value for the second column of the table (the
|
|
management module hostname).
|
|
The text B<\D+(\d+)> between the 1st two vertical bars is
|
|
a regular expression that matches the node
|
|
name that was searched for in this table (in this example B<blade20>). The text that matches
|
|
within the 1st set of parentheses is set to $1. (If there was a 2nd set of parentheses, it would
|
|
be set to $2, and so on.) In our case, the \D+ matches the non-numeric part of the name
|
|
(B<blade>) and the \d+ matches the numeric part (B<20>). So $1 is set to B<20>. The text B<amm(($1-1)/14+1)> between the
|
|
2nd and 3rd vertical bars produces the string that should be used as the value
|
|
for this column in a hypothetical row for blade20. Since $1 is set to 20, the expression B<($1-1)/14+1> equals
|
|
19/14 + 1, which equals 2. Therefore the whole string is B<amm2>, which will be used as the hostname
|
|
of the management module.
|
|
|
|
=item B<|\D+(\d+)|(($1-1)%14+1)|>
|
|
|
|
This item is similar to the one above. This substituion pattern will produce the value for
|
|
the 3rd column (the BladeCenter chassis slot number for this blade). Because this row was
|
|
the match for B<blade20>, the parentheses
|
|
within the 1st set of vertical bars will set $1 to 20. Since % means modulo division, the
|
|
expression B<($1-1)%14+1> will evaluate to 6.
|
|
|
|
=back
|
|
|
|
See http://www.perl.com/doc/manual/html/pod/perlre.html for information on perl regular expressions.
|
|
|
|
=head1 OBJECT DEFINITIONS
|
|
|
|
Because it can get confusing what attributes need to go in what tables, the xCAT database can also
|
|
be viewed and edited as logical objects, instead of flat tables. Use B<mkdef>, B<chdef>, B<lsdef>,
|
|
and B<rmdef> to create, change, list, and delete objects.
|
|
When using these commands, the object attributes will be stored in the same tables, as if you edited
|
|
the tables by hand. The only difference is that the object commands take care of knowing which tables
|
|
all of the information should go in.
|
|
|
|
To run man for any of the object definitions below, use section 7. For example: B<man 7 node>
|
|
|
|
The object types are:
|
|
|
|
=over 2
|
|
EOS1
|
|
|
|
foreach my $def (sort keys %$defdescriptions) {
|
|
print FILE "\n=item L<$def(7)|$def.7>\n";
|
|
}
|
|
|
|
print FILE <<"EOS2";
|
|
|
|
=back
|
|
|
|
=head1 TABLES
|
|
|
|
To manipulate the tables directly, use B<nodels(1)>, B<chtab(8)>, B<tabdump(8)>, B<tabedit(8)>,
|
|
B<nodeadd(8)>, B<nodech(1)>.
|
|
|
|
To run man for any of the table descriptions below, use section 5. For example: B<man 5 nodehm>
|
|
|
|
The tables are:
|
|
|
|
=over 2
|
|
EOS2
|
|
|
|
foreach my $table (sort keys %$descriptions) {
|
|
print FILE "\n=item L<$table(5)|$table.5>\n\n".$descriptions->{$table}."\n";
|
|
}
|
|
|
|
print FILE <<"EOS3";
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
B<nodels(1)>, B<chtab(8)>, B<tabdump(8)>, B<tabedit(8)>, B<lsdef(1)>, B<mkdef(1)>, B<chdef(1)>, B<rmdef(1)>
|
|
EOS3
|
|
|
|
close FILE;
|
|
}
|
|
|
|
|
|
# Create the man page for one object definition.
|
|
sub writedefmanpage {
|
|
my $file = shift; # relative path file name of the man page
|
|
my $defname = shift; # name of object
|
|
my $attrs = shift; # reference to the array of attributes
|
|
|
|
# Go thru the attributes, collecting the descriptions
|
|
# Note: this logic is loosely taken from DBobjectdefs.pm
|
|
my %attrlist; # holds the attr name as the key, and the description & tables as value
|
|
foreach my $this_attr (@$attrs) {
|
|
my $attr = $this_attr->{attr_name};
|
|
my $desc = $this_attr->{description};
|
|
my ($table, $at) = split(/\./, $this_attr->{tabentry});
|
|
if (!defined($desc)) {
|
|
# description key not there, so go to the corresponding
|
|
# entry in tabspec to get the description
|
|
my $schema = xCAT::Table->getTableSchema($table);
|
|
$desc = $schema->{descriptions}->{$at};
|
|
}
|
|
|
|
# Attr names can appear more than once, if they are in multiple tables.
|
|
# We will keep track of that based on the table attribute, because that can be duplicated too
|
|
if (!defined($attrlist{$attr})) {
|
|
$attrlist{$attr}->{'tables'} = []; # initialize the array, so we can check it below
|
|
}
|
|
my $tableattr = "$table.$at";
|
|
if (!grep(/^$tableattr$/, @{$attrlist{$attr}->{'tables'}})) {
|
|
# there can be multiple entries that refer to the same table attribute
|
|
# if this is a new table attribute, then save the attr name and description
|
|
push @{$attrlist{$attr}->{'tables'}}, $tableattr;
|
|
push @{$attrlist{$attr}->{'descriptions'}}, $desc;
|
|
}
|
|
}
|
|
|
|
open(FILE, ">$file") or die "Error: could not open $file for writing.\n";
|
|
|
|
print FILE <<"EOS1";
|
|
=head1 NAME
|
|
|
|
B<$defname> - a logical object definition in the xCAT database.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
EOS1
|
|
|
|
print FILE "B<$defname Attributes:> I<" . join('>, I<',sort(keys(%attrlist))) . ">\n";
|
|
|
|
print FILE <<"EOS2";
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Logical objects of this type are stored in the xCAT database in one or more tables. Use the following commands
|
|
to manipulate the objects: B<mkdef>, B<chdef>, B<lsdef>, and B<rmdef>. These commands will take care of
|
|
knowing which tables the object attributes should be stored in. The attribute list below shows, in
|
|
parentheses, what tables each attribute is stored in.
|
|
|
|
=head1 $defname Attributes:
|
|
|
|
=over 6
|
|
|
|
EOS2
|
|
|
|
foreach my $a (sort keys %attrlist) {
|
|
my $d = join("\nor\n", @{$attrlist{$a}->{'descriptions'}});
|
|
$d =~ s/\n/\n\n/sg; # if there are newlines, double them so pod sees a blank line, otherwise pod will ignore them
|
|
my $t = '(' . join(', ',@{$attrlist{$a}->{'tables'}}) . ')';
|
|
#print FILE "\nB<$a> - $d\n";
|
|
print FILE "\n=item B<$a> $t\n\n$d\n";
|
|
}
|
|
|
|
print FILE <<"EOS3";
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
B<mkdef(1)>, B<chdef(1)>, B<lsdef(1)>, B<rmdef(1)>
|
|
EOS3
|
|
|
|
close FILE;
|
|
}
|
|
|
|
|
|
# Create the man page for one table.
|
|
sub writepodmanpage {
|
|
my $file = shift; # relative path file name of the man page
|
|
my $tablename = shift; # name of table
|
|
my $summary = shift; # description of table
|
|
my $colorder = shift; # the order in which the table attributes should be presented in
|
|
my $descriptions = shift; # a hash containing the description of each attribute
|
|
|
|
open(FILE, ">$file") or die "Error: could not open $file for writing.\n";
|
|
|
|
print FILE <<"EOS1";
|
|
=head1 NAME
|
|
|
|
B<$tablename> - a table in the xCAT database.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
EOS1
|
|
|
|
print FILE "B<$tablename Attributes:> I<" . join('>, I<',@$colorder) . ">\n";
|
|
|
|
print FILE <<"EOS2";
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
$summary
|
|
|
|
=head1 $tablename Attributes:
|
|
|
|
=over 10
|
|
|
|
EOS2
|
|
|
|
foreach my $a (@$colorder) {
|
|
my $d = $descriptions->{$a};
|
|
#$d =~ s/\n/\n\n/sg; # if there are newlines, double them so pod sees a blank line, otherwise pod will ignore them
|
|
#print FILE "\nB<$a> - $d\n";
|
|
print FILE "\n=item B<$a>\n\n$d\n";
|
|
}
|
|
|
|
print FILE <<"EOS3";
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
B<nodels(1)>, B<chtab(8)>, B<tabdump(8)>, B<tabedit(8)>
|
|
EOS3
|
|
|
|
close FILE;
|
|
}
|