| 5 | rodolico | 1 | #!/usr/bin/env perl
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | # All functions related to maniplating domains
 | 
        
           |  |  | 4 | # part of havirt.
 | 
        
           |  |  | 5 |   | 
        
           |  |  | 6 | # Copyright 2024 Daily Data, Inc.
 | 
        
           |  |  | 7 | # 
 | 
        
           |  |  | 8 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 
 | 
        
           |  |  | 9 | # conditions are met:
 | 
        
           |  |  | 10 | #
 | 
        
           |  |  | 11 | #   Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 | 
        
           |  |  | 12 | #   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
 | 
        
           |  |  | 13 | #   in the documentation and/or other materials provided with the distribution.
 | 
        
           |  |  | 14 | #   Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
 | 
        
           |  |  | 15 | #   from this software without specific prior written permission.
 | 
        
           |  |  | 16 | # 
 | 
        
           |  |  | 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 | 
        
           |  |  | 18 | # NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 | 
        
           |  |  | 19 | # THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
        
           |  |  | 20 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
        
           |  |  | 21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | 
        
           |  |  | 22 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
        
           |  |  | 23 |   | 
        
           |  |  | 24 | # v0.0.1 20240602 RWR
 | 
        
           |  |  | 25 | # Initial setup
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | package domain;
 | 
        
           |  |  | 28 |   | 
        
           |  |  | 29 | use warnings;
 | 
        
           |  |  | 30 | use strict;  
 | 
        
           |  |  | 31 |   | 
        
           |  |  | 32 | # define the version number
 | 
        
           |  |  | 33 | # see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
 | 
        
           |  |  | 34 | use version;
 | 
        
           |  |  | 35 | our $VERSION = version->declare("0.0.1");
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 |   | 
        
           |  |  | 38 | use Data::Dumper;
 | 
        
           |  |  | 39 |   | 
        
           |  |  | 40 | use Exporter;
 | 
        
           |  |  | 41 |   | 
        
           |  |  | 42 | our @ISA = qw( Exporter );
 | 
        
           |  |  | 43 | our @EXPORT = qw( 
 | 
        
           | 7 | rodolico | 44 |                   &list
 | 
        
           | 5 | rodolico | 45 |                 );
 | 
        
           |  |  | 46 |   | 
        
           | 9 | rodolico | 47 | sub help {
 | 
        
           |  |  | 48 |    my @return;
 | 
        
           |  |  | 49 |    push @return, "domain update [domainname|-t domainname]";
 | 
        
           |  |  | 50 |    push @return, "\tUpdates capabilities on one or more domains, default is all domains";
 | 
        
           |  |  | 51 |    push @return, "domain list [--format|-f screen|tsv]";
 | 
        
           |  |  | 52 |    push @return, "\tLists all domains with some statistics about them as screen or tsv (default screen)";
 | 
        
           |  |  | 53 |    return join( "\n", @return ) . "\n";
 | 
        
           |  |  | 54 | }
 | 
        
           |  |  | 55 |   | 
        
           | 7 | rodolico | 56 |   | 
        
           |  |  | 57 | sub list {
 | 
        
           | 10 | rodolico | 58 |    &main::loadVirtDB();
 | 
        
           | 7 | rodolico | 59 |    &main::loadNodePopulations();
 | 
        
           |  |  | 60 |    print Dumper( $main::nodePopulations ) if $main::DEBUG > 2;
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |    my @header;
 | 
        
           |  |  | 63 |    my @data;
 | 
        
           |  |  | 64 |   | 
        
           |  |  | 65 |    foreach my $node ( keys %$main::nodePopulations ) {
 | 
        
           |  |  | 66 |       foreach my $virt (keys %{$main::nodePopulations->{$node}->{'running'}} ) {
 | 
        
           |  |  | 67 |          unless ( @header ) {
 | 
        
           |  |  | 68 |             # if we don't have a header yet, create it from the keys in this one. Assumes every entry has same keys
 | 
        
           |  |  | 69 |             @header = sort keys %{ $main::virtDB->{$virt} };
 | 
        
           |  |  | 70 |             unshift @header, 'Node';
 | 
        
           |  |  | 71 |             unshift @header, 'Domain';
 | 
        
           |  |  | 72 |          } # unless
 | 
        
           |  |  | 73 |          my @line;
 | 
        
           |  |  | 74 |          push @line, $node;
 | 
        
           |  |  | 75 |          push @line, $virt;
 | 
        
           |  |  | 76 |          foreach my $column ( sort keys %{ $main::virtDB->{$virt} } ) {
 | 
        
           |  |  | 77 |             push @line, $main::virtDB->{$virt}->{$column};
 | 
        
           |  |  | 78 |          }
 | 
        
           |  |  | 79 |          push @data, \@line;
 | 
        
           |  |  | 80 |       }
 | 
        
           |  |  | 81 |    }
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |    return &main::report( \@header, \@data );
 | 
        
           |  |  | 84 | }
 | 
        
           |  |  | 85 |   | 
        
           |  |  | 86 | sub update {
 | 
        
           | 10 | rodolico | 87 |    &main::loadVirtDB();
 | 
        
           | 8 | rodolico | 88 |    unless ( @_ ) {
 | 
        
           |  |  | 89 |       # they didn't pass in anything, so do everything
 | 
        
           |  |  | 90 |       @_ = keys %{ $main::virtDB }
 | 
        
           |  |  | 91 |    } # unless
 | 
        
           |  |  | 92 |    print "Preparing to update " . join( "\n", @_ ) . "\n" if $main::DEBUG > 1;
 | 
        
           | 7 | rodolico | 93 |    while ( my $virt = shift ) {
 | 
        
           |  |  | 94 |       &parseDomain( $virt );
 | 
        
           |  |  | 95 |    } # while
 | 
        
           | 9 | rodolico | 96 |    &main::writeDB( $main::domainDBName, $main::virtDB );
 | 
        
           |  |  | 97 |    return "Updated\n";
 | 
        
           | 7 | rodolico | 98 | }
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 | sub getXMLValue {
 | 
        
           |  |  | 101 |    my ( $key, $string ) = @_;
 | 
        
           | 9 | rodolico | 102 |    print "getXMLValue: looking for [$key] $string\n" if $main::DEBUG > 2;
 | 
        
           | 7 | rodolico | 103 |    my $start = "<$key";
 | 
        
           |  |  | 104 |    my $end = "</$key>";
 | 
        
           |  |  | 105 |    $string =~ m/$start([^>]*)>([^<]+)$end/;
 | 
        
           |  |  | 106 |    return ($1,$2);
 | 
        
           |  |  | 107 | }
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 | sub parseDomain {
 | 
        
           |  |  | 110 |    my ($virt, $nodePopulations ) = @_;
 | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 |    my @keysToSave = ( 'uuid', 'memory', 'vcpu','vnc' );
 | 
        
           |  |  | 113 |    my $filename = "$main::confDir/$virt.xml";
 | 
        
           |  |  | 114 |    my $xml = &getVirtConfig( $virt, $filename );
 | 
        
           |  |  | 115 |    my ($param,$value) = &getXMLValue( 'uuid', $xml );
 | 
        
           |  |  | 116 |    $main::virtDB->{$virt}->{'uuid'} = $value;
 | 
        
           |  |  | 117 |    ($param,$value) = &getXMLValue( 'memory', $xml );
 | 
        
           |  |  | 118 |    $main::virtDB->{$virt}->{'memory'} = $value;
 | 
        
           |  |  | 119 |    ($param,$value) = &getXMLValue( 'vcpu', $xml );
 | 
        
           |  |  | 120 |    $main::virtDB->{$virt}->{'vcpu'} = $value;
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |    $xml =~ m/type='vnc' port='(\d+)'/;
 | 
        
           |  |  | 123 |    $main::virtDB->{$virt}->{'vnc'} = $1;
 | 
        
           |  |  | 124 | }
 | 
        
           |  |  | 125 |   | 
        
           |  |  | 126 | # get the XML definition file of a running domain off of whatever
 | 
        
           |  |  | 127 | # node it is running on, and save it to disk
 | 
        
           |  |  | 128 | sub getVirtConfig {
 | 
        
           |  |  | 129 |    my ($virt,$filename) = @_;
 | 
        
           |  |  | 130 |    my $return;
 | 
        
           | 8 | rodolico | 131 |    print "In getVirtConfig looking for $virt with file $filename\n" if $main::DEBUG;
 | 
        
           | 7 | rodolico | 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 |       &main::loadNodePopulations();
 | 
        
           |  |  | 138 |       foreach my $node ( keys %$main::nodePopulations ) {
 | 
        
           | 9 | rodolico | 139 |          print "getVirtConfig Looking on $node for $virt\n" if $main::DEBUG > 1;;
 | 
        
           | 7 | rodolico | 140 |          if ( exists( $main::nodePopulations->{$node}->{'running'}->{$virt} ) ) { # we found it
 | 
        
           | 9 | rodolico | 141 |             print "Found $virt on node $node\n" if $main::DEBUG;;
 | 
        
           | 7 | rodolico | 142 |             $return = `ssh $node 'virsh dumpxml $virt'`;
 | 
        
           | 9 | rodolico | 143 |             print "Writing config for $virt from $node into $filename\n" if $main::DEBUG;
 | 
        
           | 7 | rodolico | 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
 |