mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-29 09:13:08 +00:00
add docker basic plugin.
This commit is contained in:
parent
d776dd582f
commit
ce1015a142
414
xCAT-server/lib/xcat/plugins/docker.pm
Executable file
414
xCAT-server/lib/xcat/plugins/docker.pm
Executable file
@ -0,0 +1,414 @@
|
||||
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head1
|
||||
xCAT plugin package to handle docker
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
package xCAT_plugin::docker;
|
||||
|
||||
BEGIN
|
||||
{
|
||||
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
||||
}
|
||||
use lib "$::XCATROOT/lib/perl";
|
||||
|
||||
use strict;
|
||||
use POSIX qw(WNOHANG nice);
|
||||
use POSIX qw(WNOHANG setsid :errno_h);
|
||||
use IO::Select;
|
||||
require IO::Socket::SSL; IO::Socket::SSL->import('inet4');
|
||||
use Time::HiRes qw(gettimeofday sleep);
|
||||
use Fcntl qw/:DEFAULT :flock/;
|
||||
use File::Path;
|
||||
use File::Copy;
|
||||
use Getopt::Long;
|
||||
Getopt::Long::Configure("bundling");
|
||||
use HTTP::Headers;
|
||||
use HTTP::Request;
|
||||
use XML::LibXML;
|
||||
|
||||
use xCAT::Usage;
|
||||
use xCAT::Utils;
|
||||
|
||||
my %globalopt;
|
||||
my @filternodes;
|
||||
my $verbose;
|
||||
my $global_callback;
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 send_msg
|
||||
|
||||
Invokes the callback with the specified message
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub send_msg {
|
||||
|
||||
my $request = shift;
|
||||
my $ecode = shift;
|
||||
my $msg = shift;
|
||||
my %output;
|
||||
|
||||
#################################################
|
||||
# Called from child process - send to parent
|
||||
#################################################
|
||||
if ( exists( $request->{pipe} )) {
|
||||
my $out = $request->{pipe};
|
||||
|
||||
$output{errorcode} = $ecode;
|
||||
$output{data} = \@_;
|
||||
print $out freeze( [\%output] );
|
||||
print $out "\nENDOFFREEZE6sK4ci\n";
|
||||
}
|
||||
#################################################
|
||||
# Called from parent - invoke callback directly
|
||||
#################################################
|
||||
elsif ( exists( $request->{callback} )) {
|
||||
my $callback = $request->{callback};
|
||||
$output{errorcode} = $ecode;
|
||||
$output{data} = $msg;
|
||||
$callback->( \%output );
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 handled_commands
|
||||
|
||||
Return list of commands handled by this plugin
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub handled_commands {
|
||||
return( {docker=>"docker"} );
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 parse_args
|
||||
|
||||
Parse the command line options and operands
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub parse_args {
|
||||
|
||||
my $request = shift;
|
||||
my $args = $request->{arg};
|
||||
my $cmd = $request->{command};
|
||||
my %opt;
|
||||
|
||||
#############################################
|
||||
# Responds with usage statement
|
||||
#############################################
|
||||
local *usage = sub {
|
||||
my $usage_string = xCAT::Usage->getUsage($cmd);
|
||||
return( [$_[0], $usage_string] );
|
||||
};
|
||||
#############################################
|
||||
# No command-line arguments - use defaults
|
||||
#############################################
|
||||
if ( !defined( $args )) {
|
||||
return(0);
|
||||
}
|
||||
#############################################
|
||||
# Checks case in GetOptions, allows opts
|
||||
# to be grouped (e.g. -vx), and terminates
|
||||
# at the first unrecognized option.
|
||||
#############################################
|
||||
@ARGV = @$args;
|
||||
$Getopt::Long::ignorecase = 0;
|
||||
Getopt::Long::Configure( "bundling" );
|
||||
|
||||
#############################################
|
||||
# Process command-line flags
|
||||
#############################################
|
||||
if (!GetOptions( \%opt,
|
||||
qw(h|help V|Verbose v|version))) {
|
||||
return( usage() );
|
||||
}
|
||||
|
||||
#############################################
|
||||
# Option -V for verbose output
|
||||
#############################################
|
||||
if ( exists( $opt{V} )) {
|
||||
$globalopt{verbose} = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 preprocess_request
|
||||
|
||||
preprocess the command
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub preprocess_request {
|
||||
my $req = shift;
|
||||
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
|
||||
my $callback=shift;
|
||||
my $command = $req->{command}->[0];
|
||||
my $extrargs = $req->{arg};
|
||||
my @exargs=($req->{arg});
|
||||
if (ref($extrargs)) {
|
||||
@exargs=@$extrargs;
|
||||
}
|
||||
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
|
||||
if ($usage_string) {
|
||||
$callback->({data=>[$usage_string]});
|
||||
$req = {};
|
||||
return;
|
||||
}
|
||||
|
||||
my @result = ();
|
||||
my $mncopy = {%$req};
|
||||
push @result, $mncopy;
|
||||
return \@result;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 process_request
|
||||
|
||||
Process the command
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub process_request {
|
||||
my $req = shift;
|
||||
my $callback = shift;
|
||||
|
||||
###########################################
|
||||
# Build hash to pass around
|
||||
###########################################
|
||||
my %request;
|
||||
$request{arg} = $req->{arg};
|
||||
$request{callback} = $callback;
|
||||
$request{command} = $req->{command}->[0];
|
||||
|
||||
####################################
|
||||
# Process command-specific options
|
||||
####################################
|
||||
my $result = parse_args( \%request );
|
||||
|
||||
####################################
|
||||
# Return error
|
||||
####################################
|
||||
if ( ref($result) eq 'ARRAY' ) {
|
||||
send_msg( \%request, 1, @$result );
|
||||
return(1);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 genreq
|
||||
|
||||
Generate the REST API http request
|
||||
Input:
|
||||
$method: GET, PUT, POST, DELETE
|
||||
$api: the url of rest api
|
||||
$content: an xml section which including the data to perform the rest api
|
||||
$dochost
|
||||
$port
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub genreq {
|
||||
my $dochost = shift;
|
||||
my $method = shift;
|
||||
my $port = shift;
|
||||
my $api = shift;
|
||||
my $content = shift;
|
||||
|
||||
if (! defined($content)) { $content = ""; }
|
||||
my $header = HTTP::Headers->new('content-type' => 'application/xml',
|
||||
'Accept' => 'application/xml',
|
||||
#'Connection' => 'keep-alive',
|
||||
'Host' => $dochost->{name}.':'.$port);
|
||||
$header->authorization_basic($dochost->{user}.'@internal', $dochost->{pw});
|
||||
|
||||
my $ctlen = length($content);
|
||||
$header->push_header('Content-Length' => $ctlen);
|
||||
|
||||
my $url = "https://".$dochost->{name}.$port.$api;
|
||||
my $request = HTTP::Request->new($method, $url, $header, $content);
|
||||
$request->protocol('HTTP/1.1');
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
=head3 get_highest_version
|
||||
|
||||
Make connection to docker daemon
|
||||
Send REST api request to docker daemon
|
||||
Receive the response from docker daemon
|
||||
Handle the error cases
|
||||
|
||||
Input: $dochost
|
||||
$port
|
||||
$request: the REST API http request
|
||||
$ssl_ca_file: path to ssl ca file
|
||||
|
||||
return: 1-ssl connection error;
|
||||
2-http response error;
|
||||
3-return a http error message;
|
||||
5-operation failed
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------
|
||||
sub send_req {
|
||||
my $dochost = shift;
|
||||
my $request = shift;
|
||||
my $ssl_ca_file = shift;
|
||||
my $port = shift;
|
||||
|
||||
my $doc_hostname = $dochost->{name};
|
||||
|
||||
my $rc = 0;
|
||||
my $response;
|
||||
my $connect;
|
||||
my $socket = IO::Socket::INET->new( PeerAddr => $doc_hostname,
|
||||
PeerPort => $port,
|
||||
Timeout => 15);
|
||||
if ($socket) {
|
||||
$connect = IO::Socket::SSL->start_SSL($socket, SSL_ca_file => $ssl_ca_file, Timeout => 0);
|
||||
if ($connect) {
|
||||
my $flags=fcntl($connect,F_GETFL,0);
|
||||
$flags |= O_NONBLOCK;
|
||||
fcntl($connect,F_SETFL,$flags);
|
||||
} else {
|
||||
$rc = 1;
|
||||
$response = "Could not make ssl connection to $doc_hostname:$port.";
|
||||
}
|
||||
} else {
|
||||
$rc = 1;
|
||||
$response = "Could not create socket to $doc_hostname:$port.";
|
||||
}
|
||||
|
||||
if ($rc) {
|
||||
return ($rc, $response);
|
||||
}
|
||||
|
||||
my $IOsel = new IO::Select;
|
||||
$IOsel->add($connect);
|
||||
|
||||
if ($verbose) {
|
||||
my $rsp;
|
||||
push @{$rsp->{data}}, "\n===================================================\n$request----------------";
|
||||
xCAT::MsgUtils->message("I", $rsp, $global_callback);
|
||||
}
|
||||
print $connect $request;
|
||||
$response = "";
|
||||
my $retry;
|
||||
my $ischunked;
|
||||
my $firstnum;
|
||||
while ($retry++ < 10) {
|
||||
unless ($IOsel->can_read(2)) {
|
||||
next;
|
||||
}
|
||||
my $readbytes;
|
||||
my $res = "";
|
||||
do { $readbytes=sysread($connect,$res,65535,length($res)); } while ($readbytes);
|
||||
if ($res) {
|
||||
my @part = split (/\r\n/, $res);
|
||||
for my $data (@part) {
|
||||
# for chunk formated data, check the last chunk to finish
|
||||
if ($data =~ /Transfer-Encoding: (\S+)/) {
|
||||
if ($1 eq "chunked") {
|
||||
$ischunked = 1;
|
||||
}
|
||||
}
|
||||
if ($ischunked && $data =~ /^([\dabcdefABCDEF]+)$/) {
|
||||
if ($1 eq 0) {
|
||||
# last chunk
|
||||
goto FINISH;
|
||||
}else {
|
||||
# continue to get the rest chunks
|
||||
$retry = 0;
|
||||
next;
|
||||
}
|
||||
} else {
|
||||
# put all data together
|
||||
$response .= $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
unless ($ischunked) {
|
||||
# for non chunk data, just read once
|
||||
if ($response) {
|
||||
last;
|
||||
} else {
|
||||
if (not defined $readbytes and $! == EAGAIN) { next; }
|
||||
$rc = 2;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
FINISH:
|
||||
if ($retry >= 10 ) {$rc = 3;}
|
||||
|
||||
if ($verbose) {
|
||||
my $rsp;
|
||||
push @{$rsp->{data}}, "$response===================================================\n";
|
||||
xCAT::MsgUtils->message("I", $rsp, $global_callback);
|
||||
}
|
||||
|
||||
$IOsel->remove($connect);
|
||||
close($connect);
|
||||
|
||||
if ($response) {
|
||||
if (grep (/<html>/, $response)) { # get a error message in the html
|
||||
$rc = 3;
|
||||
} elsif (grep (/<\?xml/, $response)) {
|
||||
$response =~ s/.*?</</ms;
|
||||
my $parser = XML::LibXML->new();
|
||||
my $doc = $parser->parse_string($response);
|
||||
if ($doc ) {
|
||||
my $attr;
|
||||
if ($attr = getAttr($doc, "/fault/detail")) {
|
||||
$response = $attr;
|
||||
$rc = 5;
|
||||
} elsif ($attr = getAttr($doc, "/action/fault/detail")) {
|
||||
if ($attr eq "[]") {
|
||||
if ($attr = getAttr($doc, "/action/fault/reason")) {
|
||||
$response = $attr;
|
||||
} else {
|
||||
$response = "failed";
|
||||
}
|
||||
} else {
|
||||
$response = $attr;
|
||||
}
|
||||
$rc = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($rc, $response);
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user