Subversion Repositories sysadmin_scripts

Rev

Rev 143 | Go to most recent revision | Details | Compare with Previous | 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";
144 rodolico 30
my $domainDBName = "$dbDir/domains.yaml";
143 rodolico 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";
144 rodolico 48
   print "\tnode scan # find domains on all nodes\n ";
49
   print "\tdomain update ALL|RUNNING|[domain] [domain]... # update domains\n";
50
   print "\tdomain list ALL|RUNNING|[domain] [domain]... # display tab delimited list of domain specs\n";
143 rodolico 51
}
52
 
53
sub readDB {
54
   my ($filename) = @_;
55
   my $yaml = YAML::Tiny->new( {} );
56
   if ( -f $filename ) {
57
      $yaml = YAML::Tiny->read( $filename );
58
   }
59
   return $yaml->[0];
60
}
61
 
62
sub writeDB {
63
   my ($filename,$data) = @_;
64
   my $yaml = YAML::Tiny->new( $data );
65
   $yaml->write( $filename );
66
}
67
 
68
sub loadVirtDB {
69
   return if $virtDB;
144 rodolico 70
   $virtDB = &readDB( $domainDBName );
143 rodolico 71
}
72
 
73
sub loadNodePopulations {
74
   return if $nodePopulations;
75
   $nodePopulations = &readDB( $nodePopulationDBName );
76
}
77
 
78
sub loadNodeDB {
79
   return if $nodeDB;
80
   $nodeDB = &readDB( $nodeDBName );
81
}
82
 
144 rodolico 83
sub domain {
143 rodolico 84
   my $action = lc shift;
85
   my $return = '';
86
   &loadVirtDB();
87
   &loadNodePopulations();
88
   @_ = keys( %$virtDB ) if ( $_[0] && $_[0] eq 'ALL' );
89
   if ( $_[0] && $_[0] eq 'RUNNING' ) {
90
      my @running;
91
      foreach my $node ( keys %$nodePopulations ) {
92
         push @running, keys %{ $nodePopulations->{$node}->{'running'} };
93
      }
94
      @_ = @running;
95
   }
96
   if ( $action eq 'update' ) { # download xml to var and update database
97
      while ( my $virt = shift ) {
144 rodolico 98
         &parseDomain( $virt );
143 rodolico 99
      } # while
144 rodolico 100
      &writeDB( $domainDBName, $virtDB );
101
   } elsif ( $action eq 'list' ) { # dump domain as a tab separated data file
143 rodolico 102
      my @return;
103
      foreach my $node ( keys %$nodePopulations ) {
104
         foreach my $virt (keys %{$nodePopulations->{$node}->{'running'}} ) {
144 rodolico 105
            push @return, &listDomain( $virt, $node );
143 rodolico 106
         }
107
      }
108
      $return = join( "\n", sort @return ) . "\n";;
109
   }
110
   return $return;;
144 rodolico 111
} # sub domain
143 rodolico 112
 
144 rodolico 113
sub listDomain {
143 rodolico 114
   my ($virt,$node) = @_;
115
   my @return;
116
   push @return, $virt;
117
   push @return, $node;
118
   foreach my $column ( sort keys %{ $virtDB->{$virt} } ) {
119
      push @return, $virtDB->{$virt}->{$column};
120
   }
121
   return join( "\t", @return);
122
}
123
 
124
 
125
 
144 rodolico 126
# get the XML definition file of a running domain off of whatever
143 rodolico 127
# node it is running on, and save it to disk
128
sub getVirtConfig {
129
   my ($virt,$filename) = @_;
130
   my $return;
131
   print "In getVirtConfig looking for $virt with file $filename\n" if $DEBUG;
132
   if ( -f $filename ) {
133
      open XML, "<$filename" or die "Could not read from $filename: $!\n";
134
      $return = join( '', <XML> );
135
      close XML;
136
   } else {
137
      &loadNodePopulations();
138
      #die Dumper( $nodePopulations );
139
      foreach my $node ( keys %$nodePopulations ) {
140
         print "getVirtConfig Looking on $node for $virt\n";
141
         if ( exists( $nodePopulations->{$node}->{'running'}->{$virt} ) ) { # we found it
142
            print "Found $virt on node $node\n";
143
            $return = `ssh $node 'virsh dumpxml $virt'`;
144
            open XML,">$filename" or die "Could not write to $filename: $!\n";
145
            print XML $return;
146
            close XML;
147
         } # if
148
      } # foreach
149
   } # if..else
150
   return $return;
151
} # sub getVirtConfig
152
 
153
sub getXMLValue {
154
   my ( $key, $string ) = @_;
155
   my $start = "<$key";
156
   my $end = "</$key>";
157
   $string =~ m/$start([^>]*)>([^<]+)$end/;
158
   return ($1,$2);
159
}
160
 
144 rodolico 161
sub parseDomain {
143 rodolico 162
   my ($virt, $nodePopulations ) = @_;
163
 
164
   my @keysToSave = ( 'uuid', 'memory', 'vcpu' );
165
   my $filename = "$confDir/$virt.xml";
166
   my $xml = &getVirtConfig( $virt, $filename );
167
   my ($param,$value) = &getXMLValue( 'uuid', $xml );
168
   $virtDB->{$virt}->{'uuid'} = $value;
169
   ($param,$value) = &getXMLValue( 'memory', $xml );
170
   $virtDB->{$virt}->{'memory'} = $value;
171
   ($param,$value) = &getXMLValue( 'vcpu', $xml );
172
   $virtDB->{$virt}->{'vcpu'} = $value;
173
 
174
   $xml =~ m/type='vnc' port='(\d+)'/;
175
   $virtDB->{$virt}->{'vnc'} = $1;
176
}
177
 
144 rodolico 178
sub getDomainsOnNode {
143 rodolico 179
   my $node = shift;
180
   my @nodeList = grep { /^\s*\d/ } `ssh $node 'virsh list'`;
181
   for ( my $i = 0; $i < @nodeList; $i++ ) {
182
      if ( $nodeList[$i] =~ m/\s*\d+\s*([^ ]+)/ ) {
183
         $nodeList[$i] = $1;
184
      }
185
   }
186
   my %hash = map{ $_ => time } @nodeList;
187
   return \%hash;
188
}
189
 
190
sub node {
191
   my $action = lc shift;
192
 
193
   my %conversion = ( 
194
     'CPU frequency' => 'clock',
195
     'CPU model' => 'cpu_model',
196
     'CPU socket(s)' => 'cpu_socket',
197
     'CPU(s)' => 'cpu_count',
198
     'Core(s) per socket' => 'cpu_cores',
199
     'Memory size' => 'memory',
200
     'NUMA cell(s)' => 'numa_cells',
201
     'Thread(s) per core' => 'threads_per_core'
202
   );
203
 
204
 
205
   print "In node, action is $action\n" if $DEBUG;
206
   my $return = '';
207
   &loadNodeDB();
208
   if ( $action eq 'update' ) { # read information for nodes and update database
209
      @_ = keys %$nodeDB if ( $_[0] eq 'ALL' );
210
      while ( my $nodename = shift ) {
211
         print "Updating $nodename\n" if $DEBUG;
212
         $return = `ssh $nodename 'virsh nodeinfo'`;
213
         print "Output of ssh $nodename 'virsh nodeinfo' is\n" . $return if $DEBUG;
214
         my @nodeinfo = split( "\n", $return );
215
         for ( my $i = 0; $i < @nodeinfo; $i++ ) {
216
            my ($key, $value) = split( /:\s+/, $nodeinfo[$i] );
217
            if ( $value =~ m/^(\d+)\s+[a-z]+$/i ) {
218
               $value = $1;
219
            }
220
            $key = $conversion{$key} if exists( $conversion{$key} );
221
            $nodeDB->{$nodename}->{$key} = $value;
222
         } # for
223
      } # while
224
      print "nodeDB state after update\n" . Dumper( $nodeDB ) if $DEBUG;
225
      &writeDB( $nodeDBName, $nodeDB );
226
   } elsif ( $action eq 'list' ) { # dump database as a tab separated file with headers
227
      my @return;
228
      foreach my $node ( sort keys %$nodeDB ) {
229
         @return[0] = "Node\t" . join( "\t", sort keys %{ $nodeDB->{$node} } ) unless @return;
230
         my @line;
231
         push @line, $node;
232
         foreach my $column (sort keys %{ $nodeDB->{$node} }) {
233
            push @line, $nodeDB->{$node}->{$column};
234
         }
235
         push @return, join( "\t", @line );
236
      }
237
      $return = join( "\n", @return ) . "\n";
238
   } elsif ( $action eq 'scan' ) {
239
      foreach my $node ( keys %$nodeDB ) {
144 rodolico 240
         $nodePopulations->{$node}->{'running'} = &getDomainsOnNode( $node );
143 rodolico 241
         $nodePopulations->{$node}->{'lastchecked'} = time;
242
      }
243
      &writeDB( $nodePopulationDBName,$nodePopulations );
244
   } # if..elsif
245
   return $return;
246
}
247
 
248
#my $config = &readConf( $confFile );
249
 
250
my $command = shift; # the first one is the actual subsection
251
my $action = shift; # second is action to run
252
 
253
if ( $command eq 'node' ) {
254
   print &node( $action, @ARGV );
144 rodolico 255
} elsif ( $command eq 'domain' ) {
256
   print &domain( $action, @ARGV );
143 rodolico 257
} else {
258
   &help();
259
}
260
 
261
 
262
1;