Group
Extension

App-Manoc/lib/App/Manoc/Controller/Device.pm

package App::Manoc::Controller::Device;
#ABSTRACT: Device Controller
use Moose;

our $VERSION = '2.99.4'; ##TRIAL VERSION

use namespace::autoclean;

BEGIN { extends 'Catalyst::Controller'; }


with
    "App::Manoc::ControllerRole::CommonCRUD",
    "App::Manoc::ControllerRole::JSONView" => { -excludes => 'get_json_object', },
    "App::Manoc::ControllerRole::CSVView";

use Text::Diff;

use App::Manoc::Form::Device::Edit;
use App::Manoc::Form::DeviceNWInfo;
use App::Manoc::Form::Cabling;
use App::Manoc::Form::Uplink;
use App::Manoc::Form::Device::Decommission;

use App::Manoc::Netwalker::Config;
use App::Manoc::Netwalker::ControlClient;

# moved  App::Manoc::Netwalker::DeviceUpdater to conditional block in refresh
# where we need it

__PACKAGE__->config(
    # define PathPart
    action => {
        setup => {
            PathPart => 'device',
        }
    },

    class            => 'ManocDB::Device',
    form_class       => 'App::Manoc::Form::Device::Edit',
    view_object_perm => undef,
    json_columns     => [ 'id', 'name' ],

    object_list_options => {
        prefetch => [ { 'rack' => 'building' }, 'mng_url_format', 'hwasset', 'netwalker_info', ]
    },

    create_page_title => 'New device',
    edit_page_title   => 'Edit device',
);


sub view : Chained('object') : PathPart('') : Args(0) {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};

    # uplinks
    $c->stash( uplinks => [ map { $_->interface } $device->uplinks->all() ] );

    # prepare template
    $c->stash( template => 'device/view.tt' );
}


sub ifstatus : Chained('object') : PathPart('ifstatus') : Args(0) {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};
    my $id     = $device->id;

    # TODO prefetch cabling
    my %cabling = map { $_->interface1 => 1 } $device->cablings;

    # prefetch interfaces last activity
    my %if_last_mat;
    my ( $e, $it );
    $it = $c->model('ManocDB::DeviceIface')->search_mat_last_activity($id);
    while ( $e = $it->next ) {
        $if_last_mat{ $e->get_column('interface') } = $e->get_column('lastseen');
    }

    my @iface_info = $device->interfaces->all;

    foreach my $r ( $device->interfaces->all ) {
        $r->{last_mat} = $if_last_mat{ $r->name },;
    }

    @iface_info =
        sort { $a->controller cmp $b->controller || $a->port <=> $b->port } @iface_info;

    $c->stash->{no_wrapper} = 1;
    $c->stash->{iface_info} = \@iface_info;
}


sub neighs : Chained('object') : PathPart('neighs') : Args(0) {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};

    my $time_limit = $c->config->{Device}->{cdp_age} || 3600 * 12;    #12 hours

    my @neighs =
        map +{
        expired        => time - $_->last_seen > $time_limit,
        local_iface    => $_->from_interface,
        remote_iface   => $_->to_interface,
        to_device      => $_->to_device,
        to_device_info => $_->to_device_info,
        remote_id      => $_->remote_id,
        remote_type    => $_->remote_type,
        date           => $_->last_seen,
        },
        $device->neighs( {}, { prefetch => 'to_device_info' } )->all();

    $c->stash->{no_wrapper} = 1;
    $c->stash->{neighs}     = \@neighs;
}


sub ssids : Chained('object') : PathPart('ssids') : Args(0) {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};

    # wireless info

    # ssid
    my @ssid_list = map +{
        interface => $_->interface,
        ssid      => $_->ssid,
        broadcast => $_->broadcast ? 'yes' : 'no',
        channel   => $_->channel
        },
        $device->ssids;
    $c->stash->{ssid_list} = \@ssid_list;

    $c->stash->{no_wrapper} = 1;
}


sub dot11clients : Chained('object') : PathPart('dot11clients') : Args(0) {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};

    my @dot11_clients = map +{
        ssid    => $_->ssid,
        macaddr => $_->macaddr,
        ipaddr  => $_->ipaddr,
        vlan    => $_->vlan,
        quality => $_->quality . '/100',
        state   => $_->state,
        },
        $device->dot11clients;
    $c->stash->{dot11_clients} = \@dot11_clients;

    $c->stash->{no_wrapper} = 1;
}


sub cablings : Chained('object') : PathPart('cablings') : Args(0) {
    my ( $self, $c ) = @_;

    my $device = $c->stash->{'object'};
    $c->require_permission( $device, 'view' );

    $c->stash->{no_wrapper} = 1;

    $c->stash->{cablings} = [ $device->cablings->all ];

    my $form = App::Manoc::Form::Cabling->new(
        {
            ctx => $c,
        }
    );
    $c->stash( form => $form );
}


