File : dispatch.sp
#!/usr/local/bin/spar
pragma annotate( summary, "dispatch server-type remote-command" )
@( param, "server-type - the type of server to run the command on" )
@( param, "remote-command - the command to run" )
@( description, "Run a command across a pre-defined set of computers " )
@( description, "using Secure Shell (SSH)." )
@( return, "On success, returns status code 0." )
@( errors, "192 - cannot log into a server" )
@( errors, "193 - the ssh command failed" )
@( author, "Ken. O. Burtch" );
pragma license( unrestricted );
procedure dispatch is
type import_string is new string;
-- my login account
LOGNAME : constant import_string := "unknown";
pragma import( shell, LOGNAME );
-- node_names is an enumerated list of servers
-- node_types are types of servers
-- node_type_array is a list of types for the node_names
-- node_name_array is a list of hostnames for the node_names
type node_names is ( host1, host2 );
type node_types is (application, database, proxy, cache, share, offline );
type node_type_array is array( host1..host2 ) of node_types;
type node_name_array is array( host1..host2 ) of string;
node_name : node_name_array;
node_type : node_type_array;
kindStr : string;
kind : node_types;
procedure usage is
-- show help
begin
put( "usage: " ) @ ( source_info.file );
put_line( " server-type remote-command" );
new_line;
put_line( "Run a command across a pre-defined set of computers" );
put_line( "using Secure Shell (SSH)." );
new_line;
end usage;
begin
-- Make a list of your hosts here
node_name( host1 ) := "localhost"; node_type( host1 ) := application;
node_name( host2 ) := "localhost2"; node_type( host2 ) := database;
-- Parameter handling
-- There should be two parameters. Any other number of parameters, or
-- -h or --help, should show script usage
command_line.set_exit_status( 0 ); -- status OK
if $# /= 2 then -- not 2 params?
usage; -- show usage
return; -- and quit
elsif $1 = "-h" or $1 = "--help" then -- help request?
usage; -- show usage
return; -- and quit
end if;
-- Convert kind to an item of the enumerated type
kindStr := $1;
case kindStr is
when "application" => kind := application;
when "database" => kind := database;
when "proxy" => kind := proxy;
when "cache" => kind := cache;
when "share" => kind := share;
when others =>
put( standard_error, source_info.file )
@( standard_error, ": Bad type of server: '" & kindStr & "'" );
return;
end case;
-- Verify machine access: this does not guarantee the machine will
-- be there but reduces the risk of a machine being down while
-- running the command.
for n in arrays.first( node_name )..arrays.last( node_name ) loop
if node_type( n ) = kind then
ssh( "-oPreferredAuthentications=publickey",
LOGNAME & "@" & node_name( n ),
"exit" );
if $? /= 0 then
put( standard_error, source_info.file )
@( standard_error, ": cannot log into '" & node_name(n) & "'" );
command_line.set_exit_status( 192 );
return;
end if;
end if;
end loop;
-- Run the command sequentially across all the computers of that kind
-- This can be improved by running ssh in the background and wait-ing
-- so that the command operate in parallel, but you lose the error
-- checking of the command results.
for n in arrays.first( node_name )..arrays.last( node_name ) loop
if node_type( n ) = kind then
? "Running command on " & node_name( n );
ssh( "-oPreferredAuthentications=publickey",
LOGNAME & "@" & node_name( n ),
command_line.argument(2) );
if $? /= 0 then
put( standard_error, source_info.file )
@( standard_error, ": command failed on '" & node_name(n) & "'" );
command_line.set_exit_status( 193 );
return;
end if;
end if;
end loop;
end dispatch;
-- VIM editor formatting instructions
-- vim: ft=spar