# COPYRIGHT_NOTICE_START
# Licensed Materials - Property of IBM
# "Restricted Materials of IBM" -- 5746-SM2
# (C) Copyright IBM Corp. 2007, 2008 All Rights Reserved.
# US Government Users Restricted Rights - Use, duplication or disclosure
# restricted by GSA ADP Schedule Contract with IBM Corp.
# COPYRIGHT_NOTICE_END

package Maiken::RO::PowerPack;

=head1 NAME

  Maiken::RO::PowerPack - interface to IBM's PowerPack

=head1 SYNOPSIS

  my $ppack = Maiken::RO::PowerPack->new(channel => '/tmp/channel',
                                         maiken  => $maiken,
                                         enabled => 1);

  $ppack->begin_operation(title => "install", steps => 10);
  $ppack->next_step(title => "Installing PowerVM Lx86");
  $ppack->progress(percent => 10, title => "Installing RPM");
  $ppack->end_operation;

  $ppack->error(message => "Can't find file");

=item DESCRIPTION

This interface allows Maiken classes to talk to IBM's PowerPack
installation interface.

You must provide both a Maiken object and a channel filename to the
constructor.

If you create the interface with 'enabled' set to false, all
operations will succeed silently without any effect.

This communication deals in terms of operations, steps and progress,
each less significant than the last.

From the outset, an operation must know how many steps it has, and
this is declared using the 'steps' argument to begin_operation. Maiken
will not generate an error if this number is incorrect, but it could
end up with the user being told that this is 'Step 7 of 5' etc, and
PowerPack's behaviour in this context is undefined.

Transitioning to the next step is done via the next_step function. The
step number does not need to be provided, as it is remembered
internally.

Within a step, progress updates can be made with a percentage (0-99)
and a message for the user.

The progress of 100 is automatically added without a message when the
next step begins.

When the operation is over, call the end_operation function.

If you encounter an error at any point, you should call the error
function.

=cut

use strict;
use warnings;

=head1 METHODS

=over 4

=item B<new>

  my $ppack = Maiken::RO::PowerPack->new(channel => '/tmp/channel',
                                         maiken  => $maiken,
                                         enabled => 1);

Create a new PowerPack object. Create the channel file. Inform
PowerPack that we have initialized.

You must specify the 'channel' and 'maiken' parameters if 'enabled'.

'enabled' must be true for this PowerPack to do anything.

=cut

sub new {
  my ($class, %args) = @_;

  my $self = bless \%args, $class;

  return $self unless $self->enabled;

  # Check we have a Maiken object
  unless (UNIVERSAL::isa($self->{maiken}, 'Maiken')) {
    die "PowerPack->new must be provided with a Maiken parameter!\n";
  }

  # Create the channel or overwrite it if already there
  open my $channel, '>', $self->{channel} or
    $self->croak("Cannot open [_1] for write: [_2]", $self->{channel}, $!);

  $self->log("Interface created. Channel is $self->{channel}.");

  # Send some initialization messages to the PowerPack
  $self->send("init");
  $self->send("info\elogfile\e".$self->{maiken}{logfile}->filename);

  $self->log("Initialized.");

  return $self;
}

=item B<begin_operation>

  $ppack->begin_operation(title => 'install', steps => 10);

Inform PowerPack that an operation has begun. It is necessary to know
how many steps the operation will have at this stage.

=cut

sub begin_operation {
  my ($self, %args) = @_;

  $self->log("Initializing operation $args{title}: $args{steps} steps");

  # Send the operation message
  $self->send("info\eoperation\e$args{title}");
  # Send the message informing PowerPack of the number of steps
  $self->send("info\enumberofsteps\e$args{steps}");

  # Reset the step counter
  $self->{total_steps} = $args{steps};
  $self->{step} = 0;

  # Record the operation name
  $self->{operation} = $args{title};
}

=item B<next_step>

  $ppack->next_step(title => 'Installing PowerVM Lx86');

Close off the last step (if there is one) and progress to the next
step. A title must be supplied for the step, but the number is
automatically calculated.

=cut

sub next_step {
  my ($self, %args) = @_;

  # Close off the last step, if there is one.
  if ($self->{step}) {
    $self->progress(percent => 100);
  }

  # Increment the step counter
  $self->{step}++;

  $self->log("Starting step $self->{step}: $args{title}");

  # Inform PowerPack of the new step
  $self->send("step\e$self->{step}\e$args{title}");
}

=item B<progress>

  $ppack->progress(percent => 0, title => "Installing RPM");

Inform PowerPack of the progress of the current step. The number
should be how many percent the step is complete, and the title should
be the operation it is about to perform or is currently performing.

=cut

sub progress {
  my ($self, %args) = @_;
  $args{title} ||= '';
  $self->send("status\e$args{percent}\e$args{title}");
}

=item B<end_operation>

  $ppack->end_operation;

Inform PowerPack that an operation has ended.

This will generate a logfile warning if the number of steps at this
stage is not the predicted number.

=cut

sub end_operation {
  my ($self, %args) = @_;

  # First, close off the existing step
  $self->progress(percent => 100);

  # Check the number of steps was what we expected
  if ($self->{total_steps} != $self->{step}) {
    $self->log("WARNING! Number of steps executed for operation '$self->{operation}' was incorrect! Expected: $self->{total_steps}; Executed: $self->{step}");
  }

  $self->log("Operation $self->{operation} ended after $self->{step} steps.");

  # Now send the 'end' message
  $self->send('end');
}

=item B<error>

  $ppack->error(message => "Oops something went wrong!");

Report an error to the PowerPack.

=cut

sub error {
  my ($self, %args) = @_;
  $self->log("Error received: $args{message}");
  $self->send("error\e$args{message}");
}

=item B<send>

  $ppack->send($msg);

Send a message directly to the channel. You probably shouldn't be using this!

=cut

sub send {
  my ($self, $msg) = @_;
  return unless $self->enabled;
  open my $channel, '>>', $self->{channel} or
    $self->croak("Can't open [_1] for write: [_2]", $self->{channel}, $!);
  print $channel "$msg\n";
  close $channel;
}

=item B<enabled>

  $ppack->enabled;

Returns true if this PowerPack object is enabled.

=cut

sub enabled {
  my ($self) = @_;
  return $self->{enabled};
}

=item B<croak>

  $ppack->croak($msg, @sub_vars);

Calls croak in the associated Maiken object.

=cut

sub croak {
  my ($self, @args)  = @_;
  $self->{maiken}->croak(@args);
}

=item B<log>

  $ppack->log($msg);

Calls log_only_to_file in the associated Maiken object with
[POWERPACK] prefixed and a newline appended.

=cut

sub log {
  my ($self, $msg) = @_;
  return unless $self->enabled;
  $self->{maiken}->log_only_to_file("[POWERPACK] $msg\n");
}

=back 4

=cut

1;