sub refresh : Chained('object') : PathPart('refresh') : Args(0) {
    my ( $self, $c ) = @_;
    my $device_id = $c->stash->{object}->id;

    my $config = App::Manoc::Netwalker::Config->new( $c->config->{Netwalker} || {} );
    my $client = App::Manoc::Netwalker::ControlClient->new( config => $config );

    my $status = $client->enqueue_device($device_id);

    if ( !$status ) {
        $c->flash( error_msg => "An error occurred while scheduling device refresh" );
    }
    else {
        $c->flash( message => "Device refresh scheduled" );
    }

    $c->response->redirect( $c->uri_for_action( '/device/view', [$device_id] ) );
    $c->detach();
}


sub uplinks : Chained('object') : PathPart('uplinks') : Args(0) {
    my ( $self, $c ) = @_;

    my $device = $c->stash->{'object'};
    $c->require_permission( $device, 'edit' );

    my $form = App::Manoc::Form::Uplink->new( { device => $device, ctx => $c } );

    if ( $device->interfaces->count() == 0 ) {
        $c->flash( error_msg => 'No known interfaces on this device' );
        $c->uri_for_action( 'device/view', [ $device->id ] );
        $c->detach();
    }
    $c->stash(
        form   => $form,
        action => $c->uri_for( $c->action, $c->req->captures ),
    );
    return
        unless $form->process( params => $c->req->parameters, );

    $c->response->redirect( $c->uri_for_action( 'device/view', [ $device->id ] ) );
    $c->detach();
}


sub nwinfo : Chained('object') : PathPart('nwinfo') : Args(0) {
    my ( $self, $c ) = @_;

    $c->require_permission( $c->stash->{object}, 'netwalker_config' );

    my $device_id = $c->stash->{object_pk};

    my $nwinfo = $c->model('ManocDB::DeviceNWinfo')->find($device_id);
    $nwinfo or $nwinfo = $c->model('ManocDB::DeviceNWInfo')->new_result( {} );

    my $form = App::Manoc::Form::DeviceNWInfo->new(
        {
            device => $device_id,
            ctx    => $c,
        }
    );
    $c->stash( form => $form );
    return unless $form->process(
        params => $c->req->params,
        item   => $nwinfo
    );

    $c->response->redirect( $c->uri_for_action( 'device/view', [$device_id] ) );
    $c->detach();
}


sub iface : Chained('object') : PathPart('iface') : Args(1) {
    my ( $self, $c, $name ) = @_;

    my $iface = $c->model('ManocDB::DeviceIface')->find(
        {
            device_id => $c->stash->{device_id},
            name      => $name
        }
    );

    $c->response->redirect( $c->uri_for_action( 'deviceiface/view', [ $iface->id ] ) );
    $c->detach();
}


sub show_config : Chained('object') : PathPart('config') : Args(0) {
    my ( $self, $c ) = @_;

    my $device = $c->stash->{object};
    $c->require_permission( $device, 'show_config' );

    my $config = $device->config;

    my $prev_config = $config->prev_config;
    my $curr_config = $config->config;

    #Get diff and modify diff string
    my $diff = diff( \$prev_config, \$curr_config );

    #Clear "@@...@@" stuff
    $diff =~ s/@@[^@]*@@/<hr>/g;

    #Insert HTML "font" tag to color "+" and "-" rows
    $diff =~ s/^\+(.*)$/<font color=\"green\"> $1<\/font>/mg;
    $diff =~ s/^\-(.*)$/<font color=\"red\"> $1<\/font>/mg;

    #Prepare template
    $c->stash(
        config => $config,
        diff   => $diff,
    );
    $c->stash( template => 'device/show_run.tt' );

}


before 'create' => sub {
    my ( $self, $c ) = @_;

    my $rack_id = $c->req->query_parameters->{'rack'};
    if ( defined($rack_id) ) {
        $c->log->debug("new device in rack $rack_id") if $c->debug;
        $c->stash( form_defaults => { rack => $rack_id } );
    }

};


sub decommission : Chained('object') : PathPart('decommission') : Args(0) {
    my ( $self, $c ) = @_;

    $c->require_permission( $c->stash->{object}, 'edit' );

    my $form = App::Manoc::Form::Device::Decommission->new( { ctx => $c } );

    $c->stash(
        form   => $form,
        action => $c->uri_for( $c->action, $c->req->captures ),
    );
    return unless $form->process(
        item   => $c->stash->{object},
        params => $c->req->parameters,
    );

    $c->response->redirect( $c->uri_for_action( 'device/view', [ $c->stash->{object_pk} ] ) );
    $c->detach();
}


