mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-07-28 07:11:11 +00:00
Add rest api helper interface to access non-xcat resource
Currently xcat rest service is running with apache user who do not have the privilege to manage some local resource like credential files needed by xcat web ui to get the conserver access. This patch add scripts in xcatconfig to generate credential files for conserver user. This patch also add a work around framework for xcat restapi to provide the interface to get or set the system resource so that restapi with apache privilege can retrieve the credential files.
This commit is contained in:
211
xCAT-server/lib/xcat/plugins/localrest.pm
Normal file
211
xCAT-server/lib/xcat/plugins/localrest.pm
Normal file
@@ -0,0 +1,211 @@
|
||||
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head1
|
||||
xCAT plugin package to non xcat resource
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
package xCAT_plugin::localrest;
|
||||
|
||||
BEGIN {
|
||||
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
|
||||
}
|
||||
use lib "$::XCATROOT/lib/perl";
|
||||
|
||||
use xCAT::Utils;
|
||||
use xCAT::MsgUtils;
|
||||
use File::Basename;
|
||||
use strict;
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 handled_commands
|
||||
|
||||
Return list of commands handled by this plugin
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
sub handled_commands
|
||||
{
|
||||
return {
|
||||
localrest => "localrest",
|
||||
};
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 process_request
|
||||
|
||||
Process the command.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub process_request
|
||||
{
|
||||
my $request = shift;
|
||||
$::callback = shift;
|
||||
my $subreq = shift;
|
||||
my $command = $request->{command}->[0];
|
||||
|
||||
if ($command eq "localrest") {
|
||||
return handle_rest_request($request, $subreq);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 handle_rest_request
|
||||
|
||||
This function check the command option, then call the
|
||||
related function to complete the request.
|
||||
|
||||
Usage example:
|
||||
This function is called from process_request,
|
||||
do not call it directly.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
sub handle_rest_request {
|
||||
my ($request, $subreq) = @_;
|
||||
my ($method, $resource, @params, $subroutine, $rsp, $rc);
|
||||
require JSON;
|
||||
my $JSON = JSON->new();
|
||||
|
||||
my @args = @{ $request->{arg} };
|
||||
if (scalar(@args) < 2) {
|
||||
$rsp->{data}->[0] = "Local rest api take at least two parameter.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
$method = shift @args;
|
||||
$resource = shift @args;
|
||||
$subroutine = $method . '_' . $resource;
|
||||
@params = @args;
|
||||
|
||||
# if related subroutine found, call it
|
||||
# subroutine for rest handler must return a ref to HASH or ARRAY
|
||||
# comtaining the data that should be return to the CGI
|
||||
if (__PACKAGE__->can({$subroutine})) {
|
||||
$rsp->{data}->[0] = "Unsupported request: $subroutine.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
no strict 'refs';
|
||||
my $result = $subroutine->(@params);
|
||||
|
||||
# handle the result from the rest subroutine
|
||||
if (ref($result) eq 'HASH') {
|
||||
if (defined($result->{'type'}) && $result->{'type'} eq 'stream'
|
||||
&& defined($result->{'filename'})) {
|
||||
$rsp->{data}->[0] = "stream";
|
||||
$rsp->{data}->[1] = $result->{'filename'};
|
||||
$rsp->{data}->[2] = $result->{'data'};
|
||||
} else {
|
||||
my $json = $JSON->encode($result);
|
||||
$rsp->{data}->[0] = "json";
|
||||
$rsp->{data}->[1] = $json;
|
||||
}
|
||||
xCAT::MsgUtils->message("I", $rsp, $::callback);
|
||||
$rc = 0;
|
||||
} elsif (ref($result) eq 'ARRAY') {
|
||||
my $json = $JSON->encode($result);
|
||||
$rsp->{data}->[0] = "json";
|
||||
$rsp->{data}->[1] = $json;
|
||||
xCAT::MsgUtils->message("I", $rsp, $::callback);
|
||||
$rc = 0;
|
||||
} elsif ($result == 1 || $result == 0) {
|
||||
$rc = $result;
|
||||
} else {
|
||||
$rc = 1;
|
||||
$rsp->{data}->[0] = "Internal error, result value is unacceptable";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
}
|
||||
return $rc;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 handler to list network adapters
|
||||
|
||||
Subroutine to handle rest request
|
||||
GET /localres/adapter/
|
||||
|
||||
Usage example:
|
||||
This function is called from handle_rest_request,
|
||||
do not call it directly.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub list_adapter {
|
||||
my ($rsp, $result, $i);
|
||||
if (!opendir DIR, "/sys/class/net") {
|
||||
$rsp->{data}->[0] = "Unable open /sys/class/net dir.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
my @dir = readdir(DIR);
|
||||
closedir(DIR);
|
||||
$i = 0;
|
||||
foreach my $item (@dir) {
|
||||
if ($item eq '.' || $item eq '..') {
|
||||
next;
|
||||
}
|
||||
$result->[ $i++ ] = $item;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 handler to download credential files
|
||||
|
||||
Subroutine to handle rest request
|
||||
GET /localres/credential/conserver/file
|
||||
GET /localres/credential/ca/file
|
||||
|
||||
Usage example:
|
||||
This function is called from handle_rest_request,
|
||||
do not call it directly.
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub download_credential {
|
||||
my @params = @_;
|
||||
my ($rsp, $buf, $fpath, $fd, $data, $result, $n);
|
||||
if (!@params) {
|
||||
$rsp->{data}->[0] = "Argmument error.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
my %filemap = (
|
||||
'conserver' => "/home/conserver/.xcat/client-cred.pem",
|
||||
'ca' => "/home/conserver/.xcat/ca.pem",
|
||||
);
|
||||
$fpath = $filemap{ $params[0] };
|
||||
if (!$fpath || !-e $fpath) {
|
||||
$rsp->{data}->[0] = "File resource for " . $params[0] . " unavailable.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!($n = open($fd, '<', $fpath))) {
|
||||
$rsp->{data}->[0] = "Coundn't open file $fpath.";
|
||||
xCAT::MsgUtils->message("E", $rsp, $::callback);
|
||||
return 1;
|
||||
}
|
||||
while ($n = read($fd, $buf, 8192)) {
|
||||
$data .= $buf;
|
||||
}
|
||||
close($fd);
|
||||
$result->{'type'} = 'stream';
|
||||
$result->{'filename'} = basename($fpath);
|
||||
$result->{'data'} = $data;
|
||||
return $result;
|
||||
}
|
||||
1;
|
@@ -448,12 +448,6 @@ if ($::INITIALINSTALL || $::FORCE || $::UPDATEINSTALL || $::genCredentials)
|
||||
}
|
||||
else
|
||||
{
|
||||
#since the xcatd service unit file is updated on xcat upgrade
|
||||
#"systemctl daemon-reload" need to be run to update the service unit
|
||||
if ($::UPDATEINSTALL){
|
||||
$xcmd="type systemctl >/dev/null 2>&1 && systemctl daemon-reload";
|
||||
xCAT::Utils->runcmd("$xcmd", 0);
|
||||
}
|
||||
#$xcmd = "/etc/init.d/xcatd restart";
|
||||
xCAT::Utils->restartservice("xcatd");
|
||||
}
|
||||
@@ -1507,8 +1501,30 @@ sub checkotherpkgs
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
sub genCredentials
|
||||
|
||||
{
|
||||
my $add_user_func = sub {
|
||||
my $user = shift;
|
||||
my ($cmd, $outref, $rc);
|
||||
$rc = getgrnam($user);
|
||||
if (!$rc) {
|
||||
$cmd = "groupadd $user";
|
||||
$outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0) {
|
||||
xCAT::MsgUtils->message('E',"$cmd failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$rc = getpwnam($user);
|
||||
if (!$rc) {
|
||||
$cmd = "useradd -g $user -s /bin/bash -d /home/$user -m $user";
|
||||
$outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
if ($::RUNCMD_RC != 0) {
|
||||
xCAT::MsgUtils->message('E',"$cmd failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
my $hname = `hostname`;
|
||||
chomp $hname;
|
||||
if ((!-d "/etc/xcat/ca") || $::FORCE || $::genCredentials)
|
||||
@@ -1616,6 +1632,24 @@ sub genCredentials
|
||||
}
|
||||
}
|
||||
|
||||
if ((!-r "/home/conserver/.xcat/client-key.pem") || $::FORCE || $::genCredentials)
|
||||
{
|
||||
$add_user_func->('conserver');
|
||||
my $cmd =
|
||||
"echo 'y\ny\ny\ny' |$::XCATROOT/share/xcat/scripts/setup-local-client.sh conserver";
|
||||
verbose("Running $cmd");
|
||||
my $rc = system($cmd);
|
||||
if ($rc >> 8)
|
||||
{
|
||||
xCAT::MsgUtils->message('E',
|
||||
"Could not create xCAT certificate in /home/conserver/.xcat/client-key.pem.");
|
||||
}
|
||||
else
|
||||
{
|
||||
xCAT::MsgUtils->message('I', "Created xCAT certificate.");
|
||||
}
|
||||
}
|
||||
|
||||
# copy to postscript directory
|
||||
$cmd = "/bin/rm -rf $::INSTALLDIR/postscripts/_xcat >/dev/null 2>&1";
|
||||
$outref = xCAT::Utils->runcmd("$cmd", 0);
|
||||
|
@@ -1230,6 +1230,24 @@ my %URIdef = (
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
### interface to access local system resource which is not managed by xcat directly
|
||||
### localres can be looked as a top level non-xcat resource pool
|
||||
### rest operation will trasfer the target resource to xcatd plugin to handle the non-xcat resource
|
||||
localres => {
|
||||
localres => {
|
||||
desc => "[URI:/localres/*] - The local non-xcat resource.",
|
||||
matcher => '^/localres(/[^/]*)+$',
|
||||
GET => {
|
||||
desc => "List information for the target system resource.",
|
||||
usage => "||For target resource can match any resource type which can be proccssed by the resthelper plugin.|",
|
||||
example => qq(|List adapters on MN machine|GET|/localres/interface/|{\n \"interfaces\":[\n\"eth0\",\n \"eth1\",\n]"|),
|
||||
cmd => "localrest",
|
||||
fhandler => \&localreshdl,
|
||||
outhdler => \&localresout,
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# supported formats
|
||||
@@ -1288,6 +1306,7 @@ my @path = split(/\//, $pathInfo); # The uri path like /nodes/node1/...
|
||||
|
||||
# Define the golbal variables which will be used through the handling process
|
||||
my $pageContent = ''; # Global var containing the ouptut back to the rest client
|
||||
my %header_info; #Global var containing the extra info to the http header
|
||||
my $request = {clienttype => 'ws'}; # Global var that holds the request to send to xcatd
|
||||
my $format = 'json'; # The output format for a request invoke
|
||||
my $xmlinstalled; # Global var to speicfy whether the xml modules have been loaded
|
||||
@@ -1649,6 +1668,17 @@ sub defout_remove_appended_info {
|
||||
}
|
||||
|
||||
|
||||
sub localresout {
|
||||
my $data = shift;
|
||||
my $json;
|
||||
if ($data->[0]->{info}->[0] eq 'stream') {
|
||||
$format = 'stream';
|
||||
$header_info{'attachment'} = $data->[0]->{info}->[1];
|
||||
addPageContent($data->[0]->{info}->[2]);
|
||||
} elsif ($data->[0]->{info}->[0] eq 'json') {
|
||||
addPageContent($data->[0]->{info}->[1]);
|
||||
}
|
||||
}
|
||||
|
||||
# hanlde the output which has the node irrelevant message (e.g. the output for updatenode command)
|
||||
# handle the input like
|
||||
@@ -2148,6 +2178,39 @@ sub actionhdl {
|
||||
return $responses;
|
||||
}
|
||||
|
||||
sub localreshdl {
|
||||
my $params = shift;
|
||||
my @args;
|
||||
my @urilayers = @{$params->{'layers'}};
|
||||
|
||||
# set the command name
|
||||
$request->{command} = $params->{'cmd'};
|
||||
|
||||
if (isGET() && scalar(@urilayers) > 1 && $urilayers[-1] eq "detail") {
|
||||
push @args, "show";
|
||||
} elsif (isGET() && scalar(@urilayers) > 1 && $urilayers[-1] eq "file") {
|
||||
push @args, "download";
|
||||
} elsif (isGET()) {
|
||||
push @args, "list";
|
||||
} elsif (isPost()) {
|
||||
push @args, "create";
|
||||
} elsif (isPut()) {
|
||||
push @args, "update";
|
||||
} elsif (isDelete()) {
|
||||
push @args, "delete";
|
||||
}
|
||||
shift @urilayers;
|
||||
foreach my $item (@urilayers) {
|
||||
push @args, $item if $item ne 'detail' && $item ne 'file';
|
||||
}
|
||||
push @{$request->{arg}}, @args;
|
||||
# localrest is single plugin handler, use sequntial to avoid of multi-level processes
|
||||
$request->{'sequential'}->[0] = 1;
|
||||
my $req = genRequest();
|
||||
my $responses = sendRequest($req);
|
||||
return $responses;
|
||||
}
|
||||
|
||||
# The operation callback subroutine for node irrelevant commands like makedns -n and makedhcp -n
|
||||
# assembe the xcat request, send it to xcatd and get response
|
||||
sub nonobjhdl {
|
||||
@@ -2965,11 +3028,22 @@ sub sendResponseMsg {
|
||||
elsif ('xml' eq $format) {
|
||||
$tempFormat = 'text/xml';
|
||||
}
|
||||
elsif ('stream' eq $format) {
|
||||
$tempFormat = 'application/octet-stream';
|
||||
}
|
||||
else {
|
||||
$tempFormat = 'text/html';
|
||||
}
|
||||
print $q->header(-status => $code, -type => $tempFormat);
|
||||
if ($pageContent) { $pageContent .= "\n"; } # if there is any content, append a newline
|
||||
|
||||
if ($header_info{attachment}) {
|
||||
print $q->header(-status => $code,
|
||||
-type => $tempFormat,
|
||||
-attachment => $header_info{attachment},
|
||||
-Content_length => length($pageContent));
|
||||
} else {
|
||||
print $q->header(-status => $code, -type => $tempFormat);
|
||||
if ($pageContent) { $pageContent .= "\n"; } # if there is any content, append a newline
|
||||
}
|
||||
print $pageContent;
|
||||
exit(0);
|
||||
}
|
||||
|
Reference in New Issue
Block a user