Subversion Repositories sysadmin_scripts

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
143 rodolico 1
#! /usr/bin/env perl
2
 
3
use strict;
4
use warnings;
5
#use experimental "switch";
6
 
7
# requires File::Slurp. 
8
# In Debian derivatives
9
# apt install libfile-slurp-perl
10
 
11
# apt install libxml-libxml-perl libyaml-tiny-perl
12
 
13
 
14
BEGIN {
15
   use FindBin;
16
   use File::Spec;
17
   # use libraries from the directory this script is in
18
   use lib File::Spec->catdir($FindBin::Bin);
19
}
20
 
21
use Data::Dumper;
22
use YAML::Tiny;
23
 
24
# global variables
25
my $scriptDir = $FindBin::RealBin;
26
my $scriptName = $FindBin::Script;
27
my $confDir = "$scriptDir/conf";
28
my $dbDir = "$scriptDir/var";
29
my $nodeDBName = "$dbDir/node.yaml";
30
my $virtualDBName = "$dbDir/virtuals.yaml";
31
my $nodePopulationDBName = "$dbDir/node_population.yaml";
32
 
33
# these contain the values from the databases
34
# loaded on demand
35
my $nodeDB;
36
my $virtDB;
37
my $nodePopulations;
38
 
39
 
40
my $dryRun = 1;
41
my $DEBUG = 0;
42
 
43
sub help {
44
   print "$0 command [argument]\n";
45
   print "where command is one of\n";
46
   print "\tnode update [node] [node]... # update a given node (or ALL)\n";
47
   print "\tnode list # display tab delimited list of node specs\n";
48
   print "\tnode scan # find virtuals on all nodes\n ";
49
   print "\tvirtual update ALL|RUNNING|[virtual] [virtual]... # update virtuals\n";
50
}
51
 
52
sub readDB {
53
   my ($filename) = @_;
54
   my $yaml = YAML::Tiny->new( {} );
55
   if ( -f $filename ) {
56
      $yaml = YAML::Tiny->read( $filename );
57
   }
58
   return $yaml->[0];
59
}
60
 
61
sub writeDB {
62
   my ($filename,$data) = @_;
63
   my $yaml = YAML::Tiny->new( $data );
64
   $yaml->write( $filename );
65
}
66
 
67
sub loadVirtDB {
68
   return if $virtDB;
69
   $virtDB = &readDB( $virtualDBName );
70
}
71
 
72
sub loadNodePopulations {
73
   return if $nodePopulations;
74
   $nodePopulations = &readDB( $nodePopulationDBName );
75
}
76
 
77
sub loadNodeDB {
78
   return if $nodeDB;
79
   $nodeDB = &readDB( $nodeDBName );
80
}
81
 
82
sub virtual {
83
   my $action = lc shift;
84
   my $return = '';
85
   &loadVirtDB();
86
   &loadNodePopulations();
87
   @_ = keys( %$virtDB ) if ( $_[0] && $_[0] eq 'ALL' );
88
   if ( $_[0] && $_[0] eq 'RUNNING' ) {
89
      my @running;
90
      foreach my $node ( keys %$nodePopulations ) {
91
         push @running, keys %{ $nodePopulations->{$node}->{'running'} };
92
      }
93
      @_ = @running;
94
   }
95
   if ( $action eq 'update' ) { # download xml to var and update database
96
      while ( my $virt = shift ) {
97
         &parseVirtual( $virt );
98
      } # while
99
      &writeDB( $virtualDBName, $virtDB );
100
   } elsif ( $action eq 'list' ) { # dump virtual as a tab separated data file
101
      my @return;
102
      foreach my $node ( keys %$nodePopulations ) {
103
         foreach my $virt (keys %{$nodePopulations->{$node}->{'running'}} ) {
104
            push @return, &listVirtual( $virt, $node );
105
         }
106
      }
107
      $return = join( "\n", sort @return ) . "\n";;
108
   }
109
   return $return;;
110
} # sub virtual
111
 
112
sub listVirtual {
113
   my ($virt,$node) = @_;
114
   my @return;
115
   push @return, $virt;
116
   push @return, $node;
117
   foreach my $column ( sort keys %{ $virtDB->{$virt} } ) {
118
      push @return, $virtDB->{$virt}->{$column};
119
   }
120
   return join( "\t", @return);
121
}
122
 
123
 
124
 
125
# get the XML definition file of a running virtual off of whatever
126
# node it is running on, and save it to disk
127
sub getVirtConfig {
128
   my ($virt,$filename) = @_;
129
   my $return;
130
   print "In getVirtConfig looking for $virt with file $filename\n" if $DEBUG;
131
   if ( -f $filename ) {
132
      open XML, "<$filename" or die "Could not read from $filename: $!\n";
133
      $return = join( '', <XML> );
134
      close XML;
135
   } else {
136
      &loadNodePopulations();
137
      #die Dumper( $nodePopulations );
138
      foreach my $node ( keys %$nodePopulations ) {
139
         print "getVirtConfig Looking on $node for $virt\n";
140
         if ( exists( $nodePopulations->{$node}->{'running'}->{$virt} ) ) { # we found it
141
            print "Found $virt on node $node\n";
142
            $return = `ssh $node 'virsh dumpxml $virt'`;
143
            open XML,">$filename" or die "Could not write to $filename: $!\n";
144
            print XML $return;
145
            close XML;
146
         } # if
147
      } # foreach
148
   } # if..else
149
   return $return;
150
} # sub getVirtConfig
151
 
152
sub getXMLValue {
153
   my ( $key, $string ) = @_;
154
   my $start = "<$key";
155
   my $end = "</$key>";
156
   $string =~ m/$start([^>]*)>([^<]+)$end/;
157
   return ($1,$2);
158
}
159
 
160
sub parseVirtual {
161
   my ($virt, $nodePopulations ) = @_;
162
 
163
   my @keysToSave = ( 'uuid', 'memory', 'vcpu' );
164
   my $filename = "$confDir/$virt.xml";
165
   my $xml = &getVirtConfig( $virt, $filename );
166
   my ($param,$value) = &getXMLValue( 'uuid', $xml );
167
   $virtDB->{$virt}->{'uuid'} = $value;
168
   ($param,$value) = &getXMLValue( 'memory', $xml );
169
   $virtDB->{$virt}->{'memory'} = $value;
170
   ($param,$value) = &getXMLValue( 'vcpu', $xml );
171
   $virtDB->{$virt}->{'vcpu'} = $value;
172
 
173
   $xml =~ m/type='vnc' port='(\d+)'/;
174
   $virtDB->{$virt}->{'vnc'} = $1;
175
}
176
 
177
sub getVirtualsOnNode {
178
   my $node = shift;
179
   my @nodeList = grep { /^\s*\d/ } `ssh $node 'virsh list'`;
180
   for ( my $i = 0; $i < @nodeList; $i++ ) {
181
      if ( $nodeList[$i] =~ m/\s*\d+\s*([^ ]+)/ ) {
182
         $nodeList[$i] = $1;
183
      }
184
   }
185
   my %hash = map{ $_ => time } @nodeList;
186
   return \%hash;
187
}
188
 
189
sub node {
190
   my $action = lc shift;
191
 
192
   my %conversion = ( 
193
     'CPU frequency' => 'clock',
194
     'CPU model' => 'cpu_model',
195
     'CPU socket(s)' => 'cpu_socket',
196
     'CPU(s)' => 'cpu_count',
197
     'Core(s) per socket' => 'cpu_cores',
198
     'Memory size' => 'memory',
199
     'NUMA cell(s)' => 'numa_cells',
200
     'Thread(s) per core' => 'threads_per_core'
201
   );
202
 
203
 
204
   print "In node, action is $action\n" if $DEBUG;
205
   my $return = '';
206
   &loadNodeDB();
207
   if ( $action eq 'update' ) { # read information for nodes and update database
208
      @_ = keys %$nodeDB if ( $_[0] eq 'ALL' );
209
      while ( my $nodename = shift ) {
210
         print "Updating $nodename\n" if $DEBUG;
211
         $return = `ssh $nodename 'virsh nodeinfo'`;
212
         print "Output of ssh $nodename 'virsh nodeinfo' is\n" . $return if $DEBUG;
213
         my @nodeinfo = split( "\n", $return );
214
         for ( my $i = 0; $i < @nodeinfo; $i++ ) {
215
            my ($key, $value) = split( /:\s+/, $nodeinfo[$i] );
216
            if ( $value =~ m/^(\d+)\s+[a-z]+$/i ) {
217
               $value = $1;
218
            }
219
            $key = $conversion{$key} if exists( $conversion{$key} );
220
            $nodeDB->{$nodename}->{$key} = $value;
221
         } # for
222
      } # while
223
      print "nodeDB state after update\n" . Dumper( $nodeDB ) if $DEBUG;
224
      &writeDB( $nodeDBName, $nodeDB );
225
   } elsif ( $action eq 'list' ) { # dump database as a tab separated file with headers
226
      my @return;
227
      foreach my $node ( sort keys %$nodeDB ) {
228
         @return[0] = "Node\t" . join( "\t", sort keys %{ $nodeDB->{$node} } ) unless @return;
229
         my @line;
230
         push @line, $node;
231
         foreach my $column (sort keys %{ $nodeDB->{$node} }) {
232
            push @line, $nodeDB->{$node}->{$column};
233
         }
234
         push @return, join( "\t", @line );
235
      }
236
      $return = join( "\n", @return ) . "\n";
237
   } elsif ( $action eq 'scan' ) {
238
      foreach my $node ( keys %$nodeDB ) {
239
         $nodePopulations->{$node}->{'running'} = &getVirtualsOnNode( $node );
240
         $nodePopulations->{$node}->{'lastchecked'} = time;
241
      }
242
      &writeDB( $nodePopulationDBName,$nodePopulations );
243
   } # if..elsif
244
   return $return;
245
}
246
 
247
#my $config = &readConf( $confFile );
248
 
249
my $command = shift; # the first one is the actual subsection
250
my $action = shift; # second is action to run
251
 
252
if ( $command eq 'node' ) {
253
   print &node( $action, @ARGV );
254
} elsif ( $command eq 'virtual' ) {
255
   print &virtual( $action, @ARGV );
256
} else {
257
   &help();
258
}
259
 
260
 
261
1;