* Created the new System.pm module with the initial read_file() method that reads files.

* Created Log->language that sets/returns the active log language.
* Created Log->variables that takes a hash reference and logs their variable: [$value] pairs.
* Created the new /usr/sbin/striker/ directory which is added to the list of search directories. We will store our tools here.
* Created the scancore-daemon and scancore-daemon.unit files which will handle all the things we used to use crontab for, minus ScanCore itself.
* Created the scancore-update-states that will eventually store some machine state information in a file that the web browser can read.
* Created the cgi-bin/home script that will be the main landing page for the Striker UI.
* Added some of the initial html files.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 8 years ago
parent e49d5285c0
commit 753d7a3ba6
  1. 28
      AN/Tools.pm
  2. 138
      AN/Tools/Log.pm
  3. 7
      AN/Tools/Storage.pm
  4. 126
      AN/Tools/System.pm
  5. 6
      AN/Tools/Template.pm
  6. 13
      AN/an-tools.xml
  7. 13
      cgi-bin/home
  8. 31
      cgi-bin/words.xml
  9. 10253
      html/jquery-3.2.1.js
  10. 1
      html/jquery-latest.js
  11. 14
      html/skins/alteeve/main.css
  12. 29
      html/skins/alteeve/main.html
  13. 53
      tools/scancore-daemon
  14. 84
      tools/scancore-update-states
  15. 12
      units/scancore-daemon.service

@ -35,6 +35,7 @@ use AN::Tools::Alert;
use AN::Tools::Get;
use AN::Tools::Log;
use AN::Tools::Storage;
use AN::Tools::System;
use AN::Tools::Template;
use AN::Tools::Words;
use AN::Tools::Validate;
@ -106,6 +107,7 @@ sub new
GET => AN::Tools::Get->new(),
LOG => AN::Tools::Log->new(),
STORAGE => AN::Tools::Storage->new(),
SYSTEM => AN::Tools::System->new(),
TEMPLATE => AN::Tools::Template->new(),
WORDS => AN::Tools::Words->new(),
VALIDATE => AN::Tools::Validate->new(),
@ -131,6 +133,7 @@ sub new
$an->Get->parent($an);
$an->Log->parent($an);
$an->Storage->parent($an);
$an->System->parent($an);
$an->Template->parent($an);
$an->Words->parent($an);
$an->Validate->parent($an);
@ -317,6 +320,18 @@ sub Storage
return ($self->{HANDLE}{STORAGE});
}
=head2 System
Access the C<System.pm> methods via 'C<< $an->System->method >>'.
=cut
sub System
{
my $self = shift;
return ($self->{HANDLE}{SYSTEM});
}
=head2 Template
Access the C<Template.pm> methods via 'C<< $an->Template->method >>'.
@ -515,14 +530,23 @@ sub _set_paths
# Executables
$an->data->{path} = {
directories => {
skins => "/var/www/html/skins",
tools => "/usr/sbin/striker",
units => "/usr/lib/systemd/system",
},
exe => {
dmidecode => "/usr/sbin/dmidecode",
gethostip => "/usr/bin/gethostip",
hostname => "/bin/hostname",
logger => "/usr/bin/logger",
},
source => {
skins => "/var/www/html/skins",
sysfs => {
network_interfaces => "/sys/class/net",
},
tools => {
'scancore-daemon' => "/usr/sbin/striker/scancore-daemon",
'scancore-update-states' => "/usr/sbin/striker/scancore-update-states",
},
words => {
'an-tools.xml' => "/usr/share/perl5/AN/an-tools.xml",

@ -12,8 +12,10 @@ my $THIS_FILE = "Log.pm";
### Methods;
# entry
# language
# level
# secure
# variables
# _adjust_log_level
=pod
@ -46,7 +48,11 @@ Methods in this module;
sub new
{
my $class = shift;
my $self = {};
my $self = {
LOG => {
LANGUAGE => "",
},
};
bless $self, $class;
@ -210,7 +216,7 @@ sub entry
my $an = $self->parent;
my $key = defined $parameter->{key} ? $parameter->{key} : "";
my $language = defined $parameter->{language} ? $parameter->{language} : $an->data->{defaults}{'log'}{language};;
my $language = defined $parameter->{language} ? $parameter->{language} : $an->Log->language;
my $level = defined $parameter->{level} ? $parameter->{level} : 2;
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $facility = defined $parameter->{facility} ? $parameter->{facility} : $an->data->{defaults}{'log'}{facility};
@ -223,12 +229,11 @@ sub entry
my $variables = defined $parameter->{variables} ? $parameter->{variables} : "";
#print $THIS_FILE." ".__LINE__."; [ Debug ] - level: [$level], defaults::log::level: [".$an->Log->{defaults}{'log'}{level}."], logging secure? [".$an->Log->secure."]\n";
### TODO: Create a method for setting/checking the active log level.
# Exit immediately if this isn't going to be logged
if ($level > $an->Log->level)
{
return(1);
}
### TODO: Create a method for setting/checking if we're recording secure messages or not.
if (($secure) && (not $an->Log->secure))
{
return(2);
@ -309,6 +314,40 @@ sub entry
return(0);
}
=head2 language
This sets or returns the log language ISO code.
Get the current log language;
my $language = $an->Log->language;
Set the log langauge to Japanese;
$an->Log->language({set => "jp"});
=cut
sub language
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $set = defined $parameter->{set} ? $parameter->{set} : "";
if ($set)
{
$self->{LOG}{LANGUAGE} = $set;
}
if (not $self->{LOG}{LANGUAGE})
{
$self->{LOG}{LANGUAGE} = $an->data->{defaults}{'log'}{language};
}
return($self->{LOG}{LANGUAGE});
}
=head2 level
This sets or returns the active log level. Valid values are 0 to 4. See the 'entry()' method docs for more details.
@ -394,6 +433,97 @@ sub secure
return($an->data->{defaults}{'log'}{secure});
}
=head2 variables
This is a special method used in testing and debugging for logging a certain number of variables. It takes a hash reference via the 'C<< variables >>' parameter and creates a raw log entry showing the variables as 'C<< variable: [value] >>' pairs.
parameters;
NOTE: It takes all of the same parameters as 'C<< Log->entry >>', minus 'C<< raw >>', 'C<< key >>' and 'C<< variables >>':
head3 list (required)
This is a hash reference containing the variables to record.
=cut
sub variables
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $language = defined $parameter->{language} ? $parameter->{language} : $an->data->{defaults}{'log'}{language};
my $level = defined $parameter->{level} ? $parameter->{level} : 2;
my $line = defined $parameter->{line} ? $parameter->{line} : "";
my $list = defined $parameter->{list} ? $parameter->{list} : {};
my $facility = defined $parameter->{facility} ? $parameter->{facility} : $an->data->{defaults}{'log'}{facility};
my $priority = defined $parameter->{priority} ? $parameter->{priority} : "";
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $server = defined $parameter->{server} ? $parameter->{server} : $an->data->{defaults}{'log'}{server};
my $source = defined $parameter->{source} ? $parameter->{source} : "";
my $tag = defined $parameter->{tag} ? $parameter->{tag} : $an->data->{defaults}{'log'}{tag};
# Exit immediately if this isn't going to be logged
if ($level > $an->Log->level)
{
return(1);
}
if (($secure) && (not $an->Log->secure))
{
return(2);
}
# If I don't have a list, or the list is empty, return.
my $entry = 1;
my $entries = keys %{$list};
if ($entries)
{
my $raw = "";
if ($entries < 5)
{
# Put all the entries on one line.
foreach my $key (sort {$a cmp $b} keys %{$list})
{
$raw .= "$key: [".$list->{$key}."], ";
}
$raw =~ s/, $//;
}
else
{
# Put all the entries on their own line.
$raw .= $an->Words->string({key => "log_0019"})."\n";
foreach my $key (sort {$a cmp $b} keys %{$list})
{
if ($entry ne $entries)
{
$raw .= "|- $key: [".$list->{$key}."]\n";
}
else
{
$raw .= "\\- $key: [".$list->{$key}."]\n";
}
$entry++;
}
}
# Do the raw log entry.
$an->Log->entry({
language => $language,
level => $level,
line => $line,
facility => $facility,
priority => $priority,
raw => $raw,
secure => $secure,
server => $server,
source => $source,
tag => $tag,
})
}
return(0);
}
# =head3
#

@ -296,11 +296,11 @@ Parameters;
This accepts either an array reference of directories to search, or a comma-separated string of directories to search (which will be converted to an array). When passed, this sets the internal list of directories to search.
By default, it is set to all directories in C<< \@INC >> and the C<< $ENV{'PATH'} >> variables, minus directories that don't actually exist. The returned array is sorted alphabetically.
By default, it is set to all directories in C<< @INC >>, 'C<< path::directories::tools >> (our tools) and the C<< $ENV{'PATH'} >> variables, minus directories that don't actually exist. The returned array is sorted alphabetically.
=head3 initialize (optional)
If this is set, the list of directories to search will be set to '@INC + $ENV{'PATH'}'.
If this is set, the list of directories to search will be set to 'C<< @INC >>' + 'C<< $ENV{'PATH'} >>' + 'C<< path::directories::tools >>'.
NOTE: You don't need to call this manually unless you want to reset the list. Invoking AN::Tools->new() causes this to be called automatically.
@ -336,6 +336,9 @@ sub search_directories
{
push @new_array, $directory;
}
# Add the tools directory
push @new_array, $an->data->{path}{directories}{tools};
$array = \@new_array;
}

@ -0,0 +1,126 @@
package AN::Tools::System;
#
# This module contains methods used to handle common system tasks.
#
use strict;
use warnings;
use Data::Dumper;
our $VERSION = "3.0.0";
my $THIS_FILE = "System.pm";
### Methods;
# read_file
=pod
=encoding utf8
=head1 NAME
AN::Tools::System
Provides all methods related to storage on a system.
=head1 SYNOPSIS
use AN::Tools;
# Get a common object handle on all AN::Tools modules.
my $an = AN::Tools->new();
# Access to methods using '$an->System->X'.
#
# Example using 'read_file()';
my $data = $an->System->read_file({file => "/tmp/foo"});
=head1 METHODS
Methods in this module;
=cut
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return ($self);
}
# Get a handle on the AN::Tools object. I know that technically that is a sibling module, but it makes more
# sense in this case to think of it as a parent.
sub parent
{
my $self = shift;
my $parent = shift;
$self->{HANDLE}{TOOLS} = $parent if $parent;
return ($self->{HANDLE}{TOOLS});
}
#############################################################################################################
# Public methods #
#############################################################################################################
=head2 read_file
This reads in a file and returns the contents of the file as a single string variable.
$an->System->read_file({file => "/tmp/foo"});
If it fails to find the file, or the file is not readable, 'C<< undef >>' is returned.
Parameters;
=head3 file (required)
This is the name of the file to read.
=cut
sub read_file
{
my $self = shift;
my $parameter = shift;
my $an = $self->parent;
my $body = "";
my $file = defined $parameter->{file} ? $parameter->{file} : "";
if (not $file)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020"});
return(undef);
}
elsif (not -e $file)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0021", variables => { file => $file }});
return(undef);
}
elsif (not -r $file)
{
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0022", variables => { file => $file }});
return(undef);
}
my $shell_call = $file;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0012", variables => { shell_call => $shell_call }});
open (my $file_handle, "<", $shell_call) or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
my $line = $_;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0023", variables => { line => $line }});
$body .= $line."\n";
}
close $file_handle;
$body =~ s/\n$//s;
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { body => $body }});
return($body);
}

@ -115,7 +115,7 @@ sub get
# Make sure the file exists.
if ($skin)
{
$source = $an->data->{path}{source}{skins}."/".$skin."/".$file;
$source = $an->data->{path}{directories}{skins}."/".$skin."/".$file;
print $THIS_FILE." ".__LINE__."; [ Debug ] - source: [$source]\n";
}
else
@ -204,7 +204,7 @@ sub skin
if ($set)
{
my $skin_directory = $an->data->{path}{source}{skins}."/".$set;
my $skin_directory = $an->data->{path}{directories}{skins}."/".$set;
if (-d $skin_directory)
{
$self->{SKIN}{HTML} = $skin_directory
@ -217,7 +217,7 @@ sub skin
if (not $self->{SKIN}{HTML})
{
$self->{SKIN}{HTML} = $an->data->{path}{source}{skins}."/".$an->data->{defaults}{template}{html};
$self->{SKIN}{HTML} = $an->data->{path}{directories}{skins}."/".$an->data->{defaults}{template}{html};
}
return($self->{SKIN}{HTML});

@ -30,6 +30,19 @@ It also has replacement variables: [#!variable!first!#] and [#!variable!second!#
<key name="log_0008">This is a test error log entry.</key>
<key name="log_0009">This is a test alert log entry.</key>
<key name="log_0010">This is a test emergency log entry.</key>
<key name="log_0011">About to run the shell command: [#!variable!shell_call!#]</key>
<key name="log_0012">About to read the file: [#!variable!shell_call!#]</key>
<key name="log_0013">About to write the file: [#!variable!shell_call!#]</key>
<key name="log_0014">There was a problem running the shell command: [#!variable!shell_call!#]. The error was: [#!variable!error!#].</key>
<key name="log_0015">There was a problem reading the file: [#!variable!shell_call!#]. The error was: [#!variable!error!#].</key>
<key name="log_0016">There was a problem writing the file: [#!variable!shell_call!#]. The error was: [#!variable!error!#].</key>
<key name="log_0017">Output: [#!variable!line!#].</key>
<key name="log_0018">About to open the directory: [#!variable!directory!#]</key>
<key name="log_0019">Variables:</key>
<key name="log_0020"><![CDATA[The module System->read_file() was called without a 'file' parameter, or the parameter was empty.]]></key>
<key name="log_0021"><![CDATA[The module System->read_file() was asked to read the file: [#!variable!file!#], but that file does not exist.]]></key>
<key name="log_0022"><![CDATA[The module System->read_file() was asked to read the file: [#!variable!file!#] which exists but can't be read.]]></key>
<key name="log_0023">Reading: [#!variable!line!#].</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>

@ -4,16 +4,23 @@ use strict;
use warnings;
use AN::Tools;
my $an = AN::Tools->new();
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $THIS_FILE = "home";
my $an = AN::Tools->new();
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
print "Content-type: text/html; charset=utf-8\n\n";
print "hello.\n";
my $header = $an->Template->get({file => "main.html", name => "header", variables => {
language =>
}});
my $template = $an->Template->get({file => "main.html", name => "master"});
print $template;

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Company: Alteeve's Niche, Inc.
License: GPL v2+
Author: Madison Kelly <mkelly@alteeve.ca>
This is the AN::Tools master 'words' file.
-->
<words>
<meta version="3.0.0" languages="en_CA,jp"/>
<!-- Canadian English -->
<language name="en_CA" long_name="Canadian English" description="Striker/ScanCore language file.">
<key name="brand_0001">Alteeve</key>
<key name="brand_0002">Anvil!</key>
<key name="brand_0003">Striker</key>
<key name="brand_0004">ScanCore</key>
</language>
<!-- 日本語 -->
<language name="jp" long_name="日本語" description="Striker/ScanCore language file.">
<key name="brand_0001">アルティーブ</key>
<key name="brand_0002">Anvil!</key>
<key name="brand_0003">ストライカ</key>
<key name="brand_0004">スカンコア</key>
</language>
</words>

10253
html/jquery-3.2.1.js vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
jquery-3.2.1.js

@ -0,0 +1,14 @@
/*
Colours;
- Darkest Gray: #343434
- Logo Gray: #9ba0a5
- Lightest Gray: #f7f7f7
- Font Dark Gray: #444444
- Font Light Gray: #f2f2f2
- Alteeve Red: #d02724
*/
body {
background-image: url("skins/alteeve/images/Texture.jpg");
background-repeat: repeat-y;
}

@ -1,38 +1,49 @@
<!-- start master -->
#!replace!header!#
#!variable!header!#
<table>
<tr>
<td>
#!replace!left_top_bar!#
#!variable!left_top_bar!#
</td>
<td>
#!replace!center_top_bar!#
#!variable!center_top_bar!#
</td>
<td>
#!replace!right_top_bar!#
#!variable!right_top_bar!#
</td>
</tr>
<tr>
<td colspan="3">
#!replace!center_body!#
#!variable!center_body!#
</td>
</tr>
<tr>
<td>
#!replace!left_bottom_bar!#
#!variable!left_bottom_bar!#
</td>
<td>
#!replace!center_bottom_bar!#
#!variable!center_bottom_bar!#
</td>
<td>
#!replace!right_bottom_bar!#
#!variable!right_bottom_bar!#
</td>
</tr>
</table>
#!replace!footer!#
#!variable!footer!#
<!-- end master -->
<!-- start header -->
<!DOCTYPE html>
<html lang="#!variable!language!#">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>#!string!brand_0001!# - #!string!brand_0003!#</title>
<link rel="stylesheet" href="#!variable!skin_directory!#/main.css" media="screen" />
<script type="text/javascript" src="#!variable!html_directory!#/jquery-latest.js"></script>
</head>
<!-- end header -->
<!-- start button_bar -->
<!--end button_bar -->

@ -0,0 +1,53 @@
#!/usr/bin/perl
#
# This is the master daemon that manages all periodically run processes on Striker dashboards and Anvil!
# nodes.
#
use strict;
use warnings;
use AN::Tools;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $an = AN::Tools->new();
$an->Log->level(2);
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
while(1)
{
update_state_file($an);
sleep 10;
}
exit(0);
#############################################################################################################
# Functions #
#############################################################################################################
# This calls 'scancore-update-states' which will scan the local machine's state (hardware and software) and
# record write it out to an HTML file
sub update_state_file
{
my ($an) = @_;
my $shell_call = $an->data->{path}{tools}{'scancore-update-states'};
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0017", variables => { output => $_ }});
}
close $file_handle;
return(0);
}

@ -0,0 +1,84 @@
#!/usr/bin/perl
#
# This is the master daemon that manages all periodically run processes on Striker dashboards and Anvil!
# nodes.
#
use strict;
use warnings;
use AN::Tools;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $an = AN::Tools->new();
$an->Log->level(2);
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
# my $shell_call = "/sys/class/net/ens11/address";
# $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0012", variables => { shell_call => $shell_call }});
# open (my $file_handle, "<$shell_call") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $shell_call, error => $! }});;
# while(<$file_handle>)
# {
# chomp;
# my $line = $_;
# $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0023", variables => { output => $line }});
# print "line: [$line]\n";
# }
# close
report_network($an);
exit(0);
#############################################################################################################
# Functions #
#############################################################################################################
# This reports the current network interface states, tracked by the MAC address.
sub report_network
{
my ($an) = @_;
my $directory = $an->data->{path}{sysfs}{network_interfaces};
local(*DIRECTORY);
$an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0018", variables => { directory => $directory }});
opendir(DIRECTORY, $directory);
while(my $file = readdir(DIRECTORY))
{
next if $file eq ".";
next if $file eq "..";
next if $file eq "lo";
my $full_path = "$directory/$file";
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path }});
if (-d $full_path)
{
# Pull out the data I want.
my $interface = $file;
my $mac_address = $an->System->read_file({file => $full_path."/address"});
my $link_state = $an->System->read_file({file => $full_path."/carrier"});
my $mtu = $an->System->read_file({file => $full_path."/mtu"});
my $duplex = $an->System->read_file({file => $full_path."/duplex"}); # full or half?
my $operational = $an->System->read_file({file => $full_path."/operstate"}); # up or down
my $speed = $an->System->read_file({file => $full_path."/speed"}); # Mbps (ie: 1000 = Gbps), gives a very high number for unplugged link
$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
interface => $interface,
mac_address => $mac_address,
link_state => $link_state,
mtu => $mtu,
duplex => $duplex,
operational => $operational,
speed => $speed,
}});
}
}
closedir(DIRECTORY);
return(0);
}

@ -0,0 +1,12 @@
[Unit]
Description=ScanCore daemon used to handle several jobs as part of the Anvil! IA suite
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/striker-daemon
ExecStop=/bin/kill -WINCH ${MAINPID}
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save