| 140 | rodolico | 1 | #! /usr/bin/env perl
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | use strict;
 | 
        
           |  |  | 4 | use warnings;
 | 
        
           |  |  | 5 | use YAML::XS qw(DumpFile LoadFile); # libyaml-libyaml-perl
 | 
        
           |  |  | 6 | use Getopt::Std;
 | 
        
           |  |  | 7 |   | 
        
           |  |  | 8 | $main::VERSION = '0.1';
 | 
        
           |  |  | 9 |   | 
        
           |  |  | 10 | my $dbFile = 'virtinfo_db.yaml';
 | 
        
           |  |  | 11 | my $lockFile = 'virtinfo.lock';
 | 
        
           |  |  | 12 |   | 
        
           |  |  | 13 |   | 
        
           |  |  | 14 | use Data::Dumper;
 | 
        
           |  |  | 15 |   | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | #
 | 
        
           |  |  | 18 | # find where the script is actually located as cfg should be there
 | 
        
           |  |  | 19 | #
 | 
        
           |  |  | 20 | sub getScriptLocation {
 | 
        
           |  |  | 21 |    use strict;
 | 
        
           |  |  | 22 |    use File::Spec::Functions qw(rel2abs);
 | 
        
           |  |  | 23 |    use File::Basename;
 | 
        
           |  |  | 24 |    return dirname(rel2abs($0));
 | 
        
           |  |  | 25 | }
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | sub main::HELP_MESSAGE {
 | 
        
           |  |  | 28 |    print "\nScript reads running virtuals and store results in database\n\n";
 | 
        
           |  |  | 29 |    print "-s          Show database and exit\n";
 | 
        
           |  |  | 30 |    print "-c filename Use alternative database\n";
 | 
        
           |  |  | 31 |    print "-n          Don't update the database\n";
 | 
        
           |  |  | 32 |    print "-v          Be Verbose (show some stats)\n";
 | 
        
           |  |  | 33 |    print "-r          Report - only display if something changed\n";
 | 
        
           |  |  | 34 | }
 | 
        
           |  |  | 35 |   | 
        
           |  |  | 36 | sub main::VERSION {
 | 
        
           |  |  | 37 |    &HELP_MESSAGE();
 | 
        
           |  |  | 38 | }
 | 
        
           |  |  | 39 |   | 
        
           |  |  | 40 | sub readDB {
 | 
        
           |  |  | 41 |    my $lock = shift;
 | 
        
           |  |  | 42 |    my $db;
 | 
        
           |  |  | 43 |    sleep(5) if -e $lockFile;
 | 
        
           |  |  | 44 |    die "Could not get lock on $dbFile: Aborting\n" if -e $lockFile;
 | 
        
           |  |  | 45 |    `touch $lockFile` unless $lock;
 | 
        
           |  |  | 46 |    $db = LoadFile( $dbFile ) if ( -e $dbFile );
 | 
        
           |  |  | 47 |    return $db;
 | 
        
           |  |  | 48 | }
 | 
        
           |  |  | 49 |   | 
        
           |  |  | 50 | sub saveDB {
 | 
        
           |  |  | 51 |    my ($db, $verbose)  = @_;
 | 
        
           |  |  | 52 |    print "Updating database\n" if $verbose;
 | 
        
           |  |  | 53 |    DumpFile( $dbFile, $db );
 | 
        
           |  |  | 54 | }
 | 
        
           |  |  | 55 |   | 
        
           |  |  | 56 | # many of the outputs produce key:value pairs
 | 
        
           |  |  | 57 | # we will parse this into a hash
 | 
        
           |  |  | 58 | sub parseDelimited {
 | 
        
           |  |  | 59 |    my $delimiter = shift;
 | 
        
           |  |  | 60 |    my %return;
 | 
        
           |  |  | 61 |    while ( my $line = shift ) {
 | 
        
           |  |  | 62 |       chomp $line;
 | 
        
           |  |  | 63 |       next unless $line;
 | 
        
           |  |  | 64 |       my ( $key, $value ) = split( /$delimiter/, $line );
 | 
        
           |  |  | 65 |       $return{lc &trim($key) } = &trim($value);
 | 
        
           |  |  | 66 |    }
 | 
        
           |  |  | 67 |    return \%return;
 | 
        
           |  |  | 68 | }
 | 
        
           |  |  | 69 |   | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 | sub getNode {
 | 
        
           |  |  | 72 |    my @info = `virsh nodeinfo`;
 | 
        
           |  |  | 73 |    my $hypervisor = &parseDelimited( ':', @info );
 | 
        
           |  |  | 74 |    return $hypervisor;
 | 
        
           |  |  | 75 | }
 | 
        
           |  |  | 76 |   | 
        
           |  |  | 77 |   | 
        
           |  |  | 78 | sub getRunningVirtuals {
 | 
        
           |  |  | 79 |    my @domains = `virsh list`;
 | 
        
           |  |  | 80 |    chomp @domains;
 | 
        
           |  |  | 81 |    my %domains;
 | 
        
           |  |  | 82 |    foreach my $domain ( @domains ) {
 | 
        
           |  |  | 83 |       # skip the headers
 | 
        
           |  |  | 84 |       next if $domain =~ m/^(Id)|(--)/;
 | 
        
           |  |  | 85 |       if ( $domain =~ m/^\s*(\d+)\s+([^\s]+)\s/ ) {
 | 
        
           |  |  | 86 |          $domains{$2}{'id'} = $1;
 | 
        
           |  |  | 87 |       }
 | 
        
           |  |  | 88 |    }
 | 
        
           |  |  | 89 |    return \%domains;
 | 
        
           |  |  | 90 | }
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 | sub updateDB {
 | 
        
           |  |  | 93 |    my ( $node, $self, $verbose ) = @_; 
 | 
        
           |  |  | 94 |    # get a list of all running virtuals
 | 
        
           |  |  | 95 |    my $virts = &getRunningVirtuals();
 | 
        
           |  |  | 96 |    my @a = sort keys %$virts;
 | 
        
           |  |  | 97 |    my @b = sort keys  %{ $node->{$self}->{'virtuals'} };
 | 
        
           |  |  | 98 |    return 0 # return if they are the same, no action needed
 | 
        
           |  |  | 99 |       if &compareArray( \@a, \@b );
 | 
        
           |  |  | 100 |    # something has changed, so load all information again
 | 
        
           |  |  | 101 |    print "Something has changed, so loading everything again\n" if $verbose;
 | 
        
           |  |  | 102 |    $node->{$self}->{'hypervisor'} = &getNode();
 | 
        
           |  |  | 103 |    $node->{$self}->{'virtuals'} = $virts;;
 | 
        
           |  |  | 104 |    foreach my $domain ( keys %{$node->{$self}->{'virtuals'}} ) {
 | 
        
           |  |  | 105 |       next unless $domain;
 | 
        
           |  |  | 106 |       my @info = `virsh dominfo $domain`;
 | 
        
           |  |  | 107 |       my $vnc = `virsh vncdisplay $domain`;
 | 
        
           |  |  | 108 |       chomp $vnc;
 | 
        
           |  |  | 109 |       $vnc =~ m/^.*:(\d+)$/;
 | 
        
           |  |  | 110 |       push @info, "vncport: $1";
 | 
        
           |  |  | 111 |       $node->{$self}->{'virtuals'}->{$domain} = &parseDelimited( ':', @info );
 | 
        
           |  |  | 112 |    }
 | 
        
           |  |  | 113 |    return ( 1, $node );;
 | 
        
           |  |  | 114 | }
 | 
        
           |  |  | 115 |   | 
        
           |  |  | 116 |   | 
        
           |  |  | 117 | # remove leading and trailing whitespace from string
 | 
        
           |  |  | 118 | sub trim {
 | 
        
           |  |  | 119 |    my $string = shift;
 | 
        
           |  |  | 120 |    $string =~ s/^\s+|\s+$//;
 | 
        
           |  |  | 121 |    return $string;
 | 
        
           |  |  | 122 | }
 | 
        
           |  |  | 123 |   | 
        
           |  |  | 124 | sub showDOMUs {
 | 
        
           |  |  | 125 |    my $conf = shift;
 | 
        
           |  |  | 126 |    foreach my $node ( keys %$conf ) {
 | 
        
           |  |  | 127 |       print "$node\n";
 | 
        
           |  |  | 128 |       foreach my $virt ( keys %{ $conf->{$node}->{'virtuals'} } ) {
 | 
        
           |  |  | 129 |          print "\t$virt\n";
 | 
        
           |  |  | 130 |       }
 | 
        
           |  |  | 131 |    }
 | 
        
           |  |  | 132 | }
 | 
        
           |  |  | 133 |   | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 | sub compareArray {
 | 
        
           |  |  | 136 |    my ($arr1, $arr2 ) = @_;
 | 
        
           |  |  | 137 |    # if they don't have the same number of elements, not equal
 | 
        
           |  |  | 138 |    return 0 if @$arr1 != @$arr2;
 | 
        
           |  |  | 139 |    for ( my $i = 0; $i < scalar( @$arr1 ); $i++ ) {
 | 
        
           |  |  | 140 |       return 0 unless $$arr1[$i] eq $$arr2[$i];
 | 
        
           |  |  | 141 |    }
 | 
        
           |  |  | 142 |    return 1;
 | 
        
           |  |  | 143 | }
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |   | 
        
           |  |  | 146 | my $path = &getScriptLocation() . '/';
 | 
        
           |  |  | 147 | $dbFile = $path . $dbFile;
 | 
        
           |  |  | 148 | $lockFile = $path . $lockFile;
 | 
        
           |  |  | 149 |   | 
        
           |  |  | 150 |   | 
        
           |  |  | 151 | $Getopt::Std::STANDARD_HELP_VERSION = 1;
 | 
        
           |  |  | 152 | my %options = ();
 | 
        
           |  |  | 153 | getopts( 'vosrc:n', \%options );
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 | if ( $options{'o'} ) {
 | 
        
           |  |  | 156 |    print "Dumping cli parameters\n";
 | 
        
           |  |  | 157 |    foreach my $option ( keys %options ) {
 | 
        
           |  |  | 158 |       print "$option\t[$options{$option}]\n";
 | 
        
           |  |  | 159 |    }
 | 
        
           |  |  | 160 | }
 | 
        
           |  |  | 161 |   | 
        
           |  |  | 162 |   | 
        
           |  |  | 163 | my $db = &readDB( $options{'n'} );
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 | my $self = `hostname -f`;
 | 
        
           |  |  | 166 | chomp $self;
 | 
        
           |  |  | 167 |   | 
        
           |  |  | 168 | if ( $options{'s'} ) {
 | 
        
           |  |  | 169 |    &showDOMUs( $db );
 | 
        
           |  |  | 170 | } elsif ( ! $options{ 'n' } ) {
 | 
        
           |  |  | 171 |    print STDERR "Updating the database\n" if $options{'v'};
 | 
        
           |  |  | 172 |    my $dirty = 0;
 | 
        
           |  |  | 173 |    ( $dirty, $db ) = &updateDB( $db,$self, $options{'v'} );
 | 
        
           |  |  | 174 |    if ( $dirty ) {
 | 
        
           |  |  | 175 |       print "Virtuals changed on $self\n" if ($options{'r'});
 | 
        
           |  |  | 176 |       &saveDB( $db, $options{'v'} );
 | 
        
           |  |  | 177 |    }
 | 
        
           |  |  | 178 | }
 | 
        
           |  |  | 179 |   | 
        
           |  |  | 180 | `rm $lockFile` if -e $lockFile;
 | 
        
           |  |  | 181 |   | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 | 1;
 |