Commit 640d2c68 authored by Leigh Stoller's avatar Leigh Stoller

Optimized version of the RPC server, using mod_fcgid to maintain long

running servers to avoid the big perl startup costs we have with starting a
brand new script each time a request comes in. At the moment, only the
sa/cm/am servers are going to run combined inside the new server, the other
APIs will use the old method. This new approach reduces minimal RPC time
from 1 second to 0.05 seconds, which makes it usable from the Portal web
interface to grab cluster data on the fly.
parent 4c3fe3ba
#!/usr/bin/perl -wT
#
# Copyright (c) 2008-2016 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
package GeniCluster;
#
# Portal stuff.
#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration!
use emdb;
use libtestbed;
use libEmulab;
use GeniResponse;
use GeniHRN;
use English;
use Data::Dumper;
use Date::Parse;
use POSIX qw(strftime);
use Time::Local;
use Project;
# Configure variables
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $MAINSITE = @TBMAINSITE@;
my $OURDOMAIN = "@OURDOMAIN@";
my $API_VERSION = 1.0;
#
# Check permission. At the moment, only the Mothership can issue requests
# and only the Cloudlab clusters will accept them.
#
sub CheckPermission()
{
my $myurn = $ENV{"MYURN"};
my $hrn = GeniHRN->new($ENV{"GENIURN"});
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not parse GENIURN")
if (!defined($hrn));
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Only the Mothership can access this interface")
if (! ($hrn->authority() eq "emulab.net" &&
$hrn->IsAuthority() && $hrn->IsRoot()));
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Only Cloudlab clusters permit this interface")
if (! ($OURDOMAIN eq "emulab.net" ||
$OURDOMAIN eq "apt.emulab.net" ||
$OURDOMAIN eq "utah.cloudlab.us" ||
$OURDOMAIN eq "wisc.cloudlab.us" ||
$OURDOMAIN eq "clemson.cloudlab.us"));
return 0;
}
#
# Tell the client what API revision we support. The correspondence
# between revision numbers and API features is to be specified elsewhere.
# No credentials are required.
#
sub GetVersion()
{
my $hasperm = CheckPermission();
return $hasperm
if (GeniResponse::IsError($hasperm));
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $API_VERSION);
}
#
# Return the InUse info, which includes the pre-reserve info.
#
sub InUse()
{
my $hasperm = CheckPermission();
return $hasperm
if (GeniResponse::IsError($hasperm));
my @blob = ();
my $query_result =
DBQueryWarn("select n.node_id,r.pid,r.eid,n.reserved_pid ".
" from nodes as n ".
"left join reserved as r on r.node_id=n.node_id ".
"left join node_types as t on t.type=n.type ".
"where n.role='testnode' and t.class='pc' ".
"order by n.node_id");
while (my ($node_id,$pid,$eid,$reserved_pid) =
$query_result->fetchrow_array()) {
push(@blob, {"node_id" => $node_id,
"pid" => $pid || "",
"eid" => $eid || "",
"reserved_pid" => $reserved_pid || ""});
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, \@blob);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
This diff is collapsed.
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
......@@ -37,7 +37,8 @@ include $(OBJDIR)/Makeconf
# These scripts installed setuid, with sudo.
SETUID_BIN_SCRIPTS =
SETUID_SBIN_SCRIPTS =
SETUID_SUEXEC_SCRIPTS = protogeni-wrapper.pl protogeni-console.pl
SETUID_SUEXEC_SCRIPTS = protogeni-wrapper.pl protogeni-console.pl \
cluster-wrapper.pl
#
# Force dependencies on the scripts so that they will be rerun through
......@@ -47,7 +48,7 @@ all: Genixmlrpc.pm GeniResponse.pm \
protogeni-ch.pm protogeni-sa.pm protogeni-cm.pm protogeni-emulab.pm \
protogeni-ses.pm geni-am.pm geni-ma.pm geni-sa.pm \
protogeni-wrapper.pl protogeni-console.pl protogeni-portal.pm \
protogeni-ims.pm
protogeni-ims.pm cluster-wrapper.pl ClusterWrapper.pm ProtoGeniDefs.pm
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -60,25 +61,31 @@ install-libs: $(INSTALL_LIBDIR)/Genixmlrpc.pm \
$(INSTALL_LIBDIR)/protogeni-ims.pm \
$(INSTALL_LIBDIR)/protogeni-ses.pm \
$(INSTALL_LIBDIR)/protogeni-emulab.pm \
$(INSTALL_LIBDIR)/ClusterWrapper.pm \
$(INSTALL_LIBDIR)/ProtoGeniDefs.pm \
$(INSTALL_LIBDIR)/geni-am.pm \
$(INSTALL_LIBDIR)/geni-ma.pm \
$(INSTALL_LIBDIR)/geni-sa.pm \
$(INSTALL_DIR)/opsdir/lib/Genixmlrpc.pm \
$(INSTALL_DIR)/opsdir/lib/GeniResponse.pm
install-scripts: $(INSTALL_LIBEXECDIR)/protogeni-wrapper.pl \
$(INSTALL_LIBEXECDIR)/protogeni-console.pl
install-scripts: install-console install-cluster \
$(INSTALL_LIBEXECDIR)/protogeni-wrapper.pl
-mkdir -p $(INSTALL_DIR)/protogeni/xmlrpc
-rm -f $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-wrapper.pl
ln $< $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-wrapper.pl
-rm -f $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-console.pl
ln $< $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-console.pl
ln $(INSTALL_LIBEXECDIR)/protogeni-wrapper.pl \
$(INSTALL_DIR)/protogeni/xmlrpc/protogeni-wrapper.pl
install-console: $(INSTALL_LIBEXECDIR)/protogeni-console.pl
-mkdir -p $(INSTALL_DIR)/protogeni/xmlrpc
-rm -f $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-console.pl
ln $< $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-console.pl
install-cluster: $(INSTALL_LIBEXECDIR)/cluster-wrapper.pl
-mkdir -p $(INSTALL_DIR)/protogeni/xmlrpc
-rm -f $(INSTALL_DIR)/protogeni/xmlrpc/cluster-wrapper.pl
ln $< $(INSTALL_DIR)/protogeni/xmlrpc/cluster-wrapper.pl
install: all install-libs install-scripts
control-install:
......
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2010, 2016 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
package ProtoGeniDefs;
use strict;
use English;
use GeniResponse;
use GeniCertificate;
#
# Each of the packages added themself to this hash.
#
my %MODULES = ();
sub AddModule($$)
{
my ($name, $module) = @_;
if (exists($module->{'PEMFILE'})) {
my $certificate = GeniCertificate->LoadFromFile($module->{'PEMFILE'});
if (!defined($certificate)) {
print STDERR "Could not load certificate: " .
$module->{'PEMFILE'} . "\n";
}
else {
$module->{'CERTIFICATE'} = $certificate;
}
}
$MODULES{$name} = $module;
}
sub Module($)
{
my ($name) = @_;
return undef
if (!exists($MODULES{$name}));
return $MODULES{$name};
}
#
# Stub
#
sub GetVersion()
{
if (1) {
my $mypid = main::WrapperFork();
if ($mypid) {
return GeniResponse->Create(GENIRESPONSE_SUCCESS, 1.0);
}
sleep(10);
return 0;
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, 1.0);
}
AddModule("root",
{
"PEMFILE" => "@prefix@/etc/emulab.pem",
"DEFVERSION" => "01",
"METHODS" => {"01" => {
"GetVersion" => \&GetVersion,
}},
});
package ProtoGeniDefs::ProtogeniSA;
use lib '@prefix@/lib';
use GeniSA;
ProtoGeniDefs::AddModule("sa",
{
"PEMFILE" => "@prefix@/etc/genisa.pem",
"DBNAME" => "geni",
"DEFVERSION" => "1.01",
"METHODS" => {"1.01" => {
"GetVersion" => \&GeniSA::GetVersion,
"GetCredential" => \&GeniSA::GetCredential,
"Resolve" => \&GeniSA::Resolve,
"Register" => \&GeniSA::Register,
"Remove" => \&GeniSA::Remove,
"GetKeys" => \&GeniSA::GetKeys,
"BindToSlice" => \&GeniSA::BindToSlice,
"Shutdown" => \&GeniSA::Shutdown,
"RenewSlice" => \&GeniSA::RenewSlice,
"RegisterSliver" => \&GeniSA::RegisterSliver,
"UnRegisterSliver" => \&GeniSA::UnRegisterSliver,
}},
});
package ProtoGeniDefs::ProtogeniCM;
use vars qw($V2_METHODS);
use lib '@prefix@/lib';
use GeniCM;
use GeniCMV2;
$V2_METHODS = {
"GetVersion" => \&GeniCMV2::GetVersion,
"Resolve" => \&GeniCMV2::Resolve,
"DiscoverResources" => \&GeniCMV2::DiscoverResources,
"CreateSliver" => \&GeniCMV2::CreateSliver,
"UpdateSliver" => \&GeniCMV2::UpdateSliver,
"DeleteSliver" => \&GeniCMV2::DeleteSliver,
"GetSliver" => \&GeniCMV2::GetSliver,
"Shutdown" => \&GeniCMV2::Shutdown,
"SliverStatus" => \&GeniCMV2::SliverStatus,
"RenewSlice" => \&GeniCMV2::RenewSlice,
"GetTicket" => \&GeniCMV2::GetTicket,
"ReleaseTicket" => \&GeniCMV2::ReleaseTicket,
"UpdateTicket" => \&GeniCMV2::UpdateTicket,
"RedeemTicket" => \&GeniCMV2::RedeemTicket,
"DeleteSlice" => \&GeniCMV2::DeleteSlice,
"StartSliver" => \&GeniCMV2::StartSliver,
"StopSliver" => \&GeniCMV2::StopSliver,
"RestartSliver" => \&GeniCMV2::RestartSliver,
"ReloadSliver" => \&GeniCMV2::ReloadSliver,
"BindToSlice" => \&GeniCMV2::BindToSlice,
"ListUsage" => \&GeniCMV2::ListUsage,
"ListHistory" => \&GeniCMV2::ListHistory,
"ReserveVlanTags" => \&GeniCMV2::ReserveVlanTags,
"InjectEvent" => \&GeniCMV2::InjectEvent,
"CreateImage" => \&GeniCMV2::CreateImage,
"DeleteImage" => \&GeniCMV2::DeleteImage,
"ImageInfo" => \&GeniCMV2::ImageInfo,
"ListImages" => \&GeniCMV2::ListImages,
"ShareLan" => \&GeniCMV2::ShareLan,
"UnShareLan" => \&GeniCMV2::UnShareLan,
"ListActiveSlivers" => \&GeniCMV2::ListActiveSlivers,
"ConsoleURL" => \&GeniCMV2::ConsoleURL,
"ConsoleInfo" => \&GeniCMV2::ConsoleInfo,
"CreateDataset" => \&GeniCMV2::CreateDataset,
"DeleteDataset" => \&GeniCMV2::DeleteDataset,
"DescribeDataset" => \&GeniCMV2::DescribeDataset,
"ModifyDataset" => \&GeniCMV2::ModifyDataset,
"Lockdown" => \&GeniCMV2::Lockdown,
"TriggerImageUpdate"=> \&GeniCMV2::TriggerImageUpdate,
"AddNodes" => \&GeniCMV2::AddNodes,
"DeleteNodes" => \&GeniCMV2::DeleteNodes,
"Panic" => \&GeniCMV2::Panic,
"RunLinktest" => \&GeniCMV2::RunLinktest,
};
ProtoGeniDefs::AddModule("cm",
{
"PEMFILE" => "@prefix@/etc/genicm.pem",
"DBNAME" => "geni-cm",
"DEFVERSION" => "2.0",
"METHODS" => {"2.0" => $V2_METHODS},
});
package ProtoGeniDefs::ProtogeniAM;
use vars qw($V2_METHODS $V3_METHODS);
use lib '@prefix@/lib';
use GeniAM;
$V2_METHODS = {
"GetVersion" => \&GeniAM::GetVersion,
"ListResources" => \&GeniAM::ListResources,
"CreateSliver" => \&GeniAM::CreateSliver,
"DeleteSliver" => \&GeniAM::DeleteSliver,
"SliverStatus" => \&GeniAM::SliverStatus,
"RenewSliver" => \&GeniAM::RenewSliver,
"Shutdown" => \&GeniAM::Shutdown,
"CreateImage" => \&GeniAM::CreateImage,
"DeleteImage" => \&GeniAM::DeleteImage,
"ListImages" => \&GeniAM::ListImages,
};
$V3_METHODS = {
"GetVersion" => \&GeniAM::GetVersion,
"ListResources" => \&GeniAM::ListResources,
"Describe" => \&GeniAM::Describe,
"Allocate" => \&GeniAM::Allocate,
"Renew" => \&GeniAM::Renew,
"Provision" => \&GeniAM::Provision,
"Status" => \&GeniAM::Status,
"PerformOperationalAction" => \&GeniAM::PerformOperationalAction,
"Delete" => \&GeniAM::Delete,
"Shutdown" => \&GeniAM::Shutdown,
"Update" => \&GeniAM::Update,
"Cancel" => \&GeniAM::Cancel,
"CreateImage" => \&GeniAM::CreateImage,
"DeleteImage" => \&GeniAM::DeleteImage,
"ListImages" => \&GeniAM::ListImages,
};
ProtoGeniDefs::AddModule("am",
{
"PEMFILE" => "@prefix@/etc/genicm.pem",
"DBNAME" => "geni-cm",
"DEFVERSION" => "2.0",
"INITMODULE" => \&GeniAM::InitModule,
"METHODS" => {"1.0" => $V2_METHODS,
"2.0" => $V2_METHODS,
"3.0" => $V3_METHODS},
"CONVERTER" => {"1.0" => \&GeniAM::ConvertResponseV1,
"2.0" => \&GeniAM::ConvertResponseV2,
"3.0" => \&GeniAM::ConvertResponseV2},
});
package ProtoGeniDefs::ProtogeniCluster;
use lib '@prefix@/lib';
use GeniCluster;
ProtoGeniDefs::AddModule("cluster",
{
"PEMFILE" => "@prefix@/etc/genicm.pem",
"DEFVERSION" => "1.0",
"METHODS" => {"1.0" => {
"GetVersion" => \&GeniCluster::GetVersion,
"InUse" => \&GeniCluster::InUse,
}},
});
1;
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2016 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
#
# Simple CGI interface to the GENI xmlrpc interface. This script is invoked
# from the web server. The certificate information is in the environment
# set up by apache.
#
use strict;
use English;
use Data::Dumper;
use FCGI;
#
# Turn off line buffering on output
#
$| = 1;
# Normal taint check requirement.
BEGIN {
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
}
# Testbed libraries.
use lib '@prefix@/lib';
use ClusterWrapper;
#
# Sanity check.
#
if ($EUID != 0) {
print STDERR "Server configuration error; we are not setuid!"
}
ClusterWrapper::Initialize();
#
# Normally one would use CGI::Fast->new() to get the next request, but
# that is a layer on CGI, which does not grok purely XML postdata, it
# wants to parse it, and of course that fails. Frontier::Responder() has
# the same problem. So we do what it does; bypass CGI and just read the
# blob of XML from STDIN.
#
my $FCGI_Handle = FCGI::Request();
while ($FCGI_Handle->Accept() >= 0) {
my $request = undef;
read(STDIN, $request, $ENV{CONTENT_LENGTH});
#
# We have to do this here again, since mod_fcgid sets the
# environment back to its original value, between each call.
#
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# We need to "detach" cause mod_fcgid has a bad "tie" implementation,
# and so most calls to open fail. detaching is fine, we have the request,
# and nothing goes back to apache until ClusterWrapper returns.
# When it does, we can reattach and print the output to apache.
#
$FCGI_Handle->Detach();
my $response = ClusterWrapper->Start($request);
$FCGI_Handle->Attach();
if ($response) {
print "Content-Type: text/xml \n\n" . $response;
}
}
#
# These used to be in protogeni-wrapper.pl and accessed as upcalls
# from the libraries with main:: but now they are down one level.
# Need to change all the code at some point.
#
sub WrapperFork() {
return ClusterWrapper::WrapperFork();
}
sub AddLogfileMetaData($$) {
return ClusterWrapper::AddLogfileMetaData($_[0],$_[1]);
}
sub AddLogfileMetaDataFromSlice($) {
return ClusterWrapper::AddLogfileMetaDataFromSlice($_[0]);
}
sub AddLogfileMetaDataFromSpeaksFor($) {
return ClusterWrapper::AddLogfileMetaDataFromSpeaksFor($_[0]);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment