Clean up XML output
[libguestfs.git] / inspector / virt-inspector.pl
index 6aa63ad..bd8de70 100755 (executable)
@@ -24,11 +24,13 @@ use Pod::Usage;
 use Getopt::Long;
 use Data::Dumper;
 use File::Temp qw/tempdir/;
+use XML::Writer;
 
 # Optional:
 eval "use Sys::Virt;";
 eval "use XML::XPath;";
 eval "use XML::XPath::XMLParser;";
+eval "use YAML::Any;";
 
 =encoding utf8
 
@@ -134,6 +136,11 @@ Produce no output at all.
 If you select I<--xml> then you get XML output which can be fed
 to other programs.
 
+=item B<--yaml>
+
+If you select I<--yaml> then you get YAML output which can be fed
+to other programs.
+
 =item B<--perl>
 
 If you select I<--perl> then you get Perl structures output which
@@ -183,6 +190,7 @@ GetOptions ("help|?" => \$help,
            "text" => sub { $output = "text" },
            "none" => sub { $output = "none" },
            "xml" => sub { $output = "xml" },
+           "yaml" => sub { $output = "yaml" },
            "perl" => sub { $output = "perl" },
            "fish" => sub { $output = "fish" },
            "guestfish" => sub { $output = "fish" },
@@ -222,6 +230,13 @@ if (-e $ARGV[0]) {
     die "cannot connect to libvirt $uri\n" unless $vmm;
 
     my @doms = $vmm->list_defined_domains ();
+    my $isitinactive = "an inactive libvirt domain";
+    if ($output ne "fish") {
+       # In the special case where we want read-only access to
+       # a domain, allow the user to specify an active domain too.
+       push @doms, $vmm->list_domains ();
+       $isitinactive = "a libvirt domain";
+    }
     my $dom;
     foreach (@doms) {
        if ($_->get_name () eq $ARGV[0]) {
@@ -229,8 +244,7 @@ if (-e $ARGV[0]) {
            last;
        }
     }
-    die "$ARGV[0] is not the name of an inactive libvirt domain\n"
-       unless $dom;
+    die "$ARGV[0] is not the name of $isitinactive\n" unless $dom;
 
     # Get the names of the image(s).
     my $xml = $dom->get_xml_description ();
@@ -831,14 +845,47 @@ sub check_for_modprobe_aliases
     local $_;
     my $root_dev = shift;
 
-    my @lines;
-    eval { @lines = $g->read_lines ("/etc/modprobe.conf"); };
-    return if $@ || !@lines;
+    # Initialise augeas
+    my $success = 0;
+    $success = $g->aug_init("/", 16);
+
+    # Register /etc/modules.conf and /etc/conf.modules to the Modprobe lens
+    my @results;
+    @results = $g->aug_match("/augeas/load/Modprobe/incl");
+
+    # Calculate the next index of /augeas/load/Modprobe/incl
+    my $i = 1;
+    foreach ( @results ) {
+        next unless m{/augeas/load/Modprobe/incl\[(\d*)]};
+        $i = $1 + 1 if ($1 == $i);
+    }
+
+    $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
+                           "/etc/modules.conf");
+    $i++;
+    $success = $g->aug_set("/augeas/load/Modprobe/incl[$i]",
+                                  "/etc/conf.modules");
+
+    # Make augeas reload
+    $success = $g->aug_load();
 
     my %modprobe_aliases;
 
-    foreach (@lines) {
-       $modprobe_aliases{$1} = $2 if /^\s*alias\s+(\S+)\s+(\S+)/;
+    for my $pattern qw(/files/etc/conf.modules/alias
+                       /files/etc/modules.conf/alias
+                       /files/etc/modprobe.conf/alias
+                       /files/etc/modprobe.d/*/alias) {
+        @results = $g->aug_match($pattern);
+
+        for my $path ( @results ) {
+            my $alias;
+            $alias = $g->aug_get($path);
+
+            my $modulename;
+            $modulename = $g->aug_get($path.'/modulename');
+
+            $modprobe_aliases{$alias} = $modulename;
+        }
     }
 
     $oses{$root_dev}->{modprobe_aliases} = \%modprobe_aliases;
@@ -924,6 +971,14 @@ elsif ($output eq "perl") {
     print Dumper(\%oses);
 }
 
+# YAML output
+elsif ($output eq "yaml") {
+    die "virt-inspector: no YAML support\n"
+       unless exists $INC{"YAML/Any.pm"};
+    
+    print Dump(\%oses);
+}
+
 # Plain text output (the default).
 elsif ($output eq "text") {
     output_text ();
@@ -1024,55 +1079,61 @@ sub output_text_os
 
 sub output_xml
 {
-    print "<operatingsystems>\n";
-    output_xml_os ($oses{$_}) foreach sort keys %oses;
-    print "</operatingsystems>\n";
+    my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
+
+    $xml->startTag("operatingsystems");
+    output_xml_os ($oses{$_}, $xml) foreach sort keys %oses;
+    $xml->endTag("operatingsystems");
+
+    $xml->end();
 }
 
 sub output_xml_os
 {
-    my $os = shift;
+    my ($os, $xml) = @_;
 
-    print "<operatingsystem>\n";
+    $xml->startTag("operatingsystem");
 
-    print "<os>", $os->{os}, "</os>\n" if exists $os->{os};
-    print "<distro>", $os->{distro}, "</distro>\n" if exists $os->{distro};
-    print "<version>", $os->{version}, "</version>\n" if exists $os->{version};
-    print "<root>", $os->{root_device}, "</root>\n";
+    foreach ( [ "name" => "os" ],
+              [ "distro" => "distro" ],
+              [ "version" => "version" ],
+              [ "root" => "root_device" ] ) {
+        $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
+    }
 
-    print "<mountpoints>\n";
+    $xml->startTag("mountpoints");
     my $mounts = $os->{mounts};
     foreach (sort keys %$mounts) {
-       printf "<mountpoint dev='%s'>%s</mountpoint>\n",
-         $mounts->{$_}, $_
+        $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
     }
-    print "</mountpoints>\n";
+    $xml->endTag("mountpoints");
 
-    print "<filesystems>\n";
+    $xml->startTag("filesystems");
     my $filesystems = $os->{filesystems};
     foreach (sort keys %$filesystems) {
-       print "<filesystem dev='$_'>\n";
-       print "<label>$filesystems->{$_}{label}</label>\n"
-           if exists $filesystems->{$_}{label};
-       print "<uuid>$filesystems->{$_}{uuid}</uuid>\n"
-           if exists $filesystems->{$_}{uuid};
-       print "<type>$filesystems->{$_}{fstype}</type>\n"
-           if exists $filesystems->{$_}{fstype};
-       print "<content>$filesystems->{$_}{content}</content>\n"
-           if exists $filesystems->{$_}{content};
-       print "</filesystem>\n";
+        $xml->startTag("filesystem", "dev" => $_);
+
+        foreach my $field ( [ "label" => "label" ],
+                            [ "uuid" => "uuid" ],
+                            [ "type" => "fstype" ],
+                            [ "content" => "content" ] ) {
+            $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
+                if exists $filesystems->{$_}{$field->[1]};
+        }
+
+        $xml->endTag("filesystem");
     }
-    print "</filesystems>\n";
+    $xml->endTag("filesystems");
 
     if (exists $os->{modprobe_aliases}) {
        my %aliases = %{$os->{modprobe_aliases}};
        my @keys = sort keys %aliases;
        if (@keys) {
-           print "<modprobealiases>\n";
+            $xml->startTag("modprobealiases");
            foreach (@keys) {
-               printf "<alias device=\"%s\">%s</alias>\n", $_, $aliases{$_}
+                $xml->dataElement("alias", $aliases{$_}, "device" => $_);
            }
-           print "</modprobealiases>\n";
+            $xml->endTag("modprobealiases");
        }
     }
 
@@ -1080,63 +1141,51 @@ sub output_xml_os
        my %modvers = %{$os->{initrd_modules}};
        my @keys = sort keys %modvers;
        if (@keys) {
-           print "<initrds>\n";
+            $xml->startTag("initrds");
            foreach (@keys) {
                my @modules = @{$modvers{$_}};
-               print "<initrd version=\"$_\">\n";
-               print "<module>$_</module>\n" foreach @modules;
-               print "</initrd>\n";
+                $xml->startTag("initrd", "version" => $_);
+                $xml->dataElement("module", $_) foreach @modules;
+                $xml->endTag("initrd");
            }
-           print "</initrds>\n";
+            $xml->endTag("initrds");
        }
     }
 
-    print "<applications>\n";
+    $xml->startTag("applications");
     my @apps =  @{$os->{apps}};
     foreach (@apps) {
-       print "<application>\n";
-       print "<name>$_->{name}</name><version>$_->{version}</version>\n";
-       print "</application>\n";
+        $xml->startTag("application");
+        $xml->dataElement("name", $_->{name});
+        $xml->dataElement("version", $_->{version});
+        $xml->endTag("application");
     }
-    print "</applications>\n";
+    $xml->endTag("applications");
 
-    print "<kernels>\n";
+    $xml->startTag("kernels");
     my @kernels = @{$os->{kernels}};
     foreach (@kernels) {
-       print "<kernel>\n";
-       print "<version>$_->{version}</version>\n";
-       print "<modules>\n";
+        $xml->startTag("kernel", "version" => $_->{version});
+        $xml->startTag("modules");
        my @modules = @{$_->{modules}};
        foreach (@modules) {
-           print "<module>$_</module>\n";
+            $xml->dataElement("module", $_);
        }
-       print "</modules>\n";
-       print "</kernel>\n";
+        $xml->endTag("modules");
+        $xml->endTag("kernel");
     }
-    print "</kernels>\n";
+    $xml->endTag("kernels");
 
     if (exists $os->{root}->{registry}) {
-       print "<windowsregistryentries>\n";
+        $xml->startTag("windowsregistryentries");
        # These are just lumps of text - dump them out.
        foreach (@{$os->{root}->{registry}}) {
-           print "<windowsregistryentry>\n";
-           print escape_xml($_), "\n";
-           print "</windowsregistryentry>\n";
+            $xml->dataElement("windowsregistryentry", $_);
        }
-       print "</windowsregistryentries>\n";
+        $xml->endTag("windowsregistryentries");
     }
 
-    print "</operatingsystem>\n";
-}
-
-sub escape_xml
-{
-    local $_ = shift;
-
-    s/&/&amp;/g;
-    s/</&lt;/g;
-    s/>/&gt;/g;
-    return $_;
+    $xml->endTag("operatingsystem");
 }
 
 =head1 QUERY MODE