<%doc> -- Operations Worksheet -- Interface (similar to device worksheet) for viewing/updating NOC relevant infomation. This includes customer contacts, devices, sites, circuits. % % <%args> $id => undef $table => undef $search => undef $_action => undef $status => undef $view => "Basics" $edit => undef $user => $ui->get_current_user($r) $only_show_available_contacts => 1 % % <%attr> title => 'Operations' section => 'Operations' % % <%init> use Netdot::Operations; my $DEBUG = 0; my $obj = undef; my $list; my @tables; my %results; my %stables; my $caller_args = $m->caller_args(-1); @{ $list } = (); if( exists( $caller_args->{page} ) ) { $user->setAttribute($r, "OPERATIONS_PAGE", $caller_args->{page}); } my $page = $user->getAttribute("OPERATIONS_PAGE"); print '%ARGS is
', Dumper(%ARGS), '

' if $DEBUG; if( $search ) { @tables = qw( Connection Device Entity Person Site RR ); if( defined( $search ) && $search =~ /\w+/ ) { # walk through a set of tables and search each column foreach my $tbl ( @tables ) { my @columns = $tbl->_essential(); if( $tbl ne "Connection" && $tbl ne "RR" ) { push @columns, "aliases"; } my %linksto = $ui->meta->get_links_to($tbl); foreach my $col ( @columns ) { if( $col eq "id" || $linksto{$col} ) { next; } else { $stables{$tbl}{$col} = 1; } } } foreach my $tbl ( keys %stables ) { foreach my $col ( keys %{ $stables{$tbl} } ) { my $q = "%" . $search . "%"; map { $results{$tbl}{$_->id} = $_ } $tbl->search_like( $col => $q ); } } } elsif( defined( $search ) && $search !~ /\w+/ ) { print "

No search criteria"; $m->abort; } elsif( defined( $id ) && defined( $table ) ) { $obj = $table->retrieve($id); if( ! defined( $obj) ) { $m->comp( 'error.mhtml', error => "Could not retrieve $table with id $id" ); } } } elsif( $status ) { @tables = qw( BGPPeering Device Interface IpService ); } elsif( defined( $id ) && defined( $table ) ) { $obj = $table->retrieve($id); if( ! defined( $obj) ) { $m->comp( 'error.mhtml', error => "Could not retrieve $table with id $id" ); } } if( %results ) { # Tables with entity column: Connection Device Person # Site must use EntitySite to look up the corresponding Entity foreach my $tbl ( keys %results ) { foreach my $id ( keys %{ $results{$tbl} } ) { if( $tbl eq "RR" ) { my $s = ( Device->search( name => $results{$tbl}{$id} ) )[0]; if( $s ) { $results{"Device"}{$s->id()} = $s; } delete( $results{$tbl}{$id} ); } } } delete( $results{"RR"} ); if( %results && scalar( keys %results ) == 1 ) { my $tbl = ( keys %results )[0]; if( scalar( keys %{ $results{$tbl} } ) == 1 ) { undef( $search ); $obj = ( values %{ $results{$tbl} } )[0]; $table = $tbl; $id = $obj->id(); } } if( defined( $search ) ) { foreach my $tbl ( keys %results ) { map { push @{ $list }, $results{$tbl}{$_} } keys %{ $results{$tbl} }; } } } elsif( $obj ) { ; # do nothing } else { print "

No results"; $m->abort; } % <%once> my $names = 0; my $dispatch = sub { # Each $table type will have specific ``call chains'' # (e.g. Device->interfaces->nearcircuits->cid is the chain to circuit # ids for a device) my $key = shift; my %table = ( Device => '.DeviceHandler', # Interface => '.InterfaceHandler', Circuit => '.CircuitHandler', Entity => '.EntityHandler', # ContactList => '.ContactListHandler', Site => '.SiteHandler', Person => '.PersonHandler', ); die "Don't know how to dispatch \"$key\"s" unless exists $table{$key}; return $table{$key}; }; %