sub restore : Chained('object') : PathPart('restore') : Args(0) {
    my ( $self, $c ) = @_;

    my $device = $c->stash->{object};
    $c->require_permission( $device, 'edit' );

    if ( !$device->decommissioned ) {
        $c->response->redirect( $c->uri_for_action( 'device/view', [ $device->id ] ) );
        $c->detach();
    }

    if ( $c->req->method eq 'POST' ) {
        $device->restore;
        $device->update();
        $c->flash( message => "Device restored" );
        $c->response->redirect( $c->uri_for_action( 'device/view', [ $device->id ] ) );
        $c->detach();
    }

    # show confirm page
    $c->stash(
        title           => 'Restore network device',
        confirm_message => 'Restore decommissione device ' . $device->name . '?',
        template        => 'generic_confirm.tt',
    );
}


sub update_from_nwinfo : Chained('object') : PathPart('from_nwinfo') : Args(0) {
    my ( $self, $c ) = @_;

    my $device = $c->stash->{object};
    $c->require_permission( $device, 'edit' );

    my $response = {};
    $response->{success} = 0;

    if ( !$device->decommissioned &&
        defined( $device->netwalker_info ) &&
        $c->req->method eq 'POST' )
    {
        my $nwinfo = $device->netwalker_info;
        my $what   = lc( $c->req->params->{what} );

        if ( $what eq 'name' ) {
            $nwinfo->name and $device->name( $nwinfo->name );
        }
        $device->update();
        $response->{success} = 1;
    }

    $c->stash( json_data => $response );
    $c->forward('View::JSON');
}


sub ifacecreate : Chained('object') : PathPart('ifacecreate') : Args(0) {
    my ( $self, $c ) = @_;

    # device is already in stash
    $c->forward('/deviceiface/create');
}


sub ifacepopulate : Chained('object') : PathPart('ifacepopulate') : Args(0) {
    my ( $self, $c ) = @_;

    # device is already in stash
    $c->forward('/deviceiface/populate');
}


sub get_object {
    my ( $self, $c, $id ) = @_;

    my $object = $c->stash->{resultset}->find($id);
    if ( !defined($object) ) {
        $object = $c->stash->{resultset}->find( { mng_address => $id } );
    }

    if ($object) {
        $c->stash(
            device    => $object,
            device_id => $id,
        );
    }

    return $object;
}


sub delete_object {
    my ( $self, $c ) = @_;
    my $device = $c->stash->{'object'};
    my $name   = $device->name;

    my $has_related_info = $device->interfaces->count() ||
        $device->uplinks->count()      ||
        $device->mat_assocs()->count() ||
        $device->dot11assocs->count()  ||
        $device->neighs->count();

    if ($has_related_info) {
        $c->flash(
            error_msg => "Device '$device' has some associated info and cannot be deleted." );
        return;
    }

    return $device->delete;
}


sub get_json_object {
    my ( $self, $c, $device ) = @_;

    my $r = $self->prepare_json_object( $c, $device );
    $r->{rack} = $device->rack->id, return $r;
}

__PACKAGE__->meta->make_immutable;

1;
# Local Variables:
# mode: cperl
# indent-tabs-mode: nil
# cperl-indent-level: 4
# cperl-indent-parens-as-block: t
# End:

__END__

=pod

=head1 NAME

App::Manoc::Controller::Device - Device Controller

=head1 VERSION

version 2.99.4

=head1 CONSUMED ROLES

=over 4

=item *

App::Manoc::ControllerRole::CommonCRUD

=item *

App::Manoc::ControllerRole::JSONView

=item *

App::Manoc::ControllerRole::CSVView

=back

=head1 ACTIONS

=head2 view

=head2 ifstatus

Called via xhr by view

=head2 neighs

Called via xhr by view

=head2 ssids

Called via xhr by view

=head2 dot11clients

Called via xhr by view

=head2 cablings

Called via xhr by view

=head2 refresh

=head2 uplinks

=head2 nwinfo

=head2 show_config

Show running configuration

=head2 create

Override in order to manage rack parameter.

=head2 decommission

=head2 restore

=head2 update_from_nwinfo

=head2 ifacecreate

Redirect to deviceiface method

=head2 ifacepopulate

Redirect to deviceiface method

=head1 METHODS

=head2 iface

Get interface by names

=head2 get_object

Find by id or mng_address.

=head2 delete_object

=head2 get_json_object

=head1 AUTHORS

=over 4

=item *

Gabriele Mambrini <gmambro@cpan.org>

=item *

Enrico Liguori

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Gabriele Mambrini.

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


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.