#!/usr/bin/env perl
use strict;
use warnings;
use v5.10;
use App::Rad;
use Net::OpenStack::Attack;
use Net::OpenStack::Compute;
use Time::SoFar qw(runtime);

App::Rad->run();

sub setup {
    my $c = shift;
    $c->register_commands({
        bad     => 'send invalid requests',
        get     => 'send get servers requests',
        create  => 'create servers [--image|-i]',
        delete  =>
            'delete all stackattack servers [--all|-a] (deletes ALL servers)',
        rebuild =>
            'rebuild all stackattack servers [--all|-a] [--image|-i]',
    });
    $c->register('get-images' => \&get_images, 'send image list requests');
}

sub pre_process {
    my $c = shift;
    my $cmd = $c->cmd;
    if ($c->is_command($cmd) and $cmd ne 'help') {
        my $msg = "%s env var is missing. Did you forget to source novarc?\n";
        die sprintf($msg, 'NOVA_URL') unless $ENV{NOVA_URL};
        die sprintf($msg, 'NOVA_USERNAME') unless $ENV{NOVA_USERNAME};
        die sprintf($msg, 'NOVA_PROJECT_ID') unless $ENV{NOVA_PROJECT_ID};
        die sprintf($msg, 'NOVA_PASSWORD or NOVA_API_KEY')
            unless $ENV{NOVA_PASSWORD} || $ENV{NOVA_API_KEY};
        my $compute = Net::OpenStack::Compute->new(
            auth_url     => $ENV{NOVA_URL},
            user         => $ENV{NOVA_USERNAME},
            password     => $ENV{NOVA_PASSWORD} || $ENV{NOVA_API_KEY},
            project_id   => $ENV{NOVA_PROJECT_ID},
            region       => $ENV{NOVA_REGION_NAME},
            service_name => $ENV{NOVA_SERVICE_NAME},
            is_rax_auth  => $ENV{NOVA_RAX_AUTH},
        );
        $c->stash->{attack} = Net::OpenStack::Attack->new(compute => $compute);
    }
    $c->stash->{num_runs} = $c->argv->[0] || 1;
}

sub create {
    my $c = shift;
    $c->getopt('image|i=s');
    my $num_runs = $c->argv->[0] || 1;
    my $attack = $c->stash->{attack};
    my $image = $c->options->{image};
    return "Creating $num_runs servers ...\n"
        . _msg($attack->create_servers($num_runs, $image));
}

sub delete {
    my $c = shift;
    $c->getopt('all|a');
    my $attack = $c->stash->{attack};
    my $servers = _filter_servers($c);
    return "Deleting " . @$servers . " servers ...\n"
        . _msg($attack->delete_servers($servers));
}

sub rebuild {
    my $c = shift;
    $c->getopt('all|a');
    my $attack = $c->stash->{attack};
    my $servers = _filter_servers($c);
    return "Rebuilding " . @$servers . " servers ...\n"
        . _msg($attack->rebuild_servers($servers));
}

sub bad {
    my $c = shift;
    my $num_runs = $c->stash->{num_runs};
    my $attack = $c->stash->{attack};
    return "Sending $num_runs /bad requests..."
        . _msg($attack->attack(GET => '/bad', $num_runs));
}

sub get {
    my $c = shift;
    my $num_runs = $c->stash->{num_runs};
    my $attack = $c->stash->{attack};
    return "Sending $num_runs /servers requests..."
        . _msg($attack->attack(GET => '/servers', $num_runs));
}

sub get_images {
    my $c = shift;
    my $num_runs = $c->stash->{num_runs};
    my $attack = $c->stash->{attack};
    return "Sending $num_runs /images requests..."
        . _msg($attack->attack(GET => '/images', $num_runs));
}

sub _msg {
    my %data = %{ shift() };
    "Successes: $data{successes} Failures: $data{failures} Time: " . runtime();
}

sub _filter_servers {
    my $c = shift;
    my $attack = $c->stash->{attack};
    my $servers = $attack->compute->get_servers(detail => 0);
    return $servers if $c->options->{all};
    return [ grep { $_->{name} eq 'stackattack' } @$servers ];
}

# PODNAME: stackattack


__END__
=pod

=head1 NAME

stackattack

=head1 VERSION

version 1.0400

=head1 SYNOPSIS

    Usage: stackattack command [arguments]

    Available Commands:
        bad         send invalid requests
        create      create servers [--image|-i]
        delete      delete all stackattack servers [--all|-a] (deletes ALL servers)
        get         send get servers requests
        get-images  send image list requests
        help        show syntax and available commands
        rebuild     rebuild all stackattack servers [--all|-a] [--image|-i]

    Examples:

    # Create a server
    $ stackattack create

    # Create 10 servers in parallel
    $ stackattack create 10

    # Create 10 servers with an explicit image id
    $ stackattack create -i b79cf9f9-cea9-44c7-a3ac-74a6668eb78b 10

    # Delete all servers
    $ stackattack delete -a

=head1 DESCRIPTION

This is a command line utility for stress testing an OpenStack deployment.
All http requests are run in parallel using L<HTTP::Async>.

=head1 AUTHORS

=over 4

=item *

William Wolf <throughnothing@gmail.com>

=item *

Naveed Massjouni <naveedm9@gmail.com>

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Naveed Massjouni.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut

