-
Notifications
You must be signed in to change notification settings - Fork 0
/
aplol-new-ap-check.pl
executable file
·270 lines (229 loc) · 6.7 KB
/
aplol-new-ap-check.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#!/usr/bin/perl
use warnings;
use strict;
use Getopt::Long;
use Text::CSV;
use File::BOM qw( :all );
use Net::OpenSSH;
use Net::Telnet::Cisco;
use Net::Ping::External qw(ping);
use Fcntl qw(:flock);
use Term::ANSIColor;
# Takes a list of APs, and checks wether or not they are online
# If they are not online, it checks a list of switches for MAC/CDP neighbor information
# Useful when you want to do a quick overview of new installs
# Expects a list of switches/routers to check as STDIN
# Also needs a CSV-file containing the APs, given by the "--ap" argument.
# rs edg-hds-h | perl $thisfile --ap $ap_csv
# --sep/--separator can be used to override default separator (comma)
# Example;
# rs edg-hds-h | grep -E "(rs[0-9]-Lo1900|sw[0-9]-Vl1900)" | perl aplol-new-ap-check.pl --ap /home/user/file.csv --sep ';'
# Load aplol
my $aplol_dir;
BEGIN {
use FindBin;
$aplol_dir = "$FindBin::Bin"; # Assume working-folder is the path where this script resides
}
use lib $aplol_dir;
use aplol;
my $aplol = aplol->new();
my %config = $aplol->get_config();
# Log
sub log_it{
$aplol->log_it("new-ap-check", "@_");
}
# Logs debug-stuff if debug has been turned on
sub debug_log{
$aplol->debug_log("new-ap-check", "@_");
}
# Logs error-stuff
sub error_log{
$aplol->error_log("new-ap-check", "@_");
}
# Ping stuff
sub pong{
my ($ip, $timeout) = @_;
my $pong = 0;
my $tries = 0;
while(($pong == 0) && ($tries < $timeout)){
if(ping(host => $ip, count => 1, timeout => 1)){
$pong = 1;
}
$tries++;
}
return $pong;
}
# We only want 1 instance of this script running
# Check if already running -- if so, abort.
unless (flock(DATA, LOCK_EX|LOCK_NB)) {
die("$0 is already running. Exiting.");
}
# Get options
my ($ap_csv, $csv_sep);
if (@ARGV > 0) {
GetOptions(
'ap=s' => \$ap_csv,
'sep|separator=s' => \$csv_sep,
)
}
# Check if required parameters is set
unless($ap_csv){
die(error_log("Required parameters not set. Exiting."));
}
$aplol->connect();
my $pi_aps = $aplol->get_active_aps();
my %sw_aps;
# Gather info from all switches/routers
while (my $switch = <STDIN>) {
chomp($switch);
next unless ($switch =~ m/^(.+?)\s\((.+?)\)\s(.+)$/);
my ($hostname, $ip, $model) = ($1, $2, $3);
if(pong($ip, 1)){
# switch is available, log in and print output
my $ssh = Net::OpenSSH->new( host => $ip,
user => $config{ssh}->{username},
password => $config{ssh}->{password},
timeout => 5,
master_opts => [
-o => "StrictHostKeyChecking=no",
-o => "UserKnownHostsFile=/dev/null",
-o => "LogLevel=quiet",
]
);
unless(defined($ssh)){
log_it("Error connecting to $ip.");
next;
}
if($ssh->error){
log_it("Error connecting to $ip: " . $ssh->error);
next;
}
# stderr-to-stdout is needed for waitfor() in Net::Telnet::Cisco to work
my ($pty, $err, $pid) = $ssh->open2pty({stderr_to_stdout => 1});
unless(defined($pty)){
log_it("Error connecting to $ip: $ssh->error");
next;
}
my $cisco = Net::Telnet::Cisco->new(
fhopen => $pty,
telnetmode => 0, # needed when using Net::OpenSSH
cmd_remove_mode => 1, # needed when using Net::OpenSSH
output_record_separator => "\r", # needed when using Net::OpenSSH
errmode => 'return',
output_log => "$config{path}->{ssh_log_folder}/cisco-output_$ip.txt",
input_log => "$config{path}->{ssh_log_folder}/cisco-input_$ip.txt",
prompt => '/[#>]$/',
);
unless (defined($cisco)){
log_it("Error connecting to $ip.");
next;
}
# remove paging
$cisco->cmd("term len 0");
# show CDP neighbors
my @output = $cisco->cmd("sh cdp nei");
my (@aps, $prevline);
foreach my $out (@output){
# remove lol
chomp($out);
# start processing
if($prevline){
# we found AP last line, but no interface
# this line should contain the interface
if($out =~ m/(Gig|Ten) ([0-9]+\/)?[0-9]+\/[0-9]+/){
# interface found, add lines together
# add to AP-array
push(@aps, $prevline . $out);
$prevline = undef;
next;
} else {
# shouldn't happen
$prevline = undef;
next;
}
} else {
if($out =~ m/^AP[a-f0-9]/){
# this is first line containing an AP
if($out =~ m/(Gig|Ten) ([0-9]+\/)?[0-9]+\/[0-9]+/){
# AP + interface on same line
# add to AP-array
push(@aps, $out);
next;
} else {
$prevline = $out;
next;
}
} else {
next;
}
}
}
# extract only AP-name
foreach my $ap (@aps){
chomp($ap);
my $port;
$ap =~ m/^(\S+?)\s*?((Gig|Ten) ([0-9]+\/)?[0-9]+\/[0-9]+).*?$/;
($ap, $port) = ($1, $2);
$port =~ s/\s//g;
# Make MAC based on AP name
(my $mac = $ap) =~ s/^AP(.+)(\.ihelse.+)?$/$1/;
$mac = $aplol->proper_mac($mac);
# Add info to hash
$sw_aps{$mac}{name} = $ap;
$sw_aps{$mac}{sw_port} = $port;
$sw_aps{$mac}{sw_name} = $hostname;
$sw_aps{$mac}{sw_ip} = $ip;
}
} else {
# switch not available
log_it("Switch ($hostname) not reachable.");
next;
}
}
# Iterate through all APs from CSV
my $csv = Text::CSV->new ( { binary => 1 } )
or die(error_log("Cannot use CSV: " . Text::CSV->error_diag ()));
# Use custom separator
if($csv_sep){
$csv->sep_char($csv_sep);
}
# We use File:BOM because Microsoft :-|
open_bom(my $CSV_FILE, $ap_csv, ':utf8')
or die(error_log("Could not open file '$ap_csv': $!"));
# Assume column names is the first row
$csv->column_names($csv->getline($CSV_FILE));
while (my $row = $csv->getline_hr($CSV_FILE)){
next unless($row->{building_name});
# $row->{building_name}
# $row->{floor_number}
# $row->{ap_number}
# $row->{ap_mac}
# $row->{circuit_id}
# $row->{ap_rotation}
# $row->{comment}
# Check if valid MAC
my $apmac = $aplol->proper_mac($row->{ap_mac});
if($aplol->valid_mac($apmac)){
if($pi_aps->{$apmac}){
# Valid MAC & online in Prime
print(colored("ap_number $row->{ap_number}: Valid MAC & online\n", 'green'));
} else {
# Valid MAC but not online in Prime
if($sw_aps{$apmac}){
# Valid MAC, not online in Prime, but found on switch
print(colored("ap_number $row->{ap_number}: Valid MAC, offline, but found on switch\n", 'cyan'));
} else {
# Valid MAC, and not online in either Prime or on switch
print(colored("ap_number $row->{ap_number}: Valid MAC, offline, not found on switch\n", 'red'));
}
}
} else {
# Invalid MAC
print(colored("ap_number $row->{ap_number}: Invalid MAC\n", 'red'));
}
}
$csv->eof or error_log("Something went wrong: " . $csv->error_diag());
close $CSV_FILE;
$aplol->disconnect();
__DATA__
Do not remove. Makes sure flock() code above works as it should.