source: trunk/backuppc-silzigan/backuppc-silzigan

Last change on this file was 415, checked in by g7moreau, 5 years ago
  • Small error on command
  • Property svn:executable set to *
File size: 13.8 KB
Line 
1#!/usr/bin/perl
2#
3# 2011/06/21 Gabriel Moreau <Gabriel.Moreau@univ-grenoble-alpes.fr>
4#
5# apt-get install iputils-ping rsync nmap perl-base libyaml-perl libio-all-perl libfile-finder-perl libnet-ldap-perl
6
7use strict;
8use warnings;
9
10use Getopt::Long qw(GetOptions);
11use YAML;
12use IO::All;
13use File::Basename;
14use File::Finder;
15use Net::LDAP;
16use List::Util qw(shuffle);
17use Logger::Syslog;
18
19my $command = shift @ARGV || 'help';
20
21my %cmd_db = (
22   'help'         => \&cmd_help,
23   'version'      => \&cmd_version,
24   'generate'     => \&cmd_generate,
25   'update'       => \&cmd_update,
26   'init'         => \&cmd_init_db,
27   'exclude-list' => \&cmd_exclude_list,
28   );
29
30#-------------------------------------------------------------------------------
31
32my ($LDAP_H, $LDAP_BASE);
33my $LIMIT_TIMESTAMP = time() - (8 * 24 * 3600); # 8 days
34
35if (defined $cmd_db{$command}) {
36   $cmd_db{$command}->(@ARGV);
37   }
38else {
39   print {*STDERR} "backuppc-silzigan: command $command not found\n\n";
40   $cmd_db{help}->();
41   exit 1;
42   }
43
44exit;
45
46#-------------------------------------------------------------------------------
47
48sub open_ldap {
49   # AuthLDAPUrl          "ldap://ldapserver.mylab.fr/ou=Users,dc=mylab,dc=fr?uid?sub"
50   # AuthLDAPBindDN       "cn=ldapconnect,ou=System,dc=mylab,dc=fr"
51   # AuthLDAPBindPassword "Rhalala128"
52
53   my ($masterLDAP, $masterDN, $masterPw);
54
55   for my $config_line (io('/etc/apache2/conf.d/backuppc.conf')->chomp->slurp) {
56      ($masterLDAP, $LDAP_BASE) = ($1, $2) if $config_line =~ m{ ^\s* AuthLDAPUrl [^/]+ // ([^/]+) / (ou=[^?]+) }xms;
57      $masterDN = $1 if $config_line =~ m{ ^\s* AuthLDAPBindDN \s+ " ([^"]+) " }xms;
58      $masterPw = $1 if $config_line =~ m{ ^\s* AuthLDAPBindPassword \s+ " ([^"]+) " }xms;
59      }
60
61   $LDAP_H = Net::LDAP->new( "$masterLDAP" ) or die "$@";
62   my $mesg = $LDAP_H->bind("$masterDN", password => "$masterPw");
63
64   return;
65   }
66
67#-------------------------------------------------------------------------------
68
69sub close_ldap {
70   $LDAP_H->unbind();
71   return;
72   }
73
74#-------------------------------------------------------------------------------
75
76sub cmd_update {
77   local @ARGV = @_;
78
79   my $search_config_file = File::Finder->type('f')->name('*.yaml');
80   for my $config_file (File::Finder->eval($search_config_file)->in('/etc/backuppc')) {
81      cmd_generate("$config_file", @ARGV);
82      }
83
84   add_oldcomputer();
85   
86   update_hosts();
87   }
88
89#-------------------------------------------------------------------------------
90
91sub add_oldcomputer {
92   my $admin = 'sys-admin';
93
94   my %hostdb = ();
95   for my $hostline (io('/etc/backuppc/hosts.order')->chomp->slurp) {
96      next if not $hostline =~ m/^\w/;
97      my ($host) = split /\s+/, $hostline, 2;
98      $hostdb{$host}++;
99      }
100
101   # Reset hosts database
102   print '' > io('/etc/backuppc/hosts.oldcomputer');
103
104   for my $pc (io->dir('/var/lib/backuppc/pc')->all_dirs) {
105      my $pcname = $pc->filename;
106      my $pcpath = $pc->pathname;
107      next if not -e "$pcpath/backups";
108      next if not -e "/etc/backuppc/$pcname.pl";
109
110      my $user = 'root';
111      my $host = $pcname;
112      ($host, $user) = split /_/, $pcname, 2 if $pcname =~ m/_/;
113      next if exists $hostdb{$pcname};
114
115      my $full_period;
116      for (io("/etc/backuppc/$pcname.pl")->chomp->slurp) {
117         m/FullPeriod/ or next;
118         $full_period++;
119         last;
120         }
121
122      io("/etc/backuppc/$pcname.pl")->append('$Conf{FullPeriod} = "-2";') if not $full_period;
123     
124      # Add to hosts database
125      print "$pcname 0 sleeping $admin,$user\n" >> io('/etc/backuppc/hosts.oldcomputer');
126      }
127   }
128
129#-------------------------------------------------------------------------------
130
131sub shell_command {
132   my $cmd = shift;
133
134   require FileHandle;
135   my $fh     = new FileHandle;
136   my @result = ();
137   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
138   @result = <$fh>;
139   close $fh;
140   chomp @result;
141   return @result;
142   }
143
144#-------------------------------------------------------------------------------
145
146sub cmd_generate {
147   local @ARGV = @_;
148   my $config_file = shift @ARGV;
149
150   my ($verbose);
151
152   GetOptions(
153      'verbose'   => \$verbose,
154      );
155
156   my $CONFIG;
157   
158   eval {
159      $CONFIG = YAML::LoadFile($config_file);
160      };
161   if ($@) {
162      warning "Error: bad YAML in file $config_file";
163      return;
164      }
165
166   $CONFIG->{default}{namespace} ||= basename($config_file, '.yaml');
167   $CONFIG->{default}{path}      ||= "/etc/backuppc/auto/$CONFIG->{default}{namespace}";
168   $CONFIG->{default}{hosts}     ||= "$CONFIG->{default}{path}/hosts";
169   $CONFIG->{default}{exclude}   ||= "/usr/lib/kont/etc/backuppc/exclude.txt";
170
171   if (not -d "/etc/backuppc/auto/$CONFIG->{default}{namespace}") {
172      io("/etc/backuppc/auto/$CONFIG->{default}{namespace}")->mkpath({mode => 0755});
173      }
174
175   print '' > io($CONFIG->{default}{hosts});
176
177   open_ldap();
178   LOOP_ON_COMPUTER:
179   for my $computer ( keys %{$CONFIG->{computers}}) {
180      my $login = $CONFIG->{computers}{$computer}{login} || 'root';
181
182      LOOP_ON_USER:
183      for my $user (  keys %{$CONFIG->{computers}{$computer}{users}}) {
184         my $pathshare = $CONFIG->{computers}{$computer}{share}
185                   || $CONFIG->{default}{share}
186                   || "/home/users";
187         my $share  = $CONFIG->{computers}{$computer}{users}{$user}{share}
188                   || "$pathshare/$user";
189         my $status = $CONFIG->{computers}{$computer}{users}{$user}{status}
190                   || $CONFIG->{computers}{$computer}{status}
191                   || $CONFIG->{default}{status}
192                   || 'auto';
193         my $admin  = $CONFIG->{computers}{$computer}{users}{$user}{admin}
194                   || $CONFIG->{computers}{$computer}{admin}
195                   || $CONFIG->{default}{admin}
196                   || 'root';
197
198         my $exclude = $CONFIG->{computers}{$computer}{users}{$user}{exclude}
199                   || $CONFIG->{computers}{$computer}{exclude}
200                   || '';
201
202         my @exclude_list = ();
203         if (ref($exclude) eq "ARRAY") {
204            push @exclude_list, @{$exclude};
205            }
206         else {
207            push @exclude_list, $exclude if not $exclude =~ m/^$/;
208            }
209         push @exclude_list, io($CONFIG->{default}{exclude})->chomp->slurp;
210         my $exclude_string = join ",\n", map { "    '$_'" } @exclude_list;
211
212         if ($status eq "auto") {
213            $status = "disable";
214            my $ldb = $LDAP_H->search(
215               base     => "$LDAP_BASE",
216               filter   => "(uid=$user)",
217               attrs    => ['shadowExpire', 'sambaKickoffTime'],
218               );
219            if (not $ldb->code ) {
220
221               LDAP_RESULT:
222               foreach my $entry ($ldb->entries) {
223
224                  my $user_expire_timestamp = $entry->get_value('sambaKickoffTime') || 0;
225                  my $user_shadow_expire = $entry->get_value('shadowExpire') || 0;
226
227                  if ($user_shadow_expire == 0) {
228                     $status = "enable";
229                     last LDAP_RESULT;
230                     }
231                  elsif (
232                     ( $user_expire_timestamp ne "" )
233                        and ( $user_expire_timestamp >  $LIMIT_TIMESTAMP )
234                     ) {
235                     $status = "enable";
236                     last LDAP_RESULT;
237                     }
238
239                  }
240               }
241            }
242
243         print STDERR "Info: write_config($user, $computer, $share, $login, $admin, $status)\n" if $verbose;
244         write_config($user, $computer, $share, $login, $admin, $status, $CONFIG, $exclude_string);
245
246         }
247
248      if (exists $CONFIG->{computers}{$computer}{subfolder}) {
249         my $home_path = $CONFIG->{computers}{$computer}{subfolder};
250         print STDERR "\nInfo: ssh on $login\@$computer\n" if $verbose;
251         my @ls = shell_command("/bin/ping -W 2 -c 1 '$computer' > /dev/null 2>&1 && {
252            /usr/bin/nmap  -p 22 -PN '$computer' | grep -q '^22/tcp[[:space:]]*open' && {
253               /usr/bin/rsync --dry-run '$login\@$computer:$home_path' /tmp/backuppc-test/ || echo Error for '$login\@$computer' | logger -t backuppc-silzigan;
254               };
255            };");
256
257         $home_path =~ s{/[^/]*$}{};
258         LINE:
259         for my $line (@ls) {
260            chomp $line;
261            next LINE if not $line =~ m/skipping\sdirectory/;
262            next LINE if $line =~ m/lost\+found/;
263            next LINE if $line =~ m/administrator/;
264            my ($user) = reverse split /\s+/, $line;
265            $user =~ s{/$}{};
266            $user =~ s{.*/}{};
267            next LINE if not $user =~ m/^\w/;
268
269            my $share = "$home_path/$user";
270
271            my @exclude_list = ();
272            push @exclude_list, io($CONFIG->{default}{exclude})->chomp->slurp;
273            my $exclude_string = join ",\n", map { "    '$_'" } @exclude_list;
274
275            my $admin = $CONFIG->{computers}{$computer}{admin}
276                     || $CONFIG->{default}{admin}
277                     || 'root';
278
279            my $status = "disable";
280            my $ldb = $LDAP_H->search(
281               base     => "$LDAP_BASE",
282               filter   => "(uid=$user)",
283               attrs    => ['shadowExpire', 'sambaKickoffTime'],
284               );
285            if (not $ldb->code ) {
286
287               LDAP_RESULT:
288               foreach my $entry ($ldb->entries) {
289
290                  my $user_expire_timestamp = $entry->get_value('sambaKickoffTime') || 0;
291                  my $user_shadow_expire = $entry->get_value('shadowExpire') || 0;
292
293                  if ($user_shadow_expire == 0) {
294                     $status = "enable";
295                     last LDAP_RESULT;
296                     }
297                  elsif (
298                     ( $user_expire_timestamp ne "" )
299                        and ( $user_expire_timestamp >  $LIMIT_TIMESTAMP )
300                     ) {
301                     $status = "enable";
302                     last LDAP_RESULT;
303                     }
304
305                  }
306               }
307
308            print STDERR "Info: write_config($user, $computer, $share, $login, $admin, $status)\n" if $verbose;
309            write_config($user, $computer, $share, $login, $admin, $status, $CONFIG, $exclude_string);
310            }
311         }
312      }
313   close_ldap();
314   }
315
316#-------------------------------------------------------------------------------
317
318sub cmd_exclude_list {
319   print io('/usr/lib/kont/etc/backuppc/exclude.txt')->all;
320   }
321
322#-------------------------------------------------------------------------------
323
324sub write_config {
325   my ($user, $computer, $share, $login, $admin, $status, $CONFIG, $exclude) = @_;
326   my ($c) = split /\./, $computer;
327   my $backup_name = $c . '_'. $user;
328
329   return if $status eq 'disable' and not -e "/var/lib/backuppc/pc/$backup_name/backups";
330
331   print "$backup_name 0 $login $admin,$user\n" >> io($CONFIG->{default}{hosts});
332
333   my $share_string = "'$share'";
334   if (ref($share) eq "ARRAY") {
335      $share_string = join ', ', map("'$_'", @{$share});
336      }
337
338#   my $exclude = join ",\n", map { "    '$_'" } io('/usr/lib/kont/etc/backuppc/exclude.txt')->chomp->slurp;
339
340   print <<END > io("$CONFIG->{default}{path}/$backup_name.pl");
341\$Conf{XferMethod} = 'rsync';
342\$Conf{ClientNameAlias} = '$computer';
343\$Conf{RsyncShareName} = [ $share_string ];
344\$Conf{RsyncClientCmd} = '\$sshPath -q -x -l $login \$host \$rsyncPath \$argList+';
345\$Conf{RsyncClientRestoreCmd} = '\$sshPath -q -x -l $login \$host \$rsyncPath \$argList+';
346\$Conf{BackupFilesExclude} = {
347  '$share' => [
348$exclude
349  ]
350};
351END
352
353   print '$Conf{FullPeriod} = "-2";' >> io("$CONFIG->{default}{path}/$backup_name.pl") if $status eq 'disable';
354   symlink "$CONFIG->{default}{path}/$backup_name.pl", "/etc/backuppc/pc/$backup_name.pl";
355   }
356
357#-------------------------------------------------------------------------------
358
359sub update_hosts {
360   io->catfile('/etc/backuppc/hosts.main') > io('/etc/backuppc/hosts.order');
361   my @hosts = io('/etc/backuppc/hosts.main')->chomp->slurp;
362
363   push @hosts, io('/etc/backuppc/hosts.oldcomputer')->chomp->slurp;
364
365   my $search_host_file = File::Finder->type('f')->name('hosts');
366   for my $host_file (File::Finder->eval($search_host_file)->in('/etc/backuppc/auto')) {
367      print "#\n# $host_file\n#\n" >> io('/etc/backuppc/hosts.order');
368      io->catfile("$host_file") >> io('/etc/backuppc/hosts.order');
369      push @hosts, io("$host_file")->chomp->slurp;
370      }
371   my ($first, @host) = grep(/./, grep(!/^#/, @hosts));
372   print "$first\n" > io('/etc/backuppc/hosts');
373   print "$_\n" >> io('/etc/backuppc/hosts') for shuffle(@host);
374   }
375
376#-------------------------------------------------------------------------------
377
378sub cmd_init_db {
379   my $cfg = {
380      computers => {
381         'machine36.hmg.priv' => {
382            login => 'root',
383            share => '/home/users/%u',
384            status => 'auto',
385            users => {
386               'dupond' => {
387                  share => [ '/home/users/dupond', '/var/www/dupond' ],
388                  status => 'enable',
389                  },
390               'durand' => {
391                  share => '/home/users/durand',
392                  },
393               },
394            },
395         },
396      };
397
398   YAML::DumpFile("/tmp/template-backuppc.yaml", $cfg);
399   }
400
401#-------------------------------------------------------------------------------
402
403sub cmd_help {
404   print <<'END';
405backuppc-silzigan - cut into small pieces a computer configuration for backuppc
406
407 backuppc-silzigan init
408 backuppc-silzigan generate config_file.yaml [--verbose]
409 backuppc-silzigan update [--verbose]
410 backuppc-silzigan help
411 backuppc-silzigan version
412 backuppc-silzigan exclude-list
413END
414   return;
415   }
416
417#-------------------------------------------------------------------------------
418
419sub cmd_version {
420   print <<'END';
421backuppc-silzigan - cut into small pieces a computer configuration for backuppc
422Author: Gabriel Moreau <Gabriel.Moreau@univ-grenoble-alpes.fr>
423Copyright (C) 2011-2019, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
424
425$Id: backuppc-silzigan..host.legilnx32 3821 2013-12-20 08:03:14Z g7moreau $
426END
427   return;
428   }
Note: See TracBrowser for help on using the repository browser.