1#!/usr/bin/env perl
2
3use strict;
4use warnings;
5
6our $home;
7
8BEGIN {
9  use FindBin;
10  FindBin::again();
11
12  $home = ($ENV{NETDISCO_HOME} || $ENV{HOME});
13
14  # try to find a localenv if one isn't already in place.
15  if (!exists $ENV{PERL_LOCAL_LIB_ROOT}) {
16      use File::Spec;
17      my $localenv = File::Spec->catfile($FindBin::RealBin, 'localenv');
18      exec($localenv, $0, @ARGV) if -f $localenv;
19      $localenv = File::Spec->catfile($home, 'perl5', 'bin', 'localenv');
20      exec($localenv, $0, @ARGV) if -f $localenv;
21
22      die "Sorry, can't find libs required for App::Netdisco.\n"
23        if !exists $ENV{PERLBREW_PERL};
24  }
25}
26
27BEGIN {
28  use Path::Class;
29
30  # stuff useful locations into @INC and $PATH
31  unshift @INC,
32    dir($FindBin::RealBin)->parent->subdir('lib')->stringify,
33    dir($FindBin::RealBin, 'lib')->stringify;
34
35  use Config;
36  $ENV{PATH} = $FindBin::RealBin . $Config{path_sep} . $ENV{PATH};
37}
38
39use App::Netdisco;
40use Dancer ':script';
41use Dancer::Plugin::DBIC 'schema';
42
43use App::Netdisco::Util::Permission ':all';
44
45# silent exit unless explicitly requested
46exit(0) unless setting('use_legacy_rancidexport');
47
48my $settings = setting( 'rancid' );
49my $domain_suffix = setting( 'domain_suffix' );
50my $delimiter = $settings->{ 'delimiter' } || ':';
51my $down_age = $settings->{ 'down_age' } || '1 day';
52my $rancidhome = $settings->{ 'rancid_home' } || '/var/lib/rancid';
53my $config_vendormap = $settings->{ 'vendormap' } || {};
54
55my $by_ip = {};
56foreach my $g (@{$settings->{ 'by_ip' }}) {
57  $by_ip->{$g} = 1;
58}
59
60my $by_hostname = {};
61foreach my $g (@{$settings->{ 'by_hostname' }}) {
62  $by_hostname->{$g} = 1;
63}
64
65my @devices = schema('netdisco')->resultset('Device')->search({},
66  {
67    '+columns' => {
68      old => \"age(now(), last_discover) > interval '$down_age'"
69    }
70  })->all;
71
72my $groups = $settings->{ 'groups' };
73my $list = {};
74
75foreach my $d (@devices) {
76  my $old = $d->get_column( 'old' );
77  my $devgroup = 'other';
78  foreach my $g (keys %$groups) {
79    if (check_acl_only( $d, $groups->{$g} )) {
80      $devgroup = $g;
81      last;
82    }
83  }
84  push(@{$list->{$devgroup}}, $d);
85}
86
87my %VENDORMAP = (
88# If netdisco vendor name and rancid vendor name
89# do not map 1:1, map it here.
90# eg:
91# 'dell:2024' => 'dellnseries',
92# 'dell:3024' => 'dellnseries'
93);
94
95foreach my $group (keys %$list) {
96  open(ROUTER, ">${rancidhome}/${group}/router.db") || die "${rancidhome}/${group}/router.db: $!\n";
97  foreach my $dev (sort {$a->ip cmp $b->ip} @{$list->{$group}}) {
98    my $vendor = $dev->vendor;
99    my $vendormodel = join(':',$dev->vendor,$dev->model);
100    my $name;
101    if ( $VENDORMAP{$vendor} or $VENDORMAP{$vendormodel} ) {
102      $vendor = $VENDORMAP{$vendormodel} || $VENDORMAP{$vendor};
103    }
104    if ( $config_vendormap->{$vendor} or $config_vendormap->{$vendormodel} ) {
105      $vendor = $config_vendormap->{$vendormodel} || $config_vendormap->{$vendor};
106    }
107    if ($by_ip->{$group}) {
108      $name = $dev->ip;
109    } else {
110      $name = ($dev->dns || $dev->name);
111    }
112    if ($by_hostname->{$group}) {
113      $name =~ s/$domain_suffix//;
114    }
115    printf ROUTER "%s$delimiter%s$delimiter%s\n", $name, $vendor,
116      $dev->get_column( 'old' ) ? "down" : "up";
117  }
118  close(ROUTER);
119}
120
121=head1 NAME
122
123netdisco-rancid-export - DEPRECATED!
124
125=head1 DEPRECATED!
126
127Note! This script is now deprecated and no longer maintained. The replacement
128is built in to Netdisco core, so can be scheduled in the backend, and also has
129more powerful configuration. See
130L<App::Netdisco::Worker::Plugin::MakeRancidConf>.
131
132=head1 CONFIGURATION
133
134This script requires some configuration to be added to your Netdisco
135"C<~/environments/deployment.yml>" file, for example:
136
137 rancid:
138   rancid_home:  /var/lib/rancid
139   down_age:     '1 day'
140   delimiter:    ':'
141   by_ip:        [ other ]
142   by_hostname:  [ other2 ]
143   groups:
144     switch:     [ 'name:.*[Ss][Ww].*' ]
145     rtr:        [ 'name:[rR]tr.*' ]
146     ap:         [ 'name:[aA][pP].*' ]
147   vendormap:
148     "dell":      force10
149     "dell:2024": dellnseries
150
151Note that C<netdisco-rancid-export> is not part of the automatic scheduler
152built in to Netdisco. You should run this script via C<cron> just after your
153periodic C<discoverall>.
154
155=head2 C<rancid_home>
156
157The location to write RANCID Group configuration files into. A subdirectory
158for each Group will be created.
159
160Default: "C</var/lib/rancid>".
161
162=head2 C<down_age>
163
164This should be the same or greater than the interval between regular discover
165jobs on your network. Devices which have not been discovered within this time
166will be marked as "C<down>" to RANCID.
167
168Default: "C<1 day>".
169
170=head2 C<delimiter>
171
172RANCID version 3 uses a semicolon as delimiter. Set this to the delimiter
173character if needed to be different from the default.
174
175Default: "C<:>".
176
177=head2 C<vendormap>
178
179If the device Vendor in Netdisco is not the same as the RANCID vendor script,
180configure a mapping here. The left hand side (key) should be the Netdisco
181vendor, the right hand side (value) should be the RANCID vendor script name.
182You can also set the Netdisco vendor to be "C<vendor:model>" for fine-grained
183control. See the synopsis for an example.
184
185=head2 C<groups>
186
187This dictionary maps RANCID Group names with configuration which will match
188devices in the Netdisco database. The configuration is the same as any of
189Netdisco's "C<*_only>" settings, and accepts IP, prefix, device property.
190
191=head2 C<by_ip>
192
193List of RANCID Groups which will have Device IPs written to the RANCID
194configuration file, instead of DNS or SNMP host names.
195
196=head2 C<by_hostname>
197
198List of RANCID Groups which will have Device Hostname written to the RANCID
199configuration file, instead of FQDN. This is done simply by stripping the
200C<domain_suffix> configuration item from the FQDN.
201
202=head1 SEE ALSO
203
204=over 4
205
206=item *
207
208L<App::Netdisco>
209
210=back
211
212=cut
213