GRID::Machine::REMOTE - The server that runs on the other side of the SSH link
As with most servers, the server side of the GRID::Machine object consists of an infinite loop waiting for requests:
GRID::Machine
while( 1 ) { my ( $operation, @args ) = $server->read_operation(); if ($server->can($operation)) { $server->$operation(@args); next; } $server->send_error( "Unknown operation $operation\nARGS: @args\n" ); }
The protocol simply consists of the name of the method to execute and the arguments for such method. The programmer - using inheritance - can extend the protocol with new methods (see the section "EXTENDING THE PROTOCOL"). The following operations are currently supported:
GRID::Machine::EVAL
Used by the local method eval
eval
GRID::Machine::STORE
Used by the local methods compile and sub to install code on the remote side.
compile
sub
GRID::Machine::EXISTS
Used by the local method exists
exists
GRID::Machine::CALL
Used by the local method call
call
GRID::Machine::MODPUT
Used by the modput method. A list of pairs (Module::Name, code for Module::Name) is sent to the remote machine. For each pair, the remote side writes to disk a file Module/Name.pm with the contents of the string code for Module::Name. The file is stored in the directory referenced by the prefix attribute of the GRID::Machine object.
modput
Module::Name, code for Module::Name
Module/Name.pm
code for Module::Name
prefix
GRID::Machine::OPEN
Used by the open method. As arguments receives a string defining the way the file will be accessed.
open
GRID::Machine::QUIT
Usually is automatically called when the GRID::Machine object goes out of scope
GRID::Machine::GPRINT
Most requests go from the local machine to the remote Perl server. However, this and the next go in the other direction. This request is generated in the remote machine and served by the local machine. It is used when inmediate printing is required (see section "Functions gprint and gprintf")
GRID::Machine::GPRINTF
This request is generated in the remote machine and served by the local machine. It is used when inmediate printing is required (see section "Functions gprint and gprintf")
GRID::Machine::CALLBACK
Used to implement callbacks
SERVER
The SERVER function is available on the remote machine. Returns the object representing the remote side of the GRID::Machine object. This way code on the remote side can gain access to the GRID::Machine object. See an example:
my $m = GRID::Machine->new( host => 'beowulf'); $m->sub(installed => q { return keys %{SERVER->stored_procedures}; }); my @functions = $m->installed()->Results; local $" = "\n"; print "@functions\n";
The stored_procedures method returns a reference to the hash containing the subroutines installed via the sub and compile methods. The keys are the names of the subroutines, the values are the CODE references implementing them. When executed the former program produces the list of installed subroutines:
stored_procedures
CODE
$ accessobject.pl tar system installed getcwd etc.
read_operation
Syntax:
my ( $operation, @args ) = $server->read_operation( );
Reads from the link. Returns the type of operation/tag and the results of the operation.
send_error
$server->send_error( "Error message" );
Inside code to be executed on the remote machine we can use the function send_error to send error messages to the client
send_result
$server->send_result( stdout => $stdout, stderr => $stderr, errmsg => $errmsg, results => [ @results ], );
Inside code to be executed on the remote machine we can use the function send_result to send results to the client
Let us see a simple example. We will extend the protocol with a new tag MYTAG. We have to write a module that will be used in the remote side of the link:
MYTAG
$ cat -n MyRemote.pm 1 package GRID::Machine; 2 use strict; 3 4 sub MYTAG { 5 my ($server, $name) = @_; 6 7 $server->send_operation("RETURNED", "Hello $name!\n") if defined($name); 8 $server->send_operation("DIED", "Error: Provide a name to greet!\n"); 9 } 10 11 1;
This component will be loaded on the remote machine via the ssh link. The name of the handling method MYTAG must be the same than the name of the tag (operation type) used to send the request. Here is a client program using the new tag:
$ cat -n extendprotocol.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use GRID::Machine; 4 5 my $name = shift; 6 my $host = 'user@remote.machine'; 7 8 my $machine = GRID::Machine->new(host => $host, remotelibs => [ qw(MyRemote) ]); 9 10 $machine->send_operation( "MYTAG", $name); 11 my ($type, $result) = $machine->read_operation(); 12 13 die $result unless $type eq 'RETURNED'; 14 print $result;
When the program is executed we get the following output:
$ extendprotocol.pl Larry Hello Larry! $ extendprotocol.pl Error: Provide a name to greet!
gprint
gprintf
When running a RPC the output generated during the execution of the remote subroutine isn't available until the return of the RPC. Use gprint and gprintf if what you want is inmediate output (for debugging purposes, for instance). They work as print and printf respectively.
print
printf
See an example:
$ cat -n gprint.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use GRID::Machine; 4 5 my $host = $ENV{GRID_REMOTE_MACHINE}; 6 7 my $machine = GRID::Machine->new(host => $host, uses => [ 'Sys::Hostname' ]); 8 9 my $r = $machine->sub( 10 rmap => q{ 11 my $f = shift; # function to apply 12 die "Code reference expected\n" unless UNIVERSAL::isa($f, 'CODE'); 13 14 15 print "Inside rmap!\n"; # last message 16 my @result; 17 for (@_) { 18 die "Array reference expected\n" unless UNIVERSAL::isa($_, 'ARRAY'); 19 20 gprint hostname(),": Processing @$_\n"; 21 22 23 push @result, [ map { $f->($_) } @$_ ]; 24 } 25 26 gprintf "%12s:\n",hostname(); 27 for (@result) { 28 my $format = "%5d"x(@$_)."\n"; 29 gprintf $format, @$_ 30 } 31 return @result; 32 }, 33 ); 34 die $r->errmsg unless $r->ok; 35 36 my $cube = sub { $_[0]**3 }; 37 $r = $machine->rmap($cube, [1..3], [4..6], [7..9]); 38 print $r;
When executed the program produces the following output:
$ gprint.pl orion: Processing 1 2 3 orion: Processing 4 5 6 orion: Processing 7 8 9 orion: 1 8 27 64 125 216 343 512 729 Inside rmap!
Observe how the message 'Inside rmap!' generated at line 15 using print is the last (actually is sent to STDOUT in line 38). The messages generated using gprint and gprintf (lines 20, 26 and 29) were inmediately sent to STDOUT.
'Inside rmap!'
STDOUT
To run the remote side under the control of the perl debugger use the debug option of new. The associated value must be a port number higher than 1024:
debug
new
my $machine = GRID::Machine->new( host => $host, debug => $port, includes => [ qw{SomeFunc} ], );
Before running the example open a SSH session to the remote machine in a different terminal and execute netcat to listen (option -l) in the chosen port:
netcat
-l
pp2@nereida:~/LGRID_Machine$ ssh beowulf 'netcat -v -l -p 12345' listening on [any] 12345 ...
Now run the program in the first terminal:
pp2@nereida:~/LGRID_Machine/examples$ debug1.pl beowulf:12345 Debugging with 'ssh beowulf PERLDB_OPTS="RemotePort=beowulf:12345" perl -d' Remember to run 'netcat -v -l -p 12345' in beowulf
The program looks blocked. If you go to the other terminal you will find the familiar perl debugger prompt:
casiano@beowulf:~$ netcat -v -l -p 12345 listening on [any] 12345 ... connect to [193.145.102.240] from beowulf.pcg.ull.es [193.145.102.240] 38979 Loading DB routines from perl5db.pl version 1.28 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. GRID::Machine::MakeAccessors::(/home/pp2/LGRID_Machine/lib/GRID/Machine/MakeAccessors.pm:33): 33: 1; auto(-1) DB<1> c GRID::Machine::main GRID::Machine::main(/home/pp2/LGRID_Machine/lib/GRID/Machine/REMOTE.pm:490): 490: my $server = shift; DB<2>
From now on you can execute almost any debugger command. Unfortunately you are now inside GRID::Machine code and - until you gain some familiarity with GRID::Machine code - it is a bit difficult to find where your code is and where to put your breakpoints. Future work: write a proper debugger front end.
GRID::Machine::IOHandle
GRID::Machine::Group
GRID::Machine::Process
GRID::Machine::perlparintro
Net::OpenSSH
IPC::PerlSSH
IPC::PerlSSH::Async
Man pages of ssh, ssh-key-gen, ssh_config, scp, ssh-agent, ssh-add, sshd
ssh
ssh-key-gen
ssh_config
scp
ssh-agent
ssh-add
sshd
http://www.openssh.com
Casiano Rodriguez Leon <casiano@ull.es>
This work has been supported by CEE (FEDER) and the Spanish Ministry of Educacion y Ciencia through Plan Nacional I+D+I number TIN2005-08818-C04-04 (ULL::OPLINK project http://www.oplink.ull.es/). Support from Gobierno de Canarias was through GC02210601 (Grupos Consolidados). The University of La Laguna has also supported my work in many ways and for many years.
I wish to thank Paul Evans for his IPC::PerlSSH module: it was the source of inspiration for this module. To Alex White, Dmitri Kargapolov, Eric Busto and Erik Welch for their contributions. To the Perl Monks, and the Perl Community for generously sharing their knowledge. Finally, thanks to Juana, Coro and my students at La Laguna.
Copyright (c) 2007 Casiano Rodriguez-Leon (casiano@ull.es). All rights reserved.
These modules are free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
To install GRID::Machine, copy and paste the appropriate command in to your terminal.
cpanm
cpanm GRID::Machine
CPAN shell
perl -MCPAN -e shell install GRID::Machine
For more information on module installation, please visit the detailed CPAN module installation guide.