#!/usr/bin/perl -w
#
# Copyright (c) 2010 CERN. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); 
# you may not use this file except in compliance with the License. 
# You may obtain a copy of the License at 
#
#    http://www.apache.org/licenses/LICENSE-2.0 
#
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
# See the License for the specific language governing permissions and 
# limitations under the License.
#

use strict;
use warnings;
use Config::General;
use Data::Dumper;
use DBI;
use Getopt::Long;
use Pod::Usage;

use Net::STOMP::Client;
use GridMon::MetricOutput;
use POSIX qw(strftime);

our(%opt);

sub error ($@) {
  my($format, @arguments) = @_;
  my($message);

  $message = sprintf($format, @arguments);
  $message =~ s/\s+$//;
  die("mrs-replay-messages: $message\n");
}

#
# load the configuration file
#

sub load_config ($) {
    my($path) = @_;
    my($config, %config, $name, $option);

    error("invalid configuration path: %s", $path) unless -f $path and -s _;
    eval {
        $config = Config::General->new(
            -ConfigFile        => $path,
            -AllowMultiOptions => "yes", 
            -InterPolateVars   => "yes", 
            -InterPolateEnv    => "yes",
            -UseApacheInclude  => "yes",
            -IncludeGlob       => "yes",
            );
            %config = $config->getall();
    };
    error("cannot parse %s: %s", $path, $@) if $@;
    # handle <option>
    if ($config{option}) {
        error("invalid <option> block in %s", $path)
             unless ref($config{option}) eq "HASH";
        foreach $name (keys(%{ $config{option} })) {
            error("unexpected option in %s: %s", $path, $name)
            unless $name =~ /^(db-uri|db-pwd|db-user)$/
            and not ref($config{option}{$name});
            ($option = $name) =~ s/-/_/;
            next if defined($opt{$option});
            $opt{$option} = $config{option}{$name};
        }
        delete($config{option});
    }   
    # complain if we see extra configuration information
    if (keys(%config)) {
        ($name) = keys(%config);
        error("unexpected configuration in %s: %s", $path, $name);
    }
}
        

sub extract_messages($$) {
    my($db, $broker) = @_;
    print "Extracting From $opt{start} to $opt{end}.\n";
    
    my $query = "select service.serviceendpoint as serviceURI, service.hostname as hostName,\
          (select service_type_flavour.flavourname from mrs.service_type_flavour where service_type_flavour.id = service.flavour_id) as serviceFlavour,\
          (select metric_set.name from mrs.metric, mrs.metric_set where  metricdata.metric_id =  metric.id and metric.metric_set_id = metric_set.id) as serviceType,\
          (select site.sitename from mrs.site, mrs.service, mrs.service_site where service.id = metricdata.service_id and service_site.service_id = service.id and service_site.site_id = site.id) as siteName,\
          (select metricstatus.description from mrs.metricstatus where metricdata.metricstatus_id = metricstatus.id) as metricStatus,\
          (select vo.voname from mrs.vo where metricdata.vo_id = vo.id) as voName,\
    	  (select fqan.name from mrs.fqan where metricdata.fqan_id = fqan.id) as voFqan,\
    	  (select metric.name from mrs.metric where metricdata.metric_id =  metric.id) as metricName,\
          metricdata.summarydata as summaryData,\
          (select gatheredat.name from mrs.gatheredat where gatheredat.id = metricdata.gatheredat_id) as gatheredAt,\
           metricdata.check_time as timestamp,\
          (select metricdetails.detail from mrs.metricdetails where metricdetails.id = metricdata.metricdetail_id ) as detailsData\
    from mrs.metricdata, mrs.service where metricdata.service_id = service.id \
              and metricdata.check_time >= UNIX_TIMESTAMP(?) and metricdata.check_time < UNIX_TIMESTAMP(?);";
    
    my $handle = $db->prepare($query);
    
    $handle->execute(($opt{start},$opt{end}));
    
    my $ref;
    my $count = 0;
    my $bad_count = 0;
    while ($ref = $handle->fetchrow_hashref) {  # retrieve one row
        next if $$ref{metricStatus} eq 'MISSING';
        next if $$ref{metricStatus} eq 'REMOVED';
        
        my $message;
        eval {
            # Next 2 lines are to deal with missing metric_sets in the DB
            $$ref{serviceType} = $$ref{serviceFlavour} if !defined $$ref{serviceType};
            $$ref{serviceType} = 'gLite-FTS-WS' if $$ref{serviceType} eq 'FTS';
            $$ref{serviceType} = 'org.sam.CREAMCE' if $$ref{serviceType} eq 'CREAM-CE';
            
            $message = GridMon::MetricOutput->new({  metric => $$ref{metricName},
                                                    status => $$ref{metricStatus},
                                                    summary => $$ref{summaryData},
                                                    site => $$ref{siteName},
                                                    service_flavour => $$ref{serviceFlavour},
                                                    service_type => $$ref{serviceType},
                                                    host_name => $$ref{hostName},
                                                    service_uri => $$ref{serviceURI},
                                                    details => $$ref{detailsData},
                                                    vo => $$ref{voName},
                                                    vo_fqan => $$ref{voFqan},
                                                    role => $opt{role}});
            # Update timestamp to that of original mesage
            $message->{timestamp} = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime($$ref{timestamp}));
        };
        if ($@) {
            print "Invalid message : ".Dumper($ref) if $opt{verbose};
            $bad_count++;
            next;
        }
        my %frame;
        $frame{sitename} = $$ref{siteName};
        $frame{role} = $opt{role};
        $frame{ROC} = $opt{roc};
        $frame{'nagios-host'} = $$ref{gatheredAt}; 
        
        $frame{body} = $message->wlcg_format();    
        $frame{persistent} = "true";
        $frame{destination} = $opt{destination};
        print Dumper(%frame) if $opt{debug};
        $broker->send(%frame) and $count++;
    }
    print "Dropped $bad_count bad messages\n" if $opt{verbose} && $bad_count > 0;
    return $count;
}