% % if( $search ) { % <&| .MakeContainerOutside, lhs => scalar( @{$list} ) . " results for $search" &> % foreach my $tbl ( sort keys %results ) { % my @list = values %{ $results{$tbl} }; <&| .MakeContainer, lhs => $tbl, &> <& /generic/sortresults.mhtml, table => $tbl, object => \@list, view => "row", page => "operations.html", withedit => 1 &> % } % % } elsif( $obj ) { # Displaying a single thing % % my $lbl = $ui->getobjlabel($obj, ", ");
<% $table %>: <% $obj->view_link %> 
View: % if( $view eq "Basics" ) { # The default view [Basics] % } else { [Basics] % }
% if( $table eq "Device" ) { [view details]  [delete] 
% } else {
% }
% $m->comp( $dispatch->($table) , obj => $obj, only_show_available_contacts => $only_show_available_contacts); #, dispatch => $dispatch );
% % } elsif( $status ) { print "

Not yet implemented."; % } %

<%def .PersonHandler > <%args> $obj => 0 $accumulate_contact_lists => 0 $only_show_available_contacts <%init> my $contact_lists = [map {$_->contactlist} $obj->roles];
<% $obj->table %>:<% $obj->view_link %>
Contact List: <% map {$_->name} @$contact_lists %>
<&| .MakeContainer, lhs => "Devices @{[ $obj->label ]} is responsible for:" &> % foreach my $device ($obj->devices) { <& .DeviceHandler, obj => $device, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> % } <&| .MakeContainer, lhs => $obj->location->table &> <& .SiteHandler, obj => $obj->location, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> <&| .MakeContainer, lhs => $obj->entity->table &> <& .EntityHandler, obj => $obj->entity, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> <& .DisplayAvailableContacts, contact_lists => $contact_lists, only_show_available_contacts => $only_show_available_contacts, obj => $obj &> <%def .EntityHandler > <%args> $obj => 0 $accumulate_contact_lists => 0 $only_show_available_contacts <%init> my $contact_lists = [$obj->contactlist];
<% $obj->table %>:<% $obj->view_link %>
Contact List: <% map {$_->name} @$contact_lists %>
% if (scalar $obj->sites) { <&| .MakeContainer, lhs => "Sites" &> % foreach my $site (map {$_->site} $obj->sites) { <& .SiteHandler, obj => $site, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> % } % } % if (scalar $obj->devices) { <&| .MakeContainer, lhs => "Devices" &> % foreach my $device ($obj->devices) { <& .DeviceHandler, obj => $device, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> % } % } % if (scalar $obj->all_circuits) { <&| .MakeContainer, lhs => "@{[$obj->table]} Circuits" &> <& .DisplayCircuits, obj => $obj, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> % } % if (ref $accumulate_contact_lists eq "ARRAY") { % @$accumulate_contact_lists = @$contact_lists; % } else { <& .DisplayAvailableContacts, contact_lists => $contact_lists, only_show_available_contacts => $only_show_available_contacts, obj => $obj &> % } <%def .SiteHandler> <%args> $obj => 0 $accumulate_contact_lists => 0 $only_show_available_contacts <%init> #$m->comp('info', table => $obj->contactlist->table, id => $obj->contactlist->id); # *** Still need the city (and contact list). # my $lhs = $obj->view_link . ':' . $obj->city . " " . $obj->state; my $contact_lists = [$obj->contactlist];
Site:<% $obj->view_link %>
Contact List: <% map {$_->name} @$contact_lists %>
<&| '.MakeContainer', lhs => "Devices", &> % foreach my $device ($obj->devices) { <& .DeviceHandler, obj => $device, only_show_available_contacts => $only_show_available_contacts, accumulate_contact_lists => $contact_lists &> % } % if (ref $accumulate_contact_lists eq "ARRAY") { % @$accumulate_contact_lists = @$contact_lists; % } else { <& .DisplayAvailableContacts, contact_lists => $contact_lists, only_show_available_contacts => $only_show_available_contacts, obj => $obj &> % } %# When you read calling code the `.' indicates it is a mason %# subroutine (as opposed to a module from a file). <%def .DeviceHandler> <%doc> <%args> $obj => 0 # If a list ref is passed for this parameter then, instead of # passing this devices contact lists to .DisplayAvailableContacts, # this devices contact lists will be pushed onto # @$accumulate_contact_lists. This is used to prevent the same # contact list from being printed multiple times when showing the # operations page for something like a site which may have # multiple devices. $accumulate_contact_lists => 0 $only_show_available_contacts <%init> my @contact_lists = map { $_->contactlist } $obj->contacts; my $lhs = $obj->view_link . ':' . $obj->productname->name . " " . $obj->productname->type->name; <&| '.MakeContainer', lhs => $lhs, rhs => "Contact List(s): @{[map {$_->name } @contact_lists]}" &> % if (scalar $obj->all_circuits != 0) { <&| '.MakeContainer', lhs => 'Device Circuits' &> <& .DisplayCircuits, obj => $obj &> % } %# Display out of bound info if there is any. % if ($obj->oobname ne "" or $obj->oobnumber ne "") { <&| '.MakeContainer', lhs => 'OOB Info' &> <& '/generic/attribute_table.mhtml', field_headers => ['oobname', 'oobnumber'], data => [$obj->oobname, $obj->oobnumber] &> % } %# Display uplinks and downlinks. <&| '.MakeContainer', lhs => "Interface Links", &> % foreach my $interface ($obj->interfaces) { %# Are there any {up,down}links? % if (scalar ($interface->children, $interface->parents)) { %# allen-304-sw1 has multiple uplinks <& /generic/data_table.mhtml, field_headers => ['interface', 'uplinks', 'downlinks'], data => [[ $interface->view_link, $m->scomp('/generic/data_table.mhtml', data => [map {[$_->view_link]} $interface->parents]), $m->scomp('/generic/data_table.mhtml', data => [map {[$_->view_link]} $interface->children]), ]] &> % } % } % if (ref $accumulate_contact_lists eq "ARRAY") { % push @$accumulate_contact_lists, @contact_lists; % } else { <& .DisplayAvailableContacts, contact_lists => \@contact_lists, obj => $obj, only_show_available_contacts=> $only_show_available_contacts &> % } <%def .DisplayCircuits> <%args> $obj <%init> # Added in Operations.pm my @circuits = $obj->all_circuits; my (@field_headers, @data); # This will fail if there aren't any circuits. # Not any more. Was getting table names from first object in @circuits. @field_headers = ('Circuit', 'Connection', 'Entity'); foreach my $circuit (@circuits) { push @data, [$circuit->view_link, $circuit->connectionid->view_link, $circuit->vendor->view_link]; } $m->comp('/generic/data_table.mhtml', field_headers => \@field_headers, data => \@data); %# Name should just be display contacts now that availability is an option <%def .DisplayAvailableContacts> <%args> @contact_lists $obj # If true only available contacts are displayed $only_show_available_contacts=> 1 <%init> # Need to ``uniqueify'' the contact lists since there might be # many repeats (most devices at the same site have the same # contact lists). The below works because keys are unique. @contact_lists = values %{ { map {$_->name => $_} @contact_lists } }; my @contacts = map { $_->contacts } @contact_lists; # Will fail if there aren't any contact_sets # Not any more. Was getting table names from first object in @contacts. my @field_headers = ("Person", "Contact List", "Contact Info",); my @data; if ($only_show_available_contacts) { @contacts = Netdot::Operations::contactable_contacts(@contacts); foreach my $contact_set (@contacts) { my $contact = $contact_set->{contact}; push @data, [$contact->person->view_link, $contact->contactlist->view_link, $m->scomp('.ContactHandler', contact => $contact, notification_modes => $contact_set->{availabilities}) ]; } } else { foreach my $contact (@contacts) { push @data, [$contact->person->view_link, $contact->contactlist->view_link, $m->scomp('.ContactHandler', contact => $contact) ]; } } my $current_set_of_contacts = $only_show_available_contacts? 'Available' : 'All'; my $other_set_of_contacts = $only_show_available_contacts? 'All' : 'Available'; <&| '.MakeContainer', lhs => "$current_set_of_contacts @{[ $obj->table ]} Contacts", rhs => "id ]}&table=@{[ $obj->table ]}&only_show_available_contacts=@{[ ! $only_show_available_contacts]}\">[$other_set_of_contacts @{[ $obj->table ]} Contacts]" &> <& '/generic/data_table.mhtml', field_headers => \@field_headers, data => \@data &> <%def .ContactHandler> <%args> $contact # Optionally specify which notification modes to display. If none # are specified then all are shown. @notification_modes => () <%init> @notification_modes = qw/notify_voice notify_pager notify_email/ if (scalar @notification_modes == 0); my $person = $contact->person; # For generically processing voice, pager, and email data. my $template = { notify_voice => { title => "Phone", fields => [qw/office cell home/], }, notify_pager => { title => "Pager", fields => [qw/pager emailpager/], }, notify_email => { title => "Email", fields => [qw/email/], }, }; my (@field_headers, @data); foreach my $mode (@notification_modes) { my $values = $template->{$mode}; push @field_headers, "" . $values->{title} . ""; push @data, $contact->$mode->view_link; foreach my $type (@{ $values->{fields} }) { my %pair = $ui->form_field(object => $contact->person, column => $type); if ($pair{value} ne "") { push @field_headers, $pair{label}; push @data, $pair{value}; } } } $m->comp('/generic/attribute_table.mhtml', field_headers => \@field_headers, data => \@data); %# Could make a meta make container method as these methods are almost %# identical. Also, there is a containerhead(outside)? class which is %# more appropriate for the case where no right side is specified. <%def .MakeContainerOutside > <%args> $lhs $rhs => " " %
<% $lhs %>
<% $rhs %>
<% $m->content %>
<%def .MakeContainer > <%args> $lhs $rhs => " " %
<% $lhs %>
<% $rhs %>
<% $m->content %>
%# Debugging <%def info> <%args> $table $id <%init> sub pp { my $obj = shift; my $columns = shift || 'Essential'; eval { my $s = $obj->pp($columns); $m->print("
$s
"); }; } use Data::Dumper; my $obj = $table->retrieve($id); my $indent = " "x4; $m->print("

ID $id, Table $table

"); pp($obj, "All"); $m->print("

Has A

"); foreach my $key (keys %{ $obj->meta_info->{has_a} }) { eval{pp($obj->$key);}; $m->print("no $key in the obj
") if ($@); } $m->print("

Has Many

"); foreach my $key (keys %{ $obj->meta_info->{has_many} }) { eval { # Same as $obj->$key :) #my $accessor = $obj->meta_info->{has_many}->{$key}->{accessor}; #$m->print($key . ": " . $obj->$accessor . "
"); $m->print("Has " . $obj->$key . " $key:
"); $m->print("
");
    foreach my $foreign ($obj->$key) {
      $m->print("$indent" . $foreign->pp . "\n");
    }
    $m->print("
"); }; }

Meta Info Dump

<% Dumper($obj->meta_info()) %>