2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-07-27 23:01:12 +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:
chenglch
2016-07-14 04:47:05 -04:00
parent 277dd2738b
commit 8187e3efa1
3 changed files with 328 additions and 9 deletions

View 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;

View File

@@ -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);

View File

@@ -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);
}