#
# initialise
#

# defaults

# options parsing
$opt{debug}='';
$opt{verbose}='';

GetOptions(
    "broker=s" => \$opt{broker_host},
    "destination=s" => \$opt{destination},
    "role=s" => \$opt{role},
    "roc=s" => \$opt{roc},
    "config=s"       => \$opt{config},
    "db-uri=s"       => \$opt{db_uri},
    "db-user=s"      => \$opt{db_user},
    "db-pwd=s"       => \$opt{db_pwd},
    "help|h|?"       => \$opt{help},
    "verbose|v"      => \$opt{verbose},
    "debug|d" => \$opt{debug},
    "start=s" => \$opt{start},
    "end=s" => \$opt{end},
) or pod2usage(2);

pod2usage(1) if $opt{help};
pod2usage(exitstatus => 0, verbose => 2) if $opt{manual};
pod2usage(2) if @ARGV;

load_config($opt{config}) if $opt{config};

$opt{db_uri} or error("--db-uri must be specified");
$opt{db_pwd} or error("--db-pwd must be specified");
$opt{db_user} or error("--db-user must be specified");

$opt{start} or error("Specify start time for export e.g '2010-06-01 10:00:00'");
$opt{end} or error("Specify stop time for export e.g '2010-06-01 10:00:00'");

$opt{broker_host} or error("Broker host must be specified");
$opt{destination} or error("destination must be specified");
$opt{role} or error("role must be specified");
$opt{role} or error("ROC must be specificed");
    
    
my $db = DBI->connect('DBI:mysql:'.$opt{db_uri}, $opt{db_user}, $opt{db_pwd}, {AutoCommit => 0, RaiseError => 1}) 
   or error "Could not connect to database: $DBI::errstr";

   
my $broker_uri= "stomp://".$opt{broker_host}.":6163/";
my $broker;
eval {
    $broker = Net::STOMP::Client->new(uri => $broker_uri);
    print "Connected to $broker_uri...\n" if $opt{verbose};
};
if ($@) {
    error("Could not connect to broker : $@");
}
$broker->connect();
my $num_extracted = extract_messages($db, $broker);

print "Extracted $num_extracted messages\n" if $num_extracted;
$broker->disconnect();
$db->disconnect();

exit(0);

















=head1 NAME

mrs-replay-messages - extract messages for a given period from a MRS database
and replay to messaging.

=head1 SYNOPSIS

B<mrs-replay-messages> [I<OPTIONS>]

=head1 DESCRIPTION

mrs-replay-messages is a tool for extratcing messages from a Metric Store
databnase and replaying them to messaging.

=head1 OPTIONS

=over

=item B<--config>

config file.

=item B<--db-uri>

DB URI (e.g.mrs;host=HOSTNAME).
 
=item B<--db-user>

DB User name.

=item B<--db-pwd>

DB password.

=item B<--help>, B<-h>, B<-?>

show some help

=item B<--verbose>, B<-v>

print verbose logging information.

=back

=head1 AUTHOR

SAM <sam-support@cern.ch>

=cut
