465 lines
9.5 KiB
Perl
465 lines
9.5 KiB
Perl
|
#!/usr/bin/perl
|
||
|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||
|
|
||
|
package DSH;
|
||
|
use base xCAT::DSHContext;
|
||
|
use xCAT::MsgUtils;
|
||
|
use File::Path;
|
||
|
|
||
|
|
||
|
# Configure Node group path from environment
|
||
|
|
||
|
our $nodegroup_path = $ENV{'DSH_NODEGROUP_PATH'};
|
||
|
|
||
|
=head3
|
||
|
context_defaults
|
||
|
|
||
|
Assign default properties for the DSH context. A default
|
||
|
property for a context will be used if the property is
|
||
|
not user configured in any other way.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
A reference to a hash table with the configured
|
||
|
default properties for the DSH context
|
||
|
|
||
|
Globals:
|
||
|
None
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
$default_properties = DSH->config_defaults;
|
||
|
|
||
|
Comments:
|
||
|
$defaults hash table contents:
|
||
|
|
||
|
$defaults{'NodeRemoteShell'} - default remote shell to use for node targets
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub context_defaults {
|
||
|
my %defaults = ();
|
||
|
|
||
|
if ( $ENV{'DSH_NODE_RSH'} ) {
|
||
|
my @remoteshell_list = split ',', $ENV{'DSH_NODE_RSH'};
|
||
|
|
||
|
foreach $context_remoteshell (@remoteshell_list) {
|
||
|
my ( $context, $remoteshell ) = split ':', $context_remoteshell;
|
||
|
|
||
|
if ( !$remoteshell ) {
|
||
|
$remoteshell = $context;
|
||
|
!$defaults{'NodeRemoteShell'}
|
||
|
&& ( $defaults{'NodeRemoteShell'} = $remoteshell );
|
||
|
}
|
||
|
|
||
|
elsif ( $context eq 'DSH' ) {
|
||
|
$defaults{'NodeRemoteShell'} = $remoteshell;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !$defaults{'NodeRemoteShell'} ) {
|
||
|
my $dsh_context_defaults = xCAT::DSHContext->context_defaults;
|
||
|
$defaults{'NodeRemoteShell'} =
|
||
|
$$dsh_context_defaults{'NodeRemoteShell'};
|
||
|
}
|
||
|
|
||
|
return \%defaults;
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
context_properties
|
||
|
|
||
|
Configure the user specified context properties for the DSH context.
|
||
|
These properties are configured by the user through environment
|
||
|
variables or external configuration files.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
A reference to a hash table of user-configured properties for
|
||
|
the DSH context.
|
||
|
|
||
|
Globals:
|
||
|
None
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
$properties = DSH->config_properties
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub context_properties {
|
||
|
my %properties = ();
|
||
|
|
||
|
$properties{'DCP_DEVICE_OPTS'} = $ENV{'DCP_DEVICE_OPTS'};
|
||
|
$properties{'DCP_DEVICE_RCP'} = $ENV{'DCP_DEVICE_RCP'}
|
||
|
|| $ENV{'DCP_DEVICE_COPY_CMD'};
|
||
|
$properties{'DCP_NODE_OPTS'} = $ENV{'DCP_NODE_OPTS'};
|
||
|
$properties{'DCP_NODE_RCP'} = $ENV{'DCP_NODE_RCP'} || $ENV{'DCP_COPY_CMD'};
|
||
|
$properties{'DSH_CONTEXT'} = $ENV{'DSH_CONTEXT'};
|
||
|
$properties{'DSH_DEVICE_LIST'} = $ENV{'DSH_DEVICE_LIST'};
|
||
|
$properties{'DSH_DEVICE_OPTS'} = $ENV{'DSH_DEVICE_OPTS'}
|
||
|
|| $ENV{'DSH_DEVICE_REMOTE_OPTS'};
|
||
|
$properties{'DSH_DEVICE_RCP'} = $ENV{'DSH_DEVICE_RCP'};
|
||
|
$properties{'DSH_DEVICE_RSH'} = $ENV{'DSH_DEVICE_RSH'}
|
||
|
|| $ENV{'DSH_DEVICE_REMOTE_CMD'};
|
||
|
$properties{'DSH_ENVIRONMENT'} = $ENV{'DSH_ENVIRONMENT'};
|
||
|
$properties{'DSH_FANOUT'} = $ENV{'DSH_FANOUT'};
|
||
|
$properties{'DSH_LOG'} = $ENV{'DSH_LOG'};
|
||
|
$properties{'DSH_NODEGROUP_PATH'} = $ENV{'DSH_NODEGROUP_PATH'};
|
||
|
$properties{'DSH_NODE_LIST'} = $ENV{'DSH_NODE_LIST'}
|
||
|
|| $ENV{'DSH_LIST'}
|
||
|
|| $ENV{'WCOLL'};
|
||
|
$properties{'DSH_NODE_OPTS'} = $ENV{'DSH_NODE_OPTS'}
|
||
|
|| $ENV{'DSH_REMOTE_OPTS'};
|
||
|
$properties{'DSH_NODE_RCP'} = $ENV{'DSH_NODE_RCP'};
|
||
|
$properties{'DSH_NODE_RSH'} = $ENV{'DSH_NODE_RSH'}
|
||
|
|| $ENV{'DSH_REMOTE_SHELL'}
|
||
|
|| $ENV{'DSH_REMOTE_CMD'};
|
||
|
$properties{'DSH_OUTPUT'} = $ENV{'DSH_OUTPUT'};
|
||
|
$properties{'DSH_PATH'} = $ENV{'DSH_PATH'};
|
||
|
$properties{'DSH_REPORT'} = $ENV{'DSH_REPORT'}
|
||
|
|| $ENV{'DSH_REPORTS_DIRECTORY'};
|
||
|
$properties{'DSH_SYNTAX'} = $ENV{'DSH_SYNTAX'};
|
||
|
$properties{'DSH_TIMEOUT'} = $ENV{'DSH_TIMEOUT'};
|
||
|
$properties{'RSYNC_RSH'} = $ENV{'RSYNC_RSH'};
|
||
|
|
||
|
if($ENV{'DSH_ON_HMC'}){
|
||
|
$properties{'DSH_NODE_RCP'} = '/usr/hmcrbin/scp';
|
||
|
$properties{'DSH_NODE_RSH'} = '/usr/hmcrbin/ssh';
|
||
|
}
|
||
|
return \%properties;
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
all_nodegroups
|
||
|
|
||
|
Returns an array of all node group names in the DSH context
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
An array of node group names
|
||
|
|
||
|
Globals:
|
||
|
$nodegroup_path
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
@nodegroups = DSH->all_nodegroups;
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub all_nodegroups {
|
||
|
my @nodegroups = ();
|
||
|
|
||
|
if ($nodegroup_path) {
|
||
|
opendir( DIR, $nodegroup_path );
|
||
|
|
||
|
while ( my $nodegroup = readdir(DIR) ) {
|
||
|
( $nodegroup !~ /^\./ ) && push @nodegroups, $nodegroup;
|
||
|
}
|
||
|
|
||
|
closedir DIR;
|
||
|
}
|
||
|
|
||
|
return @nodegroups;
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
nodegroup_members
|
||
|
|
||
|
Given a node group in the DSH context, this routine expands the
|
||
|
membership of the node group and returns a list of its members.
|
||
|
|
||
|
Arguments:
|
||
|
$nodegroup - node group name
|
||
|
|
||
|
Returns:
|
||
|
An array of node group members
|
||
|
|
||
|
Globals:
|
||
|
$nodegroup_path
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
$members = DSH->nodegroup_members('MyGroup1');
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub nodegroup_members {
|
||
|
my ( $class, $nodegroup ) = @_;
|
||
|
|
||
|
my %resolved_nodes = ();
|
||
|
my %unresolved_nodes = ();
|
||
|
|
||
|
my $nodes = DSH->read_target_file("$nodegroup_path/$nodegroup");
|
||
|
|
||
|
!$nodes && return undef;
|
||
|
|
||
|
my @members = ();
|
||
|
|
||
|
foreach $node (@$nodes) {
|
||
|
if ( $node =~ /@/ ) {
|
||
|
xCAT::MsgUtils->message("E",
|
||
|
"$node is not a valid name for group $nodegroup");
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
push @members, $node;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DSHContext->resolve_hostnames( \%resolved_nodes, \%unresolved_nodes,
|
||
|
@members );
|
||
|
|
||
|
@members = keys(%resolved_nodes);
|
||
|
return \@members;
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
all_nodes
|
||
|
|
||
|
Returns an array of all node names in the DSH context
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Returns:
|
||
|
An array of node names
|
||
|
|
||
|
Globals:
|
||
|
$nodegroup_path
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
@nodes = DSH->all_nodes;
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub all_nodes {
|
||
|
my $build_cache = undef;
|
||
|
|
||
|
if ( -e "$ENV{'HOME'}/.dsh/$nodegroup_path/AllNodes" ) {
|
||
|
my @stat_path = stat $nodegroup_path;
|
||
|
my @stat_allnodes =
|
||
|
stat "$ENV{'HOME'}/.dsh/$nodegroup_path/AllNodes.dsh";
|
||
|
|
||
|
if ( $stat_path[9] > $stat_allnodes[9] ) {
|
||
|
$build_cache = 1;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
if ($nodegroup_path) {
|
||
|
opendir( DIR, $nodegroup_path );
|
||
|
|
||
|
while ( my $nodegroup = readdir(DIR) ) {
|
||
|
|
||
|
if ( $nodegroup !~ /^\./ ) {
|
||
|
my @stat_file = stat $nodegroup;
|
||
|
( $stat_file[9] > $stat_allnodes[9] )
|
||
|
&& ( $build_cache = 1 );
|
||
|
}
|
||
|
|
||
|
last if $build_cache;
|
||
|
}
|
||
|
|
||
|
closedir DIR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
$build_cache = 1;
|
||
|
}
|
||
|
|
||
|
if ($build_cache) {
|
||
|
my @nodegroups = DSH->all_nodegroups;
|
||
|
|
||
|
my @nodes = ();
|
||
|
|
||
|
foreach $nodegroup (@nodegroups) {
|
||
|
push @nodes, @{ DSH->nodegroup_members($nodegroup) };
|
||
|
}
|
||
|
|
||
|
if ( !( -d "$ENV{'HOME'}/.dsh/$nodegroup_path" ) ) {
|
||
|
eval { mkpath( "$ENV{'HOME'}/.dsh/$nodegroup_path") };
|
||
|
if ($@) {
|
||
|
xCAT::MsgUtils->message(
|
||
|
"E",
|
||
|
" Cannot make directory: $ENV{'HOME'}/.dsh/$nodegroup_path\n");
|
||
|
return undef;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DSH->write_target_file( "$ENV{'HOME'}/.dsh/$nodegroup_path/AllNodes",
|
||
|
@nodes );
|
||
|
return @nodes;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
my $nodes =
|
||
|
DSH->read_target_file("$ENV{'HOME'}/.dsh/$nodegroup_path/AllNodes");
|
||
|
return @$nodes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
read_target_file
|
||
|
|
||
|
Processes the given filename and stores all targets in the file in an
|
||
|
array
|
||
|
|
||
|
Arguments:
|
||
|
$filename - file to read target names from
|
||
|
|
||
|
Returns:
|
||
|
A reference to an array of target names
|
||
|
|
||
|
Globals:
|
||
|
None
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
DSH->read_target_file('/tmp/target_file');
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub read_target_file {
|
||
|
my ( $class, $filename ) = @_;
|
||
|
|
||
|
my %targets = ();
|
||
|
|
||
|
if ( open( FILE, $filename ) ) {
|
||
|
|
||
|
while ( my $target = <FILE> ) {
|
||
|
$target =~ /^\s*#/ && next;
|
||
|
$target =~ /^\s*$/ && next;
|
||
|
$target =~ /;/ && next;
|
||
|
$target =~ /\S+\s+\S+/ && next;
|
||
|
$target =~ s/\s+$//;
|
||
|
chomp($target);
|
||
|
$targets{$target}++;
|
||
|
}
|
||
|
|
||
|
close FILE;
|
||
|
|
||
|
my @target_list = keys(%targets);
|
||
|
return \@target_list;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
xCAT::MsgUtils->message( "E", "Cannot open file: $filename\n");
|
||
|
return undef;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
=head3
|
||
|
write_target_file
|
||
|
|
||
|
Writes a list of supplied targets to a specified file. Each target name
|
||
|
is written to one line
|
||
|
|
||
|
Arguments:
|
||
|
$filename - file to write target names
|
||
|
@targets - array of target names to write
|
||
|
|
||
|
Returns:
|
||
|
None
|
||
|
|
||
|
Globals:
|
||
|
None
|
||
|
|
||
|
Error:
|
||
|
None
|
||
|
|
||
|
Example:
|
||
|
DSH->read_target_file('/tmp/target_file');
|
||
|
|
||
|
Comments:
|
||
|
|
||
|
=cut
|
||
|
|
||
|
sub write_target_file {
|
||
|
my ( $class, $filename, @targets ) = @_;
|
||
|
|
||
|
if ( open( FILE, ">$filename" ) ) {
|
||
|
|
||
|
print FILE "#\n";
|
||
|
print FILE "# DSH Utilities Target File\n";
|
||
|
print FILE "#\n";
|
||
|
|
||
|
foreach $target (@targets) {
|
||
|
print FILE "$target\n";
|
||
|
}
|
||
|
|
||
|
close FILE;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
xCAT::MsgUtils->message("E", "Error writing file $filename");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub query_node {
|
||
|
my ( $class, $node ) = @_;
|
||
|
my $res = 0;
|
||
|
|
||
|
$~ = "NODES";
|
||
|
NetworkUtils->tryHost( $node, \$res );
|
||
|
if ($res) {
|
||
|
print("$node : Valid\n");
|
||
|
}
|
||
|
else {
|
||
|
print("$node : Invalid\n");
|
||
|
}
|
||
|
|
||
|
format NODES =
|
||
|
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<
|
||
|
$node, $status
|
||
|
.
|
||
|
|
||
|
}
|
||
|
|
||
|
sub query_group {
|
||
|
my ( $class, $group ) = @_;
|
||
|
my @dsh_groups = all_nodegroups();
|
||
|
|
||
|
$~ = "GROUPS";
|
||
|
if ( grep( /^$group$/, @dsh_groups ) ) {
|
||
|
print("$group : Valid\n");
|
||
|
}
|
||
|
else {
|
||
|
print("$group : Invalid\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
1; #end
|