mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-06-13 01:40:26 +00:00
PCM discover initial draft ready
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@13904 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
@ -6,6 +6,7 @@ use warnings;
|
||||
use Socket;
|
||||
use File::Path qw/mkpath/;
|
||||
use File::Temp qw/tempfile/;
|
||||
use Fcntl qw(:flock);
|
||||
require xCAT::Table;
|
||||
require xCAT::TableUtils;
|
||||
require xCAT::NodeRange;
|
||||
@ -67,6 +68,7 @@ sub get_allocable_staticips_innet
|
||||
=head3 genhosts_with_numric_tmpl
|
||||
Description : Generate numric hostnames using numric template name.
|
||||
Arguments : $format - The hostname format string..
|
||||
$rank - The start number.
|
||||
Returns : numric hostname list
|
||||
Example :
|
||||
calling genhosts_with_numric_tmpl("compute#NNnode") will return a list like:
|
||||
@ -76,10 +78,10 @@ sub get_allocable_staticips_innet
|
||||
#-------------------------------------------------------------------------------
|
||||
sub genhosts_with_numric_tmpl
|
||||
{
|
||||
my ($class, $format) = @_;
|
||||
my ($class, $format, $rank) = @_;
|
||||
|
||||
my ($prefix, $appendix, $len) = xCAT::PCMNodeMgmtUtils->split_hostname($format, 'N');
|
||||
return xCAT::PCMNodeMgmtUtils->gen_numric_hostnames($prefix, $appendix, $len);
|
||||
return xCAT::PCMNodeMgmtUtils->gen_numric_hostnames($prefix, $appendix, $len, $rank);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
@ -139,10 +141,13 @@ sub split_hostname
|
||||
#-------------------------------------------------------------------------------
|
||||
sub gen_numric_hostnames
|
||||
{
|
||||
my ($class, $prefix, $appendix, $len) = @_;
|
||||
my ($class, $prefix, $appendix, $len, $rank) = @_;
|
||||
my @hostnames;
|
||||
my $cnt = 0;
|
||||
|
||||
my $cnt=0;
|
||||
if ($rank){
|
||||
$cnt = $rank;
|
||||
}
|
||||
my $maxnum = 10 ** $len;
|
||||
while($cnt < $maxnum)
|
||||
{
|
||||
@ -392,39 +397,56 @@ sub get_allnode_singleattrib_hash
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 acquire_lock
|
||||
Description : Create lock file for node management plugin so that there is
|
||||
no multi node mamangement plugins running.
|
||||
The file content will be the pid of the plugin running process.
|
||||
Once the plugin process terminated abnormally and lock file not removed,
|
||||
next time node management plugin runs, it will read the lock file
|
||||
and check whether this process is running or not. If not,
|
||||
just remove this lock file and create a new one.
|
||||
Arguments : N/A
|
||||
Returns : 1 - create lock success.
|
||||
0 - failed, there may be a node management process running.
|
||||
Description : Create lock file for PCM plugins so that there is
|
||||
no multi instance of plugins running at same time.
|
||||
The lock file content will be the pid of the plugin running process.
|
||||
Using perl's flock to achive this.
|
||||
Note: we can not judge whether PCM discovering is running or only
|
||||
through acquire_lock("nodemgmt")
|
||||
We must also call is_discover_started()
|
||||
Arguments : action name: for example: nodemgmt, imageprofile...etc We'll generate
|
||||
a lock file named as /var/lock/pcm/$action.
|
||||
Returns : -1 - Acquire lock failed.
|
||||
fh of lock file - the filehandler of lock file.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub acquire_lock
|
||||
{
|
||||
my $lockfile = "/var/lock/pcm/nodemgmt";
|
||||
my $class = shift;
|
||||
my $action = shift;
|
||||
my $lockdir = "/var/lock/pcm";
|
||||
mkdir "$lockdir", 0755 unless -d "$lockdir";
|
||||
my $lockfile = "$lockdir/$action";
|
||||
|
||||
if (-e $lockfile) {
|
||||
open LOCKFILE, "<$lockfile";
|
||||
my @lines = <LOCKFILE>;
|
||||
my $pid = $lines[0];
|
||||
if (-d "/proc/$pid") {
|
||||
return 0;
|
||||
} else{
|
||||
# the process already not exists, remove lock file.
|
||||
File::Path->rmtree($lockfile);
|
||||
}
|
||||
mkdir "$lockdir", 0755 unless -d "$lockdir";
|
||||
open my $fh, ">$lockfile";
|
||||
# use flock, non-blocking mode while acquiring a lock.
|
||||
my $lockret = flock($fh, LOCK_EX|LOCK_NB);
|
||||
if(! $lockret){
|
||||
close $fh;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print $fh $$;
|
||||
return $fh;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 is_discover_started
|
||||
Description : Judge whether PCM discovering is running or not.
|
||||
Arguments : NA
|
||||
Returns : 1 - Discover is running
|
||||
0 - Discover is not started.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub is_discover_started
|
||||
{
|
||||
my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
|
||||
if (! $sitevalues[0]){
|
||||
return 0;
|
||||
}
|
||||
open LOCKFILE, ">$lockfile";
|
||||
print LOCKFILE $$;
|
||||
close LOCKFILE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -432,23 +454,18 @@ sub acquire_lock
|
||||
|
||||
=head3 release_lock
|
||||
Description : Release lock for node management process.
|
||||
Arguments : N/A
|
||||
Returns : N/A
|
||||
Arguments : fh - the lock file handler.
|
||||
Returns : return value of flock.
|
||||
True - release lock succeed.
|
||||
False - release lock failed.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub release_lock
|
||||
{
|
||||
my $lockfile = "/var/lock/pcm/nodemgmt";
|
||||
if (-e $lockfile){
|
||||
open LOCKFILE, "<$lockfile";
|
||||
my @lines = <LOCKFILE>;
|
||||
my $pid = $lines[0];
|
||||
close LOCKFILE;
|
||||
if ($pid ne $$){
|
||||
File::Path->rmtree($lockfile);
|
||||
}
|
||||
}
|
||||
my $class = shift;
|
||||
my $lockfh = shift;
|
||||
return flock($lockfh, LOCK_UN|LOCK_NB);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
@ -70,56 +70,95 @@ sub process_request {
|
||||
|
||||
my $nodelist = $request->{node};
|
||||
my $retref;
|
||||
my $rsp;
|
||||
|
||||
if($command eq 'kitcmd_nodemgmt_add')
|
||||
{
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1);
|
||||
# Work around for makedns bug, it will set umask to 0007.
|
||||
#umask(0022);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
my $firstnode = (@$nodelist)[0];
|
||||
my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]);
|
||||
my %profilehash = %$profileref;
|
||||
if (exists $profilehash{$firstnode}{"ImageProfile"}){
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update nodes' boot settings";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}, $request_command, 0, 1);
|
||||
}
|
||||
|
||||
}
|
||||
elsif ($command eq 'kitcmd_nodemgmt_remove'){
|
||||
$retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['offline']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist, arg=>['-r']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1);
|
||||
# Work around for makedns bug, it will set umask to 0007.
|
||||
#umask(0022);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist, arg=>['-d']}, $request_command, 0, 1);
|
||||
}
|
||||
elsif ($command eq 'kitcmd_nodemgmt_update'){
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1);
|
||||
# Work around for makedns bug, it will set umask to 0007.
|
||||
#umask(0022);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
my $firstnode = (@$nodelist)[0];
|
||||
my $profileref = xCAT::PCMNodeMgmtUtils->get_nodes_profiles([$firstnode]);
|
||||
my %profilehash = %$profileref;
|
||||
if (exists $profilehash{$firstnode}{"ImageProfile"}){
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update nodes' boot settings";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["nodeset"], node=>$nodelist, arg=>['osimage='.$profilehash{$firstnode}{"ImageProfile"}]}, $request_command, 0, 1);
|
||||
}
|
||||
}
|
||||
elsif ($command eq 'kitcmd_nodemgmt_refresh'){
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating hosts entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makehosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist, arg=>['-n']}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating DNS entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makedns"], node=>$nodelist}, $request_command, 0, 1);
|
||||
# Work around for makedns bug, it will set umask to 0007.
|
||||
#umask(0022);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update DHCP entries";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makekdhcp"], node=>$nodelist}, $request_command, 0, 1);
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Update known hosts";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makeknownhosts"], node=>$nodelist}, $request_command, 0, 1);
|
||||
}
|
||||
elsif ($command eq 'kitcmd_nodemgmt_finished')
|
||||
{
|
||||
$rsp->{info}->[0] = "[PCM nodes mgmt]Updating conserver configuration files";
|
||||
$::CALLBACK->($rsp);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["makeconservercf"]}, $request_command, 0, 1);
|
||||
}
|
||||
else
|
||||
|
@ -61,6 +61,10 @@ sub handled_commands {
|
||||
addhost_discover => 'pcmnodes',
|
||||
removehost => 'pcmnodes',
|
||||
updatehost => 'pcmnodes',
|
||||
pcmdiscover_start => 'pcmnodes',
|
||||
pcmdiscover_stop => 'pcmnodes',
|
||||
pcmdiscover_nodels => 'pcmnodes',
|
||||
findme => 'pcmnodes',
|
||||
};
|
||||
}
|
||||
|
||||
@ -76,16 +80,29 @@ sub handled_commands {
|
||||
#-------------------------------------------------------
|
||||
sub process_request {
|
||||
|
||||
my $lock = xCAT::PCMNodeMgmtUtils->acquire_lock();
|
||||
unless ($lock){
|
||||
setrsp_errormsg("Can not acquire lock, some process is operating node related actions.");
|
||||
return;
|
||||
}
|
||||
$request = shift;
|
||||
$callback = shift;
|
||||
$::CALLBACK = $callback;
|
||||
$request_command = shift;
|
||||
$command = $request->{command}->[0];
|
||||
$args = $request->{arg};
|
||||
|
||||
|
||||
my $lockfh = xCAT::PCMNodeMgmtUtils->acquire_lock("nodemgmt");
|
||||
if ($lockfh == -1){
|
||||
setrsp_errormsg("Can not acquire lock, some process is operating node related actions.");
|
||||
return;
|
||||
}
|
||||
|
||||
# These commands should make sure no discover is running.
|
||||
if (grep{ $_ eq $command} ("addhost_hostfile", "removehost", "updatehost")){
|
||||
my $discover_running = xCAT::PCMNodeMgmtUtils->is_discover_started();
|
||||
if ($discover_running){
|
||||
setrsp_errormsg("Can not run command $command as PCM discover is running.");
|
||||
xCAT::PCMNodeMgmtUtils->release_lock($lockfh);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($command eq "addhost_hostfile"){
|
||||
addhost_hostfile()
|
||||
@ -93,8 +110,17 @@ sub process_request {
|
||||
removehost();
|
||||
} elsif ($command eq "updatehost"){
|
||||
updatehost();
|
||||
} elsif ($command eq "pcmdiscover_start"){
|
||||
pcmdiscover_start();
|
||||
} elsif ($command eq "pcmdiscover_nodels"){
|
||||
pcmdiscover_nodels();
|
||||
} elsif ($command eq "findme"){
|
||||
findme();
|
||||
} elsif ($command eq "pcmdiscover_stop"){
|
||||
pcmdiscover_stop();
|
||||
}
|
||||
xCAT::PCMNodeMgmtUtils->release_lock($lock);
|
||||
|
||||
xCAT::PCMNodeMgmtUtils->release_lock($lockfh);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
@ -157,39 +183,49 @@ sub parse_args{
|
||||
sub addhost_hostfile {
|
||||
|
||||
# Parse arges.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Import PCM nodes through hostinfo file.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Import PCM nodes through hostinfo file.");
|
||||
my $retstr = parse_args();
|
||||
if ($retstr){
|
||||
setrsp_errormsg($retstr);
|
||||
return;
|
||||
}
|
||||
# Make sure the specified parameters are valid ones.
|
||||
# TODO: support privisioning template.
|
||||
my @enabledparams = ('file', 'groups', 'networkprofile', 'hardwareprofile', 'imageprofile');
|
||||
my @enabledparams = ('file', 'groups', 'networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat');
|
||||
foreach my $argname (keys %args_dict){
|
||||
if (! grep{ $_ eq $argname} @enabledparams){
|
||||
setrsp_errormsg("Illegal attribute $argname specified.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
# validate hostinfo file.
|
||||
if (! exists $args_dict{'file'}){
|
||||
setrsp_errormsg("No hostinfo file specified.");
|
||||
return;
|
||||
# Mandatory arguments.
|
||||
foreach (('file','networkprofile', 'imageprofile', 'hostnameformat')){
|
||||
if(! exists($args_dict{$_})){
|
||||
setrsp_errormsg("Mandatory parameter $_ not specified.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
elsif(! (-e $args_dict{'file'})){
|
||||
|
||||
if(! (-e $args_dict{'file'})){
|
||||
setrsp_errormsg("The hostinfo file not exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
# Get database records: all hostnames, all ips, all racks...
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Getting database records.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Getting database records.");
|
||||
my $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('nodelist', 'node');
|
||||
%allhostnames = %$recordsref;
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
|
||||
%allbmcips = %$recordsref;
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('mac', 'mac');
|
||||
%allmacs = %$recordsref;
|
||||
# MAC records looks like: "01:02:03:04:05:0E!node5│01:02:03:05:0F!node6-eth1". We want to get the real mac addres.
|
||||
foreach (keys %allmacs){
|
||||
my @hostentries = split(/\|/, $_);
|
||||
foreach my $hostandmac ( @hostentries){
|
||||
my ($macstr, $machostname) = split("!", $hostandmac);
|
||||
$allmacs{$macstr} = 0;
|
||||
}
|
||||
}
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('hosts', 'ip');
|
||||
%allinstallips = %$recordsref;
|
||||
$recordsref = xCAT::NetworkUtils->get_all_nicips(1);
|
||||
@ -198,13 +234,14 @@ sub addhost_hostfile {
|
||||
# Merge all BMC IPs and install IPs into allips.
|
||||
%allips = (%allips, %allbmcips, %allinstallips);
|
||||
|
||||
#my $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname');
|
||||
#%allracks = %$recordsref;
|
||||
#my $recordsref = xCAT::PCMNodeMgmtUtils->get_allchassis(1);
|
||||
#%allchassis = %$recordsref;
|
||||
#TODO: can not use getallnode to get rack infos.
|
||||
#$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname');
|
||||
%allracks = ();
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_all_chassis(1);
|
||||
%allchassis = %$recordsref;
|
||||
|
||||
# Generate temporary hostnames for hosts entries in hostfile.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generate temporary hostnames.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Generate temporary hostnames.");
|
||||
my ($retcode_read, $retstr_read) = read_and_generate_hostnames($args_dict{'file'});
|
||||
if ($retcode_read != 0){
|
||||
setrsp_errormsg($retstr_read);
|
||||
@ -212,7 +249,7 @@ sub addhost_hostfile {
|
||||
}
|
||||
|
||||
# Parse and validate the hostinfo string. The real hostnames will be generated here.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Parsing hostinfo string and validate it.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Parsing hostinfo string and validate it.");
|
||||
my ($hostinfo_dict_ref, $invalid_records_ref) = parse_hosts_string($retstr_read);
|
||||
my %hostinfo_dict = %$hostinfo_dict_ref;
|
||||
my @invalid_records = @$invalid_records_ref;
|
||||
@ -226,18 +263,18 @@ sub addhost_hostfile {
|
||||
}
|
||||
|
||||
# Create the real hostinfo string in stanza file format.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Generating new hostinfo string.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Generating new hostinfo string.");
|
||||
my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_string(\%hostinfo_dict);
|
||||
unless ($retcode_gen){
|
||||
setrsp_errormsg($retstr_gen);
|
||||
return;
|
||||
}
|
||||
# call mkdef to create hosts and then call nodemgmt for node management plugins.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call mkdef to create pcm nodes.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]call mkdef to create pcm nodes.");
|
||||
my $retref = xCAT::Utils->runxcmd({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}, $request_command, 0, 1);
|
||||
|
||||
my @nodelist = keys %hostinfo_dict;
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins.");
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_add"], node=>\@nodelist}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>\@nodelist}, $request_command, 0, 1);
|
||||
setrsp_success(\@nodelist);
|
||||
@ -255,12 +292,12 @@ sub addhost_hostfile {
|
||||
#-------------------------------------------------------
|
||||
sub removehost{
|
||||
my $nodes = $request->{node};
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Remove PCM nodes.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Remove PCM nodes.");
|
||||
# For remove nodes, we should call 'nodemgmt' in front of 'noderm'
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins.");
|
||||
my $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_remove"], node=>$nodes}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}, $request_command, 0, 1);
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call noderm to remove nodes.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]call noderm to remove nodes.");
|
||||
$retref = xCAT::Utils->runxcmd({command=>["noderm"], node=>$nodes}, $request_command, 0, 1);
|
||||
setrsp_success($nodes);
|
||||
}
|
||||
@ -279,7 +316,7 @@ sub updatehost{
|
||||
my $nodes = $request->{node};
|
||||
my %updated_groups;
|
||||
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update PCM nodes settings.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Update PCM nodes settings.");
|
||||
# Parse arges.
|
||||
my $retstr = parse_args();
|
||||
if ($retstr){
|
||||
@ -287,7 +324,6 @@ sub updatehost{
|
||||
return;
|
||||
}
|
||||
# Make sure the specified parameters are valid ones.
|
||||
# TODO: support privisioning template.
|
||||
my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile');
|
||||
foreach my $argname (keys %args_dict){
|
||||
if (! grep{ $_ eq $argname} @enabledparams){
|
||||
@ -297,7 +333,7 @@ sub updatehost{
|
||||
}
|
||||
|
||||
# Get current templates for all nodes.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Read database to get groups for all nodes.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Read database to get groups for all nodes.");
|
||||
my %groupdict;
|
||||
my $nodelstab = xCAT::Table->new('nodelist');
|
||||
my $nodeshashref = $nodelstab->getNodesAttribs($nodes, ['groups']);
|
||||
@ -326,13 +362,13 @@ sub updatehost{
|
||||
}
|
||||
|
||||
#update DataBase.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]Update database records.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]Update database records.");
|
||||
my $nodetab = xCAT::Table->new('nodelist',-create=>1);
|
||||
$nodetab->setNodesAttribs(\%updatenodeshash);
|
||||
$nodetab->close();
|
||||
|
||||
# call plugins
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n");
|
||||
setrsp_infostr("[PCM nodes mgmt]call nodemgmt plugins.");
|
||||
my $retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_update"], node=>$nodes}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>$nodes}, $request_command, 0, 1);
|
||||
setrsp_success($nodes);
|
||||
@ -340,6 +376,314 @@ sub updatehost{
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 pcmdiscover_start
|
||||
|
||||
Description : Start PCM discovery. If already started, return a failure.
|
||||
User should specify networkprofile, hardwareprofile,
|
||||
imageprofile, hostnameformat, rack, chassis, height and u so
|
||||
that node's IP address will be generated automatcially
|
||||
according to networkprofile, node's hardware settings will
|
||||
be set according to hardware profile, node's os settings will
|
||||
be set according to image profile, node's hostname will be
|
||||
set according to hostnameformat and rank. And other node's
|
||||
attribs will also be set according to rack, chassis, height and u.
|
||||
Arguments : N/A
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub pcmdiscover_start{
|
||||
# Parse arges.
|
||||
setrsp_infostr("[PCM nodes mgmt]PCM discovery started.");
|
||||
my $retstr = parse_args();
|
||||
if ($retstr){
|
||||
setrsp_errormsg($retstr);
|
||||
return;
|
||||
}
|
||||
|
||||
my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat', 'rank', 'rack', 'chassis', 'height', 'u');
|
||||
foreach my $argname (keys %args_dict){
|
||||
if (! grep{ $_ eq $argname} @enabledparams){
|
||||
setrsp_errormsg("Illegal attribute $argname specified.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
# mandatory arguments.
|
||||
foreach my $key ('networkprofile', 'imageprofile', 'hostnameformat'){
|
||||
if (! exists $args_dict{$key}){
|
||||
setrsp_errormsg("argument $key must be specified");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Read DB to confirm the discover is not started yet.
|
||||
my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
|
||||
if ($sitevalues[0]){
|
||||
setrsp_errormsg("PCM node discovery already started.");
|
||||
return;
|
||||
}
|
||||
|
||||
# save discover args into table site.
|
||||
my $valuestr = "";
|
||||
foreach (keys %args_dict){
|
||||
if($args_dict{$_}){
|
||||
$valuestr .= "$_:$args_dict{$_},";
|
||||
}
|
||||
}
|
||||
|
||||
my $sitetab = xCAT::Table->new('site',-create=>1);
|
||||
$sitetab->setAttribs({"key" => "__PCMDiscover"}, {"value" => "$valuestr"});
|
||||
$sitetab->close();
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 pcmdiscover_stop
|
||||
|
||||
Description : Stop PCM auto discover. This action will remove the
|
||||
dababase flags.
|
||||
Arguments : N/A
|
||||
|
||||
=cut
|
||||
|
||||
#------------------------------------------------------
|
||||
sub pcmdiscover_stop{
|
||||
# Read DB to confirm the discover is started.
|
||||
my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
|
||||
if (! $sitevalues[0]){
|
||||
setrsp_errormsg("PCM node discovery not started yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
# remove site table records: discover flag.
|
||||
my $sitetab=xCAT::Table->new("site");
|
||||
my %keyhash;
|
||||
$keyhash{'key'} = "__PCMDiscover";
|
||||
$sitetab->delEntries(\%keyhash);
|
||||
$sitetab->commit();
|
||||
|
||||
# Update node's attributes, remove from gruop "__PCMDiscover".
|
||||
# we'll call rmdef so that node's groupinfo in table nodelist will be updated automatically.
|
||||
my @nodes = xCAT::NodeRange::noderange('__PCMDiscover');
|
||||
if (@nodes){
|
||||
# There are some nodes discvoered.
|
||||
my $retref = xCAT::Utils->runxcmd({command=>["rmdef"], arg=>["-t", "group", "-o", "__PCMDiscover"]}, $request_command, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 pcmdiscover_nodels
|
||||
|
||||
Description : List all discovered PCM nodes.
|
||||
Arguments : N/A
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub pcmdiscover_nodels{
|
||||
# Read DB to confirm the discover is started.
|
||||
my @sitevalues = ();
|
||||
@sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
|
||||
if (! $sitevalues[0]){
|
||||
setrsp_errormsg("PCM node discovery not started yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
my @nodes = xCAT::NodeRange::noderange('__PCMDiscover');
|
||||
my $mactab = xCAT::Table->new("mac");
|
||||
my $macsref = $mactab->getNodesAttribs(\@nodes, ['mac']);
|
||||
my $nodelisttab = xCAT::Table->new("nodelist");
|
||||
my $statusref = $nodelisttab->getNodesAttribs(\@nodes, ['status']);
|
||||
|
||||
my %rsp = ();
|
||||
my %rspentry = ();
|
||||
foreach (@nodes){
|
||||
if (! $_){
|
||||
next;
|
||||
}
|
||||
$rspentry{"node"} = $_;
|
||||
#TODO: get provisioning mac.
|
||||
$rspentry{"mac"} = $macsref->{$_}->[0]->{"mac"};
|
||||
|
||||
if ($statusref->{$_}->[0]){
|
||||
$rspentry{"status"} = $statusref->{$_}->[0];
|
||||
} else{
|
||||
$rspentry{"status"} = "defined";
|
||||
}
|
||||
$callback->(\%rspentry);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 findme
|
||||
|
||||
Description : The default interface for node discovery.
|
||||
We must implement this method so that
|
||||
PCM nodes's findme request can be answered
|
||||
while PCM discovery is running.
|
||||
Arguments : N/A
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub findme{
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: Start.\n");
|
||||
# Read DB to confirm the discover is started.
|
||||
my @sitevalues = xCAT::TableUtils->get_site_attribute("__PCMDiscover");
|
||||
if (! @sitevalues){
|
||||
setrsp_errormsg("PCM node discovery not started yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
# We store node profiles in site table, key is "__PCMDiscover"
|
||||
my @profilerecords = split(',', $sitevalues[0]);
|
||||
foreach (@profilerecords){
|
||||
if ($_){
|
||||
my ($profilename, $profilevalue) = split(':', $_);
|
||||
if ($profilename and $profilevalue){
|
||||
$args_dict{$profilename} = $profilevalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Get database records: all hostnames, all ips, all racks...
|
||||
# To improve performance, we should initalize a daemon later??
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: Getting database records.\n");
|
||||
my $recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('nodelist', 'node');
|
||||
%allhostnames = %$recordsref;
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('ipmi', 'bmc');
|
||||
%allbmcips = %$recordsref;
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('mac', 'mac');
|
||||
%allmacs = %$recordsref;
|
||||
foreach (keys %allmacs){
|
||||
my @hostentries = split(/\|/, $_);
|
||||
foreach my $hostandmac ( @hostentries){
|
||||
my ($macstr, $machostname) = split("!", $hostandmac);
|
||||
$allmacs{$macstr} = 0;
|
||||
}
|
||||
}
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('hosts', 'ip');
|
||||
%allinstallips = %$recordsref;
|
||||
$recordsref = xCAT::NetworkUtils->get_all_nicips(1);
|
||||
%allips = %$recordsref;
|
||||
# Merge all BMC IPs and install IPs into allips.
|
||||
%allips = (%allips, %allbmcips, %allinstallips);
|
||||
|
||||
#$recordsref = xCAT::PCMNodeMgmtUtils->get_allnode_singleattrib_hash('rack', 'rackname');
|
||||
#%allracks = %$recordsref;
|
||||
%allracks = ();
|
||||
$recordsref = xCAT::PCMNodeMgmtUtils->get_all_chassis(1);
|
||||
%allchassis = %$recordsref;
|
||||
|
||||
my @enabledparams = ('networkprofile', 'hardwareprofile', 'imageprofile', 'hostnameformat', 'rack', 'chassis', 'u', 'height', 'rank');
|
||||
foreach my $argname (keys %args_dict){
|
||||
if (! grep{ $_ eq $argname} @enabledparams){
|
||||
setrsp_errormsg("Illegal attribute $argname specified.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
# mandatory arguments.
|
||||
foreach my $key ('networkprofile', 'imageprofile', 'hostnameformat'){
|
||||
if (! exists $args_dict{$key}){
|
||||
setrsp_errormsg("argument $key must be specified");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# set default value for rack, startunit and height if not specified.
|
||||
if (exists $args_dict{'rack'}){
|
||||
if (! exists $allracks{$args_dict{'rack'}}){
|
||||
setrsp_errormsg("Specified rack $args_dict{'rack'} not defined");
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
# set default rack.
|
||||
#TODO : how to set default rack.
|
||||
}
|
||||
if (!exists $args_dict{'u'}){
|
||||
$args_dict{'u'} = 1;
|
||||
}
|
||||
if (!exists $args_dict{'height'}){
|
||||
$args_dict{'height'} = 1;
|
||||
}
|
||||
# chassis jdugement.
|
||||
if (exists $args_dict{'chassis'}){
|
||||
if (! exists $allchassis{$args_dict{'chassis'}}){
|
||||
setrsp_errormsg("Specified chassis $args_dict{'chassis'} not defined");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Get discovered client IP and MAC
|
||||
my $ip = $request->{'_xcat_clientip'};
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: _xcat_clientip is $ip.\n");
|
||||
my $mac = '';
|
||||
my $arptable = `/sbin/arp -n`;
|
||||
my @arpents = split /\n/,$arptable;
|
||||
foreach (@arpents) {
|
||||
if (m/^($ip)\s+\S+\s+(\S+)\s/) {
|
||||
$mac=$2;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if (! $mac){
|
||||
setrsp_errormsg("[PCM nodes mgmt]PCM discover: Can not get mac address of this node.");
|
||||
return;
|
||||
}
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: mac is $mac.\n");
|
||||
if ( exists $allmacs{$mac}){
|
||||
setrsp_errormsg("Discovered MAC $mac already exists in database.");
|
||||
return;
|
||||
}
|
||||
|
||||
# Assign TMPHOSTS9999 as a temporary hostname, in parse_hsots_string,
|
||||
# it will detect this and arrange a real hostname for it.
|
||||
my $raw_hostinfo_str = "TMPHOSTS9999:\n mac=$mac\n";
|
||||
# Append rack, chassis, unit, height into host info string.
|
||||
foreach my $key ('rack', 'chassis', 'u', 'height'){
|
||||
if(exists($args_dict{$key})){
|
||||
$raw_hostinfo_str .= " $key=$args_dict{$key}\n";
|
||||
}
|
||||
}
|
||||
my ($hostinfo_dict_ref, $invalid_records_ref) = parse_hosts_string($raw_hostinfo_str);
|
||||
my %hostinfo_dict = %$hostinfo_dict_ref;
|
||||
# Create the real hostinfo string in stanza file format.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]PCM discover: Generating new hostinfo string.\n");
|
||||
my ($retcode_gen, $retstr_gen) = gen_new_hostinfo_string($hostinfo_dict_ref);
|
||||
unless ($retcode_gen){
|
||||
setrsp_errormsg($retstr_gen);
|
||||
return;
|
||||
}
|
||||
|
||||
# call mkdef to create hosts and then call nodemgmt for node management plugins.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call mkdef to create pcm nodes.\n");
|
||||
my $retref = xCAT::Utils->runxcmd({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}, $request_command, 0, 1);
|
||||
# increase unit automatically.
|
||||
$args_dict{'u'} = $args_dict{'u'} + $args_dict{'height'};
|
||||
|
||||
my @nodelist = keys %hostinfo_dict;
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call nodemgmt plugins.\n");
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_add"], node=>\@nodelist}, $request_command, 0, 1);
|
||||
$retref = xCAT::Utils->runxcmd({command=>["kitcmd_nodemgmt_finished"], node=>\@nodelist}, $request_command, 0, 1);
|
||||
|
||||
# call discover to notify client.
|
||||
xCAT::MsgUtils->message('S', "[PCM nodes mgmt]call discovered request.\n");
|
||||
$request->{"command"} = ["discovered"];
|
||||
$request->{"node"} = \@nodelist;
|
||||
$retref = xCAT::Utils->runxcmd($request, $request_command, 0, 1);
|
||||
|
||||
# Set discovered flag.
|
||||
my $nodegroupstr = $hostinfo_dict{$nodelist[0]}{"groups"};
|
||||
my $nodelstab = xCAT::Table->new('nodelist',-create=>1);
|
||||
$nodelstab->setNodeAttribs($nodelist[0],{groups=>$nodegroupstr.",__PCMDiscover"});
|
||||
$nodelstab->close();
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 replace_item_in_array
|
||||
|
||||
Description : Replace an item in a list with new value. This item should match specified pattern.
|
||||
@ -528,9 +872,7 @@ sub parse_hosts_string{
|
||||
my %hostinfo_dict;
|
||||
my @invalid_records;
|
||||
|
||||
my $nicstab = xCAT::Table->new('nics');
|
||||
my $nodehashref = $nicstab->getNodeAttribs($args_dict{'networkprofile'}, ['hostnameformat']);
|
||||
my $nameformat = $nodehashref->{'hostnameformat'};
|
||||
my $nameformat = $args_dict{'hostnameformat'};
|
||||
|
||||
my $nameformattype = xCAT::PCMNodeMgmtUtils->get_hostname_format_type($nameformat);
|
||||
my %freehostnames;
|
||||
@ -580,7 +922,11 @@ sub parse_hosts_string{
|
||||
|
||||
# Generate hostnames based on numric hostname format.
|
||||
if (! exists $freehostnames{$numricformat}){
|
||||
$freehostnames{$numricformat} = xCAT::PCMNodeMgmtUtils->genhosts_with_numric_tmpl($numricformat);
|
||||
my $rank = 0;
|
||||
if (exists($args_dict{'rank'})){
|
||||
$rank = $args_dict{'rank'};
|
||||
}
|
||||
$freehostnames{$numricformat} = xCAT::PCMNodeMgmtUtils->genhosts_with_numric_tmpl($numricformat, $rank);
|
||||
}
|
||||
my $hostnamelistref = $freehostnames{$numricformat};
|
||||
my $nexthostname = shift @$hostnamelistref;
|
||||
@ -651,19 +997,28 @@ sub validate_node_entry{
|
||||
}elsif ($_ eq "switch"){
|
||||
#TODO: xCAT switch discovery enhance: verify whether switch exists.
|
||||
}elsif ($_ eq "port"){
|
||||
#TODO: xCAT switch discovery enhance: verify whether port exists.
|
||||
}elsif ($_ eq "rack"){
|
||||
if (not exists $allracks{$node_entry{$_}}){
|
||||
return "Specified rack $node_entry{$_} not defined";
|
||||
}
|
||||
# rack must be specified with chassis or unit + height.
|
||||
if (exists $node_entry{"chassis"}){
|
||||
} elsif (exists $node_entry{"height"} and exists $node_entry{"u"}){
|
||||
} else {
|
||||
return "Rack must be specified together with chassis or height + u ";
|
||||
}
|
||||
}elsif ($_ eq "chassis"){
|
||||
if (not exists $allchassis{$node_entry{$_}}){
|
||||
return "Specified chassis $node_entry{$_} not defined";
|
||||
}
|
||||
}elsif ($_ eq "unit"){
|
||||
# Chassis must not be specified with unit and height.
|
||||
if (exists $node_entry{"height"} and exists $node_entry{"u"}){
|
||||
return "Chassis should not be specified together with height + u";
|
||||
}
|
||||
}elsif ($_ eq "u"){
|
||||
# Not a valid number.
|
||||
if (!($node_entry{$_} =~ /^\d+$/)){
|
||||
return "Specified unit $node_entry{$_} is a invalid number";
|
||||
return "Specified u $node_entry{$_} is a invalid number";
|
||||
}
|
||||
}elsif ($_ eq "height"){
|
||||
# Not a valid number.
|
||||
@ -674,15 +1029,6 @@ sub validate_node_entry{
|
||||
return "Invalid attribute $_ specified";
|
||||
}
|
||||
}
|
||||
# For blades, don't support specify unit and height.
|
||||
if(exists $node_entry{"chassis"} ){
|
||||
if(exists $node_entry{"unit"}){
|
||||
return "Can not specify 'unit' together with 'chassis'";
|
||||
}
|
||||
if(exists $node_entry{"height"}){
|
||||
return "can not specify 'height' together with 'chassis'";
|
||||
}
|
||||
}
|
||||
# push hostinfo into global dicts.
|
||||
$allhostnames{$node_name} = 0;
|
||||
return undef;
|
||||
@ -706,6 +1052,7 @@ sub setrsp_invalidrecords
|
||||
my $master=xCAT::TableUtils->get_site_Master();
|
||||
|
||||
# The total number of invalid records.
|
||||
$rsp->{error} = "Some error records detected";
|
||||
$rsp->{invalid_records_num} = scalar @$recordsref;
|
||||
|
||||
# We write details of invalid records into a file.
|
||||
@ -740,6 +1087,26 @@ sub setrsp_errormsg
|
||||
$callback->($rsp);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 setrsp_infostr
|
||||
|
||||
Description : Set response for a info string.
|
||||
Arguments : infostr - The info string..
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub setrsp_infostr
|
||||
{
|
||||
my $infostr = shift;
|
||||
my $rsp;
|
||||
xCAT::MsgUtils->message('S', "$infostr\n");
|
||||
$rsp->{info}->[0] = $infostr;
|
||||
$callback->($rsp);
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 setrsp_success
|
||||
|
Reference in New Issue
Block a user