* Create (but not yet tested) Server->shutdown() to, well, shutdown servers.

* Modified Server->boot() to now only work locally. Also updated it to optionally take the XML definition path for the server to boot.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 5 years ago
parent d224be9344
commit dff74102db
  1. 297
      Anvil/Tools/Server.pm
  2. 14
      ocf/alteeve/server
  3. 6
      share/words.xml

@ -15,6 +15,7 @@ my $THIS_FILE = "Server.pm";
# boot # boot
# find # find
# get_status # get_status
# shutdown
=pod =pod
@ -77,6 +78,24 @@ sub parent
=head2 boot =head2 boot
This takes a server name and tries to boot it (using C<< virsh create /mnt/shared/definition/<server>.xml >>. It requires that any supporting systems already be started (ie: DRBD resource is up).
If booted, C<< 1 >> is returned. Otherwise, C<< 0 >> is returned.
my ($booted) = $anvil->Server->boot({server => "test_server"});
Parameters;
=head3 definition (optional, see below for default)
This is the full path to the XML definition file to use to boot the server.
By default, the definition file used will be named C<< <server>.xml >> in the C<< path::directories::shared::deinitions >> directory.
=head3 server (required)
This is the name of the server, as it appears in C<< virsh >>.
=cut =cut
sub boot sub boot
{ {
@ -85,57 +104,35 @@ sub boot
my $anvil = $self->parent; my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $password = defined $parameter->{password} ? $parameter->{password} : ""; my $server = defined $parameter->{server} ? $parameter->{server} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : ""; my $definition = defined $parameter->{definition} ? $parameter->{definition} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root"; my $success = 0;
my $server = defined $parameter->{server} ? $parameter->{server} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "local";
my $success = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->secure ? $password : $anvil->Words->string({key => "log_0186"}), server => $server,
port => $port, definition => $definition,
remote_user => $remote_user,
server => $server,
target => $target,
}}); }});
if (not $server) if (not $server)
{ {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Server->get_status()", parameter => "server" }}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Server->boot()", parameter => "server" }});
return(1); return(1);
} }
if (not $definition)
# Is this a local call or a remote call?
my $shell_call = $anvil->data->{path}{exe}{virsh}." create ".$anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml";
my $output = "";
my $return_code = "";
if (($target) && ($target ne "local") && ($target ne $anvil->_hostname) && ($target ne $anvil->_short_hostname))
{
# Remote call.
($output, my $error, $return_code) = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call,
target => $target,
port => $port,
password => $password,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output,
error => $error,
return_code => $return_code,
}});
}
else
{ {
# Local. $definition = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml";
($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { efinition => $definition }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output,
return_code => $return_code,
}});
} }
# Is this a local call or a remote call?
my ($output, $return_code) = $anvil->System->call({
debug => $debug,
shell_call => $anvil->data->{path}{exe}{virsh}." create ".$definition,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output,
return_code => $return_code,
}});
# Wait up to five seconds for the server to appear. # Wait up to five seconds for the server to appear.
my $wait = 5; my $wait = 5;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'wait' => $wait }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'wait' => $wait }});
@ -215,6 +212,12 @@ sub find
target => $target, target => $target,
}}); }});
# Clear any old data
if (exists $anvil->data->{server}{location})
{
delete $anvil->data->{server}{location};
}
my $host_type = $anvil->System->get_host_type({debug => $debug}); my $host_type = $anvil->System->get_host_type({debug => $debug});
my $host = $anvil->_hostname; my $host = $anvil->_hostname;
my $virsh_output = ""; my $virsh_output = "";
@ -278,8 +281,6 @@ sub find
This reads in a server's XML definition file from disk, if available, and from memory, if the server is running. The XML is analyzed and data is stored under 'server::<server_name>::from_disk::x' for data from the on-disk XML and 'server::<server_name>::from_memory::x'. This reads in a server's XML definition file from disk, if available, and from memory, if the server is running. The XML is analyzed and data is stored under 'server::<server_name>::from_disk::x' for data from the on-disk XML and 'server::<server_name>::from_memory::x'.
Any pre-existing data on the server is flushed before the new information is processed. Any pre-existing data on the server is flushed before the new information is processed.
Parameters; Parameters;
@ -425,6 +426,216 @@ sub get_status
return(0); return(0);
} }
=head2 shutdown
This takes a server name and tries to shut it down. If the server was found locally, the shut down is requested and this method will wait for the server to actually shut down before returning.
If shut down, C<< 1 >> is returned. If the server wasn't found or another problem occurs, C<< 0 >> is returned.
my ($shutdown) = $anvil->Server->shutdown({server => "test_server"});
Parameters;
=head3 force (optional, default '0')
Normally, a graceful shutdown is requested. This requires that the guest respond to ACPI power button events. If the guest won't respond, or for some other reason you want to immediately force the server off, set this to C<< 1 >>.
B<WARNING>: Setting this to C<< 1 >> results in the immediate shutdown of the server! Same as if you pulled the power out of a traditional machine.
=head3 server (required)
This is the name of the server (as it appears in C<< virsh >>) to shut down.
=head3 wait (optional, default '0')
By default, this method will wait indefinetly for the server to shut down before returning. If this is set to a non-zero number, the method will wait that number of seconds for the server to shut dwwn. If the server is still not off by then, C<< 0 >> is returned.
=cut
sub shutdown
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $server = defined $parameter->{server} ? $parameter->{server} : "";
my $force = defined $parameter->{force} ? $parameter->{force} : 0;
my $wait = defined $parameter->{'wait'} ? $parameter->{'wait'} : 0;
my $success = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
force => $force,
server => $server,
}});
if (not $server)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Server->shutdown()", parameter => "server" }});
return($success);
}
if (($wait) && ($wait =~ /\D/))
{
# Bad value.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0422", variables => { server => $server, 'wait' => $wait }});
return($success);
}
# Is the server running?
$anvil->Server->find({debug => $debug});
# And?
if (exists $anvil->data->{server}{location}{$server})
{
my $shutdown = 1;
my $status = $anvil->data->{server}{location}{$server}{status};
my $task = "shutdown";
if ($force)
{
$task = "destroy";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "log_0424", variables => { server => $server }});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0425", variables => { server => $server }});
}
if ($status eq "shut off")
{
# Already off.
$success = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0423", variables => { server => $server }});
return($success);
}
elsif ($state eq "paused")
{
# The server is paused. Resume it, wait a few, then proceed with the shutdown.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0314", variables => { server => $server }});
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." resume $server"});
if ($return_code)
{
# Looks like virsh isn't running.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0315", variables => {
server => $server,
return_code => $return_code,
output => $output,
}});
$anvil->nice_exit({exit_code => 1});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0316"});
sleep 3;
}
elsif ($state eq "pmsuspended")
{
# The server is suspended. Resume it, wait a few, then proceed with the shutdown.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0317", variables => { server => $server }});
my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dompmwakeup $server"});
if ($return_code)
{
# Looks like virsh isn't running.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0318", variables => {
server => $server,
return_code => $return_code,
output => $output,
}});
$anvil->nice_exit({exit_code => 1});
}
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0319"});
sleep 30;
}
elsif (($state eq "idle") or ($state eq "crashed"))
{
# The server needs to be destroyed.
$task = "destroy";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0322", variables => {
server => $server,
'state' => $state,
}});
}
elsif ($state eq "in shutdown")
{
# The server is already shutting down
$shutdown = 0;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0320", variables => { server => $server }});
}
elsif ($state ne "running")
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "log_0325", variables => {
server => $server,
'state' => $state,
}});
return($success);
}
# Shut it down.
if ($shutdown)
{
my ($output, $return_code) = $anvil->System->call({
debug => $debug,
shell_call => $anvil->data->{path}{exe}{virsh}." ".$task." ".$server,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
output => $output,
return_code => $return_code,
}});
}
}
else
{
# Server wasn't found, assume it's off.
$success = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0423", variables => { server => $server }});
return($success);
}
# Wait indefinetely for the server to exit.
my $stop_waiting = 0;
if ($wait)
{
$stop_waiting = time + $wait;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { stop_waiting => $stop_waiting }});
};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'wait' => $wait }});
until($success)
{
# Update
$anvil->Server->find({debug => $debug});
if ((exists $anvil->data->{server}{location}{$server}) && ($anvil->data->{server}{location}{$server}{status}))
{
my $status = $anvil->data->{server}{location}{$server}{status};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { status => $status }});
if ($status eq "shut off")
{
# Success! It should be undefined, but we're not the place to worry about
# that.
$success = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0426", variables => { server => $server }});
}
}
else
{
# Success!
$success = 1;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0426", variables => { server => $server }});
}
if (($stop_waiting) && (time > $stop_waiting))
{
# Give up waiting.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0427", variables => {
server => $server,
'wait' => $wait,
}});
}
else
{
# Sleep a second and then try again.
sleep 1;
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { success => $success }});
return($success);
}
# =head3 # =head3
# #
# Private Functions; # Private Functions;

@ -524,8 +524,20 @@ sub stop_server
# Stopping the server is simply a question of "is the server running?" and, if so, stop it. Once # Stopping the server is simply a question of "is the server running?" and, if so, stop it. Once
# stopped, we stop the DRBD resource on both nodes. # stopped, we stop the DRBD resource on both nodes.
my $server = $anvil->data->{environment}{OCF_RESKEY_name}; my $server = $anvil->data->{environment}{OCF_RESKEY_name};
# We don't (for now) set 'wait', so this won't return until/unless the server actually shuts down.
my $success = $anvil->Server->shutdown({debug => 2, server => $server});
if (not $success)
{
# Something went wrong. Details should be in the logs.
$anvil->nice_exit({exit_code => 1});
}
### TODO: Left off here.
# Now stop the DRBD resource(s).
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "log_0324", variables => { server => $server }});
$anvil->nice_exit({exit_code => 0}); $anvil->nice_exit({exit_code => 0});
} }

@ -739,6 +739,12 @@ Failed to promote the DRBD resource: [#!variable!resource!#] primary. Expected a
<key name="log_0419">The server: [#!variable!server!#] needs the DRBD resource: [#!variable!resource!#]. Bringing it up locally and on the peer: [#!variable!peer!#] (via IP: #!variable!peer_ip!#).</key> <key name="log_0419">The server: [#!variable!server!#] needs the DRBD resource: [#!variable!resource!#]. Bringing it up locally and on the peer: [#!variable!peer!#] (via IP: #!variable!peer_ip!#).</key>
<key name="log_0420">DRBD's 'auto-promote' is disabled. Promoting the resource: [#!variable!resource!#].</key> <key name="log_0420">DRBD's 'auto-promote' is disabled. Promoting the resource: [#!variable!resource!#].</key>
<key name="log_0421">The server: [#!variable!server!#] is now running on the host: [#!variable!host!#].</key> <key name="log_0421">The server: [#!variable!server!#] is now running on the host: [#!variable!host!#].</key>
<key name="log_0422">The request to shutdown the server: [#!variable!server!#] was given the wait period of: [#!variable!wait!#], which is not a valid number of seconds.</key>
<key name="log_0423">The server: [#!variable!server!#] is already off.</key>
<key name="log_0424">The server: [#!variable!server!#] will now be forced off!</key>
<key name="log_0425">The server: [#!variable!server!#] will now be gracefully shut down.</key>
<key name="log_0426">The server: [#!variable!server!#] is now off.</key>
<key name="log_0427">[ Warning ] - The server: [#!variable!server!#] is not yet off after: [#!variable!wait!#] seconds. Giving up waiting.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>

Loading…
Cancel
Save