package RackMan::Tasks;
use Moose::Role;
use RackMan;
use RackMan::Types;
use Scalar::Util qw< looks_like_number >;
use Term::ANSIColor qw< BOLD RED >;
use namespace::autoclean;
#
# task_list()
# ---------
sub task_list {
my ($class, $opts) = @_;
print STDERR "usage: $::COMMAND list <pdu | server | switch | all>\n"
and return if $opts->{stdout} and not defined $opts->{type};
# the object connected to the RackTables database
my $rackman = $opts->{rackman};
my $racktables = $rackman->racktables;
my $req_type = lc $opts->{type};
my %conds;
if ($req_type ne "all") {
# determine the type of objects to look
$req_type = RackMan::Types->to_racktables($req_type);
# construct the additional WHERE conditions
%conds = ( dict_value => $req_type );
}
# create the resultset
my $list = $racktables->resultset("RackObject")->search(
{ chapter_id => 1, %conds },
{ join => "dictionary", order_by => { -asc => "name" } },
);
my $r = 1;
my @objects;
$opts->{as} ||= "simple";
# fetch and print the corresponding objects
while (my $rackobject = $list->next) {
my $id = $rackobject->id;
my $name = $rackobject->name;
my $type = $rackobject->dictionary->dict_value;
not defined and $_ = "(null)" for $name, $type;
# direct output on screen
if ($opts->{stdout}) {
my $rackdev = $rackman->device_by_id($id);
if ($opts->{as} eq "ldif") {
require RackMan::Format::LDAP;
my $ldif = eval { RackMan::Format::LDAP::make_ldif($rackdev) };
$r &&= print $ldif, $/ if $ldif;
}
else {
$r &&= printf "%s (%d), %s, use:%s\n", $name, $id, $type,
eval { $rackdev->attributes->{Use} } || "(null)";
}
}
# don't print, return the data
else {
push @objects, { id => $id, name => $name, type => $type };
}
}
if ($opts->{stdout}) {
return $r
}
else {
return wantarray ? @objects : \@objects
}
}
#
# task_info()
# ---------
sub task_info {
my ($self, $opts) = @_;
print "Name: ", $self->object_name, " (", $self->object_id, ")", $/,
"Type: ", $self->object_type, $/;
print "Asset tag: ", $self->object_asset_no, $/ if $self->object_asset_no;
print $/;
print "Attributes\n----------\n";
my $attr = $self->attributes;
print " $_: $attr->{$_}\n" for sort keys %$attr;
print $/;
print "Tags\n----\n";
print " explicit tags: ", join(", ", @{ $self->explicit_tags }), $/;
print " implicit tags: ", join(", ", @{ $self->implicit_tags }), $/;
print $/;
print "Location\n--------\n";
if (my ($parent_id) = @{ $self->parents }) {
my $parent = $self->rackman->device_by_id($parent_id);
print " host: ", $parent->object_name, $/;
}
print " rack: ", $self->rack->{name}, $/,
" site: ", $self->rack->{row_name}, $/;
print $/;
print "Comment\n-------\n", $self->object_comment, $/, $/
if $self->object_comment;
# choose the appropriate sort function for the ports
if ($self->object_type eq "Switch") {
*by_port_name = sub {
my @a = ( (split "/", $a->{name}), 0, 0);
my @b = ( (split "/", $b->{name}), 0, 0);
lc $a[0] cmp lc $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2]
}
} else {
*by_port_name = sub {
my @a = looks_like_number($a->{name})
? ("", $a->{name}) : ($a->{name}, "");
my @b = looks_like_number($b->{name})
? ("", $b->{name}) : ($b->{name}, "");
lc $a[0] cmp lc $b[0] || $a[1] <=> $b[1]
}
}
if (@{ $self->ports }) {
print "Ports\n-----\n";
printf " [%s] --(%s)--> %s [%s]\n",
$_->{name}, $_->{oif_name},
$_->{peer_object_name}||"", $_->{peer_port_name}||""
for sort by_port_name @{ $self->ports };
print $/;
}
if ($self->has_ipv4addrs) {
print "IPv4 addresses\n--------------\n";
for my $addr (@{ $self->ipv4addrs }) {
print " $addr->{type} | $addr->{iface} | $addr->{addr}\n"
}
my $gateway = $self->default_ipv4_gateway->{addr};
print " * default gateway: $gateway\n" if defined $gateway;
}
if ($self->has_ipv6addrs) {
print "IPv6 addresses\n--------------\n";
for my $addr (@{ $self->ipv6addrs }) {
print " $addr->{type} | $addr->{iface} | $addr->{addr}\n"
}
}
print $/;
}
#
# task_dump()
# ---------
sub task_dump {
my ($self, $opts) = @_;
# extract the meaningful parts of the object
my %struct;
my @attrs = grep { !/^rack/ } map { $_->name }
$self->meta->get_all_attributes;
push @attrs, qw<
object_id object_name object_asset_no
object_comment object_has_problem rack
>;
# .. and copy over its attributes
for my $attr (@attrs) {
$struct{$attr} = $self->$attr;
}
$opts->{as} ||= "yaml";
my $dump;
if ($opts->{as} eq "ldif") {
require RackMan::Format::LDAP;
$dump = RackMan::Format::LDAP::make_ldif($self);
}
elsif ($opts->{as} eq "json") {
require JSON;
$dump = JSON::encode_json(\%struct);
}
elsif ($opts->{as} eq "perl") {
$dump = \%struct
}
else {
require YAML;
$dump = YAML::Dump(\%struct);
}
if ($opts->{stdout}) {
# stringify Perl structure
if (ref $dump) {
require Data::Dumper;
my $dumper = Data::Dumper->new([$dump]);
$dumper->Indent(1)->Sortkeys(1)->Terse(1);
$dump = $dumper->Dump;
}
# add a newline if output is missing one (typically for JSON)
$dump .= "\n" unless $dump =~ /\n$/;
return print $dump
}
else {
return $dump
}
}
#
# task_write()
# ----------
sub task_write {
my ($self, $opts) = @_;
my $verbose = $opts->{rackman}->options->{verbose};
if ($self->class and not $opts->{rackman}->options->{no_write_dev_cfg}) {
# invoke the write_config() method, which should be provided by
# the appropriate device role (RackMan::Device::*)
my ($type) = $self->class =~ /::(\w+)$/;
print "- ", BOLD($type), "\n" if $verbose;
eval {
$self->write_config({
rackman => $opts->{rackman},
verbose => $verbose,
}); 1
} or warn $@;
}
# invoke the write() method from each format that the device
# role has declared to be relevent
for my $format ($self->formats) {
print "- ", BOLD($format) if $verbose;
# load the module
my $module = "RackMan::Format::$format";
if (not eval { Class::MOP::load_class($module) }) {
print BOLD(RED(" -- failed")), "\n" if $verbose;
(my $error = $@) =~ s/INC \(.+$/INC/sm;
RackMan->warning("can't load $module: $error");
next
}
print $/ if $verbose;
# execute its write() method
eval {
$module->write({
rackdev => $self,
rackman => $opts->{rackman},
verbose => $verbose,
}); 1
} or warn $@;
}
}
#
# task_diff()
# ---------
sub task_diff {
my ($self, $opts) = @_;
my $verbose = $opts->{verbose} = $opts->{rackman}->options->{verbose};
RackMan->error("object '", $self->object_name, "' has no hardware trait")
unless $self->class;
RackMan->error("hardware trait '", $self->class, "' has no support for ",
"the 'diff' task") unless $self->class->can("diff_config");
my ($type) = $self->class =~ /::(\w+)$/;
print "- ", BOLD($type), "\n" if $verbose;
eval { $self->diff_config($opts); 1 } or warn $@;
}
#
# task_push()
# ---------
sub task_push {
my ($self, $opts) = @_;
my $verbose = $opts->{verbose} = $opts->{rackman}->options->{verbose};
RackMan->error("object '", $self->object_name, "' has no hardware trait")
unless $self->class;
RackMan->error("hardware trait '", $self->class, "' has no support for ",
"the 'diff' task") unless $self->class->can("diff_config");
my ($type) = $self->class =~ /::(\w+)$/;
print "- ", BOLD($type), "\n" if $verbose;
eval { $self->push_config($opts); 1 } or warn $@;
}
__PACKAGE__
__END__
=pod
=head1 NAME
RackMan::Tasks - High-level tasks
=head1 DESCRIPTION
This module provides high-level tasks for working on RackObjects.
It especially implements the actions documented in L<rack> under
the methods named C<task_$action>.
=head1 METHODS
All tasks expect arguments to be given as a hashref, the following ones
being common to all tasks:
=over
=item *
C<rackman> - C<RackMan> instance
=item *
C<stdout> - indicate that the task is executed from the C<rack> command,
and as such should send results to stdout
=back
=head2 task_diff
Print the difference between the actual and expected configuration
of the device corresponding to the given RackObject
=head2 task_dump
Print or return (an extract of) the internal structure of the RackObject.
The task prints on standard output when option C<stdout> is set,
otherwise returns the result.
B<Arguments>
=over
=item *
C<as> - specify the format: C<json>, C<perl>, C<yaml>
=back
=head2 task_info
Print information about the RackObject
=head2 task_list
Print the list the RackObject of the given type (C<server>, C<pdu>,
C<switch> or C<all>)
=head2 task_push
Push the configuration to the device corresponding to the given RackObject
=head2 task_write
Generate and write the configuration files for the given RackObject
=head1 AUTHOR
Sebastien Aperghis-Tramoni (sebastien@aperghis.net)
=cut