#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/bin/cdat/cdat-show.pl 1.6 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2010,2011 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)27    1.6  src/bos/usr/bin/cdat/cdat-show.pl, cdat, bos720 7/15/11 05:38:04
use strict;
use warnings;
use Getopt::Long;
use Fcntl ':flock';	# LOCK_* constants
use XML::LibXML;
use cdat;
use messages;

#
# Constants.
#
my $XMLFILE = 'cdat.xml';

#
# Globals.
#
my $path;
my @collects;
# Default verbosity level is set to 0
my $verbose = 0;

######################################################################
# Function:	usage
# Purpose:	Display usage.
# Tasks:	Print usage and exit.
# Input:	None
# Output:	None
######################################################################
sub usage
{
    printf(STDERR catgets(MSG_CDAT_SHOW_USAGE,
        "Usage: cdat show -h\n".
	"       cdat show [-v]\n".
	"       cdat show [-v] Id\n".
	"       cdat show [-v] -p PMR\n".
	"       cdat show [-v] -n [Host]\n"));
    exit(1);
}

######################################################################
# Function:	show_id
# Purpose:	Show collect information for the specified collect id
#		or for all collects.
# Tasks:	Display collects information.
# Input:	list of collect XML elements, collect id or undef to
#		show all collects
# Output:	None
######################################################################
sub show_id
{
    my ($id) = @_;

    if (defined($id)) {
	# a collect id is specified, find it...
	my $found = 0;
	foreach my $collect (@collects) {
	    my $attr = $collect->getAttribute('id');
	    if (defined($attr) && $id eq $attr) {
	        # ... and display it.
		print_collect($collect);
		$found = 1;
		last;	# there can be only 1 collect with that id
	    }
	}
	if (!$found) {
	    printf(catgets(MSG_NO_COLLECT_FOUND_FOR_ID,
	        "No collect found for id %s.\n"), $id);
	}

    } else {
	# no collect id is specified, display all collects
	foreach my $collect (@collects) {
	    print_collect($collect);
	}
    }
}

######################################################################
# Function:	show_pmr
# Purpose:	Show collects information for the specified PMR.
# Tasks:	Search for collects with the specified PMR number and
#		display them.
# Input:	list of collect elements, PMR number
# Output:	None
######################################################################
sub show_pmr
{
    my ($pmr) = @_;
    my $found = 0;

    foreach my $collect (@collects) {
        my @pmrs = $collect->getElementsByTagName('pmr');
        next if (@pmrs == 0);	# no pmr tag for this collect, skip

	# retrieve value of first pmr tag
        my $val = $pmrs[0]->textContent;
	# skip if it does not match specified PMR number
	next if ($pmr ne $val);

        print_collect($collect);
	$found = 1;
	# NB: there can be several collects for a given PMR
    }
    if (!$found) {
        printf(catgets(MSG_NO_COLLECT_FOUND_FOR_PMR,
	    "No collect found for PMR %s.\n"), $pmr);
    }
}

######################################################################
# Function:	show_node
# Purpose:	Show collects information for a specified node or for
#		all nodes.
# Tasks:	Display collects information.
# Input:	list of collect XML elements, node name or "" for all
#		nodes.
# Output:	None
######################################################################
sub show_node
{
    my ($name) = @_;

    # ignore -v option with -n
    $verbose = 0;

    # get the list of collects per node (NB: $name can be '')
    my %list = get_collects_for_node($name);

    # display results
    while (my ($hostname, $collects) = each(%list)) {
        print("$hostname:\n\n");
        foreach my $collect (@$collects) {
	    print_collect($collect);
        }
    }
}

######################################################################
# Function:	get_nodes
# Purpose:	Retrieve the list of node XML elements for a given
#		XML file.
# Tasks:	Parse XML file and retrieve all node elements.
# Input:	XML filename
# Output:	Array of node XML elements
######################################################################
sub get_nodes
{
    my ($filename) = @_;
    my @nodes = ();

    my $parser = XML::LibXML->new();
    return @nodes if (!defined($parser));

    my $tree = $parser->parse_file($filename);
    return @nodes if (!defined($tree));

    my $root = $tree->getDocumentElement;
    return @nodes if (!defined($root));

    @nodes = $root->getElementsByTagName('node');
    return @nodes;
}

######################################################################
# Function:	get_collects_for_node
# Purpose:	Retrieve per-node XML collect elements
# Tasks:	Parse XML files and build a hash of node to XML
#		collect elements.
# Input:	node name or '' for all nodes.
# Output:	Hash of (node => array of XML collect elements)
######################################################################
sub get_collects_for_node
{
    my ($name) = @_;
    my %list;

    foreach my $collect (@collects) {
	# retrieve sub-directory of the collect
        my @locations = $collect->getElementsByTagName('location');
	next if (@locations == 0);	# should not happen
        my $location = $locations[0]->textContent;

	# retrieve the list of nodes from the XML file
	my @nodes = get_nodes("$path/$location/collect.xml");

        foreach my $node (@nodes) {
            my $hostname = $node->getAttribute('hostname');
	    next if (!defined($hostname));	# should not happen
	    # skip nodes that do not match query
	    next if ($name ne '' && $name ne $hostname);

	    if (!defined($list{$hostname})) {
		$list{$hostname} = [ $collect ];
            } else {
		push(@{ $list{$hostname} }, $collect);
            }
        }
    }
    return %list;
}

######################################################################
# Function:	print_node
# Purpose:	Display information about a node.
# Tasks:	Display information about a node (name, machine id...)
# Input:	node XML element
# Output:	None
######################################################################
sub print_node
{
    my ($node) = @_;

    my $hostname = $node->getAttribute('hostname');
    return if (!defined($hostname));	# should not happen

    print("\n    $hostname:\n");

    my @types = $node->getElementsByTagName('node-type');
    if (@types != 0) {
	printf("\t%-11s: %s\n", catgets(MSG_TYPE, "type"),
	    $types[0]->textContent);
    }

    my @users = $node->getElementsByTagName('user');
    if (@users != 0) {
	printf("\t%-11s: %s\n", catgets(MSG_USER, "user"),
	    $users[0]->textContent);
    }

    my @machine_ids = $node->getElementsByTagName('machine_id');
    if (@machine_ids != 0) {
	printf("\t%-11s: %s\n", catgets(MSG_MACHINE_ID, "machine id"),
	    $machine_ids[0]->textContent);
    }

    my @lpar_ids = $node->getElementsByTagName('lpar_id');
    if (@lpar_ids != 0) {
	printf("\t%-11s: %s\n", catgets(MSG_LPAR_ID, "lpar id"),
	    $lpar_ids[0]->textContent);
    }

    my @timezones = $node->getElementsByTagName('timezone');
    if (@timezones != 0) {
	printf("\t%-11s: %s\n", catgets(MSG_TIMEZONE, "timezone"),
	    $timezones[0]->textContent);
    }
}

######################################################################
# Function:	print_collect
# Purpose:	Display information about a collect.
# Tasks:	Display information about a collect and about the
#		nodes this collect was run on if verbosity is > 0.
# Input:	collect XML element
# Output:	None
######################################################################
sub print_collect
{
    my ($collect) = @_;

    my $id = $collect->getAttribute('id');
    return if (!defined($id));	# should not happen

    print("$id:");

    my @dates = $collect->getElementsByTagName('date');
    if (@dates) {
        print(" " . $dates[0]->textContent);
    }
    print("\n\n");

    my @descriptions = $collect->getElementsByTagName('description');
    if (@descriptions) {
        my $descr = $descriptions[0]->textContent;
        print("    $descr\n");
    }

    my @pmrs = $collect->getElementsByTagName('pmr');
    if (@pmrs) {
        printf(catgets(MSG_PMR, "    PMR: %s\n"),
	    $pmrs[0]->textContent);
    }

    my @locations = $collect->getElementsByTagName('location');
    my $location;
    if (@locations) {
        $location = $locations[0]->textContent;
	printf(catgets(MSG_LOCATION, "    Location: %s\n"),
	    "$path/$location");
    }

    if ($verbose != 0 && defined($location)) {
	my @nodes = get_nodes("$path/$location/collect.xml");
	foreach my $node (@nodes) {
	    print_node($node);
	}
    }
    print("\n");
}

######################################################################
# Function:	main
# Purpose:	Entry point of the show subcommand.
# Tasks:	Parse command line and print collects matching query.
# Input:	None
# Output:	None
######################################################################
sub main
{
    my ($rc, $pmr, $node, $id);

    # Parse command line options
    Getopt::Long::Configure('bundling', 'no_ignore_case');
    $rc = GetOptions(
	'h'   => \&usage,
	'v+'  => \$verbose,
	'p=s' => \$pmr,
	'n:s' => \$node
	);
    if (!$rc) {
	usage();
    }

    if (!defined($pmr) && !defined($node)) {
        # neither -p nor -n is specified, check if a collectid is specified
        if (@ARGV > 0) {
	    $id = shift @ARGV;
	}
    }
    # make sure there are no extra arguments
    usage if (@ARGV != 0);

    # validate PMR number
    if (defined($pmr) && !($pmr =~ m/\d{5,}(,[0-9A-Za-z]{3,}){2}/)) {
	printf(STDERR catgets(MSG_INVALID_PMR,
	    "Invalid PMR '%s', format should be 'PMR#,BRANCH#,COUNTRY#'.\n"), $pmr);
	exit(1);
    }

    $path = cdat::odm_get_path();
    if (!defined($path)) {
	printf(STDERR catgets(MSG_DIR_NOT_DEFINED,
	    "The cdat directory path is not defined.\n".
	    "Please, run 'cdat init' first.\n"));
	exit(2);
    }

    cdat::switch_user();

    my $fh;
    if (!open($fh, "<", "$path/$XMLFILE")) {
	printf(STDERR catgets(MSG_CANNOT_OPEN,
	    "Cannot open %s.\n"), "$path/$XMLFILE");
	exit(2);
    }
    # Try to acquire read-only access to the XML file (but do not block)
    if (!flock($fh, LOCK_SH | LOCK_NB)) {
        printf(STDERR catgets(MSG_BUSY,
	    "Another instance of the cdat command is running.\n".
	    "Please wait until this instance terminates.\n"));
        exit(2);
    }

    my $parser = XML::LibXML->new();
    if (!defined($parser)) {
        printf(STDERR catgets(MSG_CANNOT_CREATE_XML_PARSER,
	    "Cannot create XML parser.\n"));
        exit(2);
    }

    my $tree = $parser->parse_fh($fh);
    if (!defined($tree)) {
        printf(STDERR catgets(MSG_CANNOT_PARSE,
	    "Cannot parse %s.\n"), "$path/$XMLFILE");
	exit(2);
    }

    printf(catgets(MSG_REPOSITORY , "Repository: %s\n"), $path);
    printf(catgets(MSG_LOCAL_USER , "Local user: %s\n"), cdat::odm_get_user());
    printf("\n");

    my $root = $tree->getDocumentElement;

    @collects = $root->getElementsByTagName('collect');

    if (defined($pmr)) {
        # -p is specified, display all collects for specified PMR number
	show_pmr($pmr);
    } elsif (defined($node)) {
        # -n is specified, display collects by nodes
	show_node($node);
    } else {
        # neither -p nor -n is specified, display collects by id
	show_id($id);
    }
}

main;