Blob Blame History Raw
# Copyright (c) 2013-2016 Carbonite, Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# 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.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Contact information: Carbonite Inc., 756 N Pastoria Ave
# Sunnyvale, CA 94085, or: http://www.zmanda.com

package Amanda::Rest::Services;
use strict;
use warnings;

use Amanda::Config qw( :init :getconf config_dir_relative );
use Amanda::Debug;
use Amanda::Paths;
use Amanda::Service;
use Amanda::Rest::Configs;
use Symbol;
use Data::Dumper;
use JSON;
use IPC::Open3;

use vars qw(@ISA);

=head1 NAME

Amanda::Rest::Services -- Rest interface to amservice

=head1 INTERFACE

=over

=item Run amservice

 request:
  GET localhost:5000/amanda/v1.0/services/discover
    query arguments:
        host=HOST
        application=APPLICATION
        auth=AUTH
	config=CONFIG
	diskdevice=DISK

 reply:
  HTTP status: 200 Ok
  [
   {
      "application" : "amgtar",
      "code" : "2900002",
      "message" : "The application 'amgtar' does not support the 'discover' method",
      "severity" : "16",
      "source_filename" : "senddiscover.c",
      "source_line" : "270"
   }
  ]

=back

=cut

sub discover {
    my %params = @_;

    Amanda::Util::set_pname("Amanda::Rest::Services");
    my @amservice_args;
    my @result_messages;
    if ($params{'CONF'}) {
	my ($status, @result_messages) = Amanda::Rest::Configs::config_init(@_);
	return ($status, \@result_messages) if @result_messages;
    }
    my $ret;

    my $user_msg = sub {
	my $msg = shift;
	push @result_messages, $msg;
    };

    if (!defined $params{'application'} or !$params{'application'}) {
	push @result_messages, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100004,
			severity => $Amanda::Message::ERROR);
	return (-1, \@result_messages);
    }
    if (!defined $params{'host'} or !$params{'host'}) {
	push @result_messages, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100000,
			severity => $Amanda::Message::ERROR);
	return (-1, \@result_messages);
    }
    if (!defined $params{'auth'} or !$params{'auth'}) {
	push @result_messages, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100001,
			severity => $Amanda::Message::ERROR);
	return (-1, \@result_messages);
    }
    if ($params{'CONF'}) {
	push @amservice_args, '--config';
	push @amservice_args, $params{'CONF'};
    }
    push @amservice_args, $params{'host'};
    push @amservice_args, $params{'auth'};
    push @amservice_args, 'senddiscover';

    my $config_overrides = $params{'config_overrides'};
    if (defined $config_overrides) {
	if (ref $config_overrides eq 'ARRAY') {
	    for my $co (@{$config_overrides}) {
		push @amservice_args, "-o", $co;
	    }
	} else {
	    push @amservice_args, "-o", $config_overrides;
	}
    }

    Amanda::Debug::debug("Running: $Amanda::Paths::sbindir/amservice " . join(' ', @amservice_args));
    # fork the amservice process
    my($wtr, $rdr);
    my $pid = open3($wtr, $rdr, undef, "$Amanda::Paths::sbindir/amservice", @amservice_args);
    print $wtr "<dle>\n";
    if (defined $params{'diskdevice'}) {
	print $wtr "  <diskdevice>$params{'diskdevice'}</diskdevice>\n";
    }
    print $wtr "  <program>APPLICATION</program>\n";
    print $wtr "  <backup-program>\n";
    print $wtr "    <plugin>$params{'application'}</plugin>\n";
    if (defined $params{'esxpass'}) {
	print $wtr "    <property>\n";
	print $wtr "      <name>esxpass</name>\n";
	print $wtr "      <value>$params{'esxpass'}</value>\n";
	print $wtr "    </property>\n";
    }
    print $wtr "  </backup-program>\n";
    print $wtr "</dle>\n";
    close($wtr);

    #read stdout in a buffer
    my $first_line = <$rdr>;
    my $buf = "";
    while (my $line = <$rdr>) {
	$buf .= $line;
    }
    close($rdr);

    my $buffer = $first_line;
    $buffer .= "\n:" . $buf .":" if $buf;
    chomp $buffer;

    if (!defined $first_line) {
	push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100006,
			severity => $Amanda::Message::ERROR);
    } elsif ($first_line =~ /OPTIONS /) {
	#convert JSON buffer to perl object
	Amanda::Debug::debug("buf: $buf");
	eval { $ret = decode_json $buf };
	if ($@) {
	    $ret = undef;
	    push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100003,
			severity => $Amanda::Message::ERROR,
			errmsg   => $@,
			buffer   => $buffer);
	}
    }  else {
	push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code     => 3100002,
			severity => $Amanda::Message::ERROR,
			errmsg   => $buffer);
    }

    waitpid($pid, 0);
    my $child_error = $?;
    my $exit_code = $? >> 8;
    my $errno = $!;
    if ($child_error == -1) {
	push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code            => 3100008,
			severity        => $Amanda::Message::ERROR,
			program		=> "$Amanda::Paths::sbindir/amservice",
			errno		=> $errno);
    } elsif ($child_error & 127) {
	push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code            => 3100009,
			severity        => $Amanda::Message::ERROR,
			program		=> "$Amanda::Paths::sbindir/amservice",
			signal          => ($child_error & 127));
    } elsif ($child_error > 0) {
	push @{$ret}, Amanda::Service::Message->new(
			source_filename => __FILE__,
			source_line     => __LINE__,
			code            => 3100007,
			severity        => $exit_code == 0 ? $Amanda::Message::SUCCESS : $Amanda::Message::ERROR,
			program		=> "$Amanda::Paths::sbindir/amservice",
			exit_code       => $exit_code);
    }

    #return perl object
    return (-1, $ret);
}

1;