Query kvm xml information script

Introduction

One of the problems I was expieriencing after creating a machine is to gather information for further post scripts. I had thaught of creating a mysql table with all the information of the hosts, but I have’nt designed it yet, and decided to rather query information straight from the domain xml.

A further possibility for the scipt is to run it from cron, and check if the database is updated with the correct information.

Setup

According to my previous post ( http://wp.me/p1kux7-bi ), the machine setup has not changed.

Script

root@localhost # cat host_info.pl
#!/usr/bin/perl
###############
#
#  Query information about the host
#
use strict;
use warnings;
use Getopt::Long;
use Switch;

##############
#  XML location and variables
my $XMLDIR="/etc/libvirt/qemu";
my $debug = 0;
my $help = 0;
my $host = 0;
my $list = 0;
my $memory = 0;
my $vcpu = 0;
my $disk = 0;
my $mac = 0;
my $name = 0;
my $verbose =0;
my $all = 1;

################
# Arguments
GetOptions("help|?" => \$help,
           "verbose|v" => \$verbose,
           "h|host=s" => \$host,
           "l|list" => \$list,
           "xml=s"   => \$XMLDIR,
           "mem" => sub { $memory=1 , $all=0 } ,
           "disk" => sub { $disk=1 , $all=0 } ,
           "cpu" => sub { $vcpu=1 , $all=0 } ,
           "name" => sub { $name=1, $all=0 },
           "mac" => sub { $mac=1, $all=0},
           "all" => \$all,
           "d|debug" => \$debug
        );

if ( $help ) {
        print "How to use the query host tool\n";
        print "$0 \$options \n";
        print "-? or help               help\n";
        print "-l or list               list all host information\n";
        print "-h or host               show information for one host\n";
        print "-xml                     Change default XML directory (default: '$XMLDIR')\n";
        print "-v or verbose            be verbose by output\n";
        print "-mem                     show memory\n";
        print "-name                    show name in output\n";
        print "-disk                    show disk\n";
        print "-cpu                     return number of cpus\n";
        print "-mac                     return mac address\n";
        print "-all                     print all info\n";
        print "-debug                   print debug information\n";
}

sub openxml($) {
    my $file= $_[0];
    $debug and print $file."\n";
    my $omemory = 0 ;
    my $ovcpu = 0 ;
    my $odisk = 0 ;
    my $omac = 0 ;
    my $oname = 0 ;
    my ( $prefix,$postfix ) = 0;
    open ( my $FILE, $file );
    while ( my $line = <$FILE>) {
        chomp $line;
        $debug and print $line."\n";
        if ( $line =~ /^\s*<mac address='/ ) {
            ( $prefix, $omac, $postfix ) = $line =~ /(^\s*<mac address=')([^']*[^'])('\/>\s*$)/;
            $debug and print "The mac address is:'$omac'\n";
        }
        if ( $line =~ /^\s*<memory>/ ) {
            ( $prefix, $omemory, $postfix ) = $line =~ /(^\s*<memory>)([^>]*[^<])(<\/memory>\s*$)/;
            $debug and print "My memory is:'$omemory'\n";
        }
        if ( $line =~ /^\s*<vcpu>/ ) {
            ( $prefix, $ovcpu, $postfix ) = $line =~ /(^\s*<vcpu>)([^<>]*)(<\/vcpu>\s*$)/;
            $debug and print "My Vcpu is:'$ovcpu'\n";
        }
        if ( $line =~ /^\s*<source dev='/ ) {
            ( $prefix, $odisk, $postfix ) = $line =~ /(^\s*<source dev=')([^']*[^'])('\/>\s*$)/;
            $debug and print "My disk is:'$odisk'\n";
        }
        if ( $line =~ /^\s*<name>/ ) {
            ( $prefix, $oname, $postfix ) = $line =~ /(^\s*<name>)([^']*[^'])(<\/name>\s*$)/;
            $debug and print "My name is:'$oname'\n";
        }
    }
    return ( $oname, $omemory, $omac, $ovcpu, $odisk );
    close $FILE;
}

sub format(@) {
    my ( $fname, $fmemory, $fmac, $fcpu, $fdisk) = @_;
    my $outstring = "";
    $debug and print "My variables for format sub:name =>'$fname' memory =>'$fmemory' mac =>'$fmac' cpu =>'$fcpu' disk =>'$fdisk'\n";
    if ( $all ) {
        $debug and print "All argument found.\n";
        ($verbose and $outstring= "host=$fname memory=$fmemory mac=$fmac cpu=$fcpu disk=$fdisk" ) or $outstring="$fname $fmemory $fmac $fcpu $fdisk";
    } else {
        $debug and print "I am in the else tree\n";
        if ( $name ne 0 ) {
            $debug and print "Name specified\n";
            ($verbose and $outstring .= "host=$fname ") or $outstring.="$fname ";
            $debug and print "My outstring is:'$outstring'\n";
        }
        if ( $memory ne 0 ) {
            $debug and print "Memory specified.\n";
            ($verbose and $outstring .= "memory=$fmemory ") or $outstring.="$fmemory ";
            $debug and print "\n";
        }
        if ( $mac ne 0 ) {
            $debug and print "Mac specified.\n";
            ($verbose and $outstring .= "mac=$fmac ") or $outstring.="$fmac ";
            $debug and print "\n";
        }
        if ( $vcpu ne 0 ) {
            $debug and print "CPU specified.\n";
            ($verbose and $outstring .= "cpu=$fcpu " ) or $outstring.="$fcpu ";
            $debug and print "\n";
        }
        if ( $disk ne 0 ) {
            $debug and print "Disk specified.\n";
            ($verbose and $outstring .= "disk=$fdisk") or $outstring.="$fdisk ";
            $debug and print "\n";
        }
    }
    $debug and print "My outstring is:'$outstring'\n";
    return($outstring);
}

if ( $host ) {
    $debug and print "Host specified :'$host'\n";
    my $file="$XMLDIR/$host.xml";
    $debug and print "My file is '$file'\n";
    if ( -e $file ) {
        $debug and print "File exists\n";
        my @output = openxml($file);
        $debug and print "My output was:'@output'\n";
        ( $verbose and print &format(@output)."\n") or print &format(@output)."\n";
    } else {
        $debug and print "File doesnt exist:'$file'\n";
    }
}
elsif ( $list ) {
    $debug and print "List specified\n";
    my @files=<$XMLDIR/*.xml>;
    $debug and print "My list is:'@files'\n";
    foreach my $file ( @files ) {
        $debug and print "File found doing sub on it:'$file'\n";
        my @output = openxml($file);
        $debug and print "My output was:'@output'\n";
        ( $verbose and print &format(@output)."\n" ) or print &format(@output)."\n";
    }
} else {
        $debug and print "No host or list specified.\n";
}

Explanation

Let’s iterate through the lines of code.

The entry point of the script is at line 137. There are 3 possbilities. Either 1 machine information is requested then a host is specified. If every information is requested from all hosts then a list is  generated. An unexpected possibility that neither a specific host or list is requested then nothing is printed  (except if we specify the debug option).

Since kvm names its xml files according to the domainname, this can be used for querying. It is possible in some setups when the domainname.xml is renamed and the filename and the domainname entry in the xml are not the same. Some checks or monitoring solutions need to find such cases aswell.

In the case that a list has to be processed, all xml files within the requested folder  (default for kvm is /etc/libvirt/qemu on debian) are added to a array, and with a loop every file is treated like a single host, passed onto openxml sub, which will be discussed later.

If only a single host is required the above step can be skipped and the file can be opened with openxml sub.

Openxml sub

This is the heart of the program, which can be attuned to your requirments. Attributes which I found important were mac address, maximum memory, virtual cpu limit, name and disk(this is useful for example for resize.sh at http://wp.me/p1kux7-bi ). If other information is required this sub should be changed.

One interesting question is if all xml files are according to standard format, and what are those standards in your enviroment. For example I have vcpus defined as follows:


<vcpu>2</vcpu>

According to libvirt domain xml format guide at http://libvirt.org/formatdomain.html this should be the correct and accepted version,although I have seen setups using different format:


<vcpu=2/>

After openxml sub is run  it returns with all information.

For large scale deployments this is not optimised. There is alot of unneeded information queried which should never be touched if not requested. CPU time and IO is expensive and should be kept to minimum.

Format sub

Format sub prints the information. This has been prepared for user and script handling. The main idea is that if we specify the -v (verbose) argument, the output is verbose  showing which value represents what:

root@localhost# host_info.pl -v -h debian
host=debian memory=262144 mac=ac:de:48:4a:88:f0 cpu=1 disk=/dev/mapper/lvm-vg_debian

or without the verbose argument for script parsing:

root@localhost# host_info.pl -h debian
debian 262144 ac:de:48:4a:88:f0 1 /dev/mapper/lvm-vg_debian

There are also some extra arguments which can be supplied if specific information is required. If we only need the memory, or mac address we can simly query that:

root@localhost# host_info.pl -h debian -v -mac
mac=ac:de:48:4a:88:f0

Also the requested information can be chained together:


root@localhost# host_info.pl -h debian -v -mac -mem
memory=262144 mac=ac:de:48:4a:88:f0

In case a list is requested all information is printed in a new line.

Thaughts

My purpose with this script is to feed post scripts and have the possibility to update sql database with information or to check data cosistency after updates.

If we are in a large scale enviroment then disk IO for querying files can be costful and a backend database file would be cheaper and easier to query.

Advertisements

Checking the age of a file – Perl in monitoring

An unusual monitoring request

Some time ago we – at the monitoring team got a bit strange request: Check the time when a file was written last time. If the modification time of it is older than “x” minutes, rise a warning message in the monitoring system.

Although, it is kind of indirect monitoring, it can be really relevant regarding the health of an application which is known to write information into a file at known regular intervals. Continue reading “Checking the age of a file – Perl in monitoring”

Perl exercises shortest route between two towns

Introduction

I will present some of the exercises I got from my perl programming mentor. These exercises helped me to get more in depth with perl, and learn the mechanism behind the language.  You can try to solve these exercises also on your own, to improve your skills.

My solution is only one way to solve it. A problem has unlimited number of solutions, and if they all accomplish the task, they are all correct.

Usually I will first write the specification I got, then I will show my solution and also give some explanation.
Continue reading “Perl exercises shortest route between two towns”

SmartCard-based authentication for SSH sessions

The issue

The security of all IT systems can be compromised only through the interfaces between them and the world, so the security measures that protect the access to such systems are highly important. Administrators usually have higher privileges than average users, this is especially true for administrative access, that is one of the reasons why the old-fashioned telnet has been replaced by ssh.

Although the network traffic itself was thus protected by encryption, as long as the key of this encryption still depended on just some passwords -in the case of the PasswordAuthentication method- it was still vulnerable for dictionary attack, and as some kind of hash of this password had to be stored on server-side, it could be stolen and used for known-cyphertext attacks against the password.

The next step was using RSA private/public keypairs, where the public key was known by the server but the private one was kept as secret by the user. The user proved his identity by encrypting some server-generated nonce with his private key and the server verified it by decrypting it with the public half of the keypair: if the result matches the original nonce, it proves that the user has the private part of the keypair, and this confirms his identity (or defines it, depends on the viewpoint).

This way the user could be impersonated only by stealing his private key, something that exists only on his client computer, and even there only in a locally encrypted form, so the user has to (temporarily) decrypt it with the passphrase of the key before use. But what happens if this client computer gets stolen? If the passphrase is simple enough for a human to remember, it is usually simple enough for some cryptographic attack to crack. As the attacker now has the encrypted private key, he may attempt to decrypt it with as many candidate passphrases as he wants to, and sooner or later he would eventually succeed.
(OK, with a decent passphrase this later may outlast the Sun, but then it would be too complex to remember and even to enter frequently without mistakes.)

The only measures that can prevent such attacks are disabling access even to the encrypted private key and limiting the number of failed attempts before disabling it permanently. Exactly this is what SmartCards do: they contain some storage for the private and public keys (the latter ones embedded in X.509 Certificates) and a small microcontroller that actually performs all the tasks that require the private key. This way only the microcontroller has access to the private key, and as it is programmed to physically delete it after some number of failed passphrase attempts, this system can resist most security attacks. Considering that the encrypted private key is no longer available for anyone, the importance of passphrase complexity decreased dramatically: if the number of retries is low, even a 4-digit number will do, that is what the PIN number actually is for.

Our goal

Now that we have everything at hand, all we have to do is to put these subsystems together, and that is what this article is about. Our goal is to integrate and set up a bunch of 3rd-party tools to make our SmartCard available for authentication for SSH clients: first for PuTTY and second to Cygwin-based OpenSSH, while keeping the native Unix clients in mind as well.

Continue reading “SmartCard-based authentication for SSH sessions”