#!/usr/bin/perl

#Copyright (C) 2015 SUSE LLC
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

use strict;
use warnings;

use XML::LibXML;
use Getopt::Long qw(:config no_ignore_case);
use JSON;

use AzureMetadata::AzureDisk;
use AzureMetadata::AzureNetwork;
use AzureMetadata::AzureGeneral;


sub main {
    my %options = parse_options();
    my %data;
    my $agent_conf;

    if ($options{tag}) {
        # VHD disk tag discovery
        $data{billingTag} = AzureDisk::getVHDDiskTag($options{device});
    }

    if ($options{cloudServiceName}) {
        # Cloud Service Name
        $agent_conf = AzureNetwork::import_config() if not defined $agent_conf;
        my $srvName = AzureNetwork::get_cloudservice_name($agent_conf);
        $data{cloudservice} = $srvName;
    }

    if ($options{external_ip}) {
        # Public External IP discovery
        $agent_conf = AzureNetwork::import_config() if not defined $agent_conf;
        my $ip = AzureNetwork::read_external_ip($agent_conf);
        $data{external_ip} = $ip;
    }

    if ($options{instance_name}) {
        # Instance Name
        $agent_conf = AzureNetwork::import_config() if not defined $agent_conf;
        my $name = AzureGeneral::get_instance_name($agent_conf);
        $data{instance_name} = $name;
    }

    if ($options{internal_ip}) {
        # Private Internal IP discovery
        $agent_conf = AzureNetwork::import_config() if not defined $agent_conf;
        my $ip = AzureNetwork::read_internal_ip($agent_conf);
        $data{internal_ip} = $ip;
    }

    print writeMetaData(\%data, $options{output})
}

sub parse_options {
    my %options;
    my $bareOut;
    my $cloudServiceName;
    my $device;
    my $external_ip;
    my $instance_name;
    my $internal_ip;
    my $tag;
    my $xmlOut;
    my $result = GetOptions(
        "bare|b"          => \$bareOut,
        "cloudservice|c"  => \$cloudServiceName,
        "device|d=s"      => \$device,
        "external-ip|e"   => \$external_ip,
        "instance-name|n" => \$instance_name,
        "internal-ip|i"   => \$internal_ip,
        "tag|t"           => \$tag,
        "xml|x"           => \$xmlOut
    );
    $options{bareOut}          = $bareOut;
    $options{cloudServiceName} = $cloudServiceName;
    $options{device}           = $device;
    $options{external_ip}      = $external_ip;
    $options{instance_name}    = $instance_name;
    $options{internal_ip}      = $internal_ip;
    $options{tag}              = $tag;
    $options{xmlOut}           = $xmlOut;
    if ( $result != 1 ) {
        usage();
        print STDERR "An invalid option was specified.";
        exit 1;
    }
    if ((! $options{cloudServiceName}) &&
        (! $options{external_ip}) &&
        (! $options{instance_name})  &&
        (! $options{internal_ip}) &&
        (! $options{tag})
    ) {
        usage();
        print STDERR "Please provide a query option.";
        exit 1;
    }
    if (! $options{device}) {
        $options{device} = '/dev/sda';
    }
    $options{output} = 'json';
    if ($options{xmlOut}) {
        $options{output} = 'xml';
    } elsif ($options{bareOut}) {
        $options{output} = 'bare';
    }
    return %options;
}

sub usage {
    print "Usage:\n";
    print "  azuremetadata \n";
    print "    --cloudservice | -c\n";
    print "      print the cloud service DNS name\n";
    print "\n";
    print "    --external-ip | -e\n";
    print "      print public external IPv4 address\n";
    print "\n";
    print "    --instance-name | -n\n";
    print "      print the instance's unique name\n";
    print "\n";
    print "    --internal-ip | -i\n";
    print "      print private internal IPv4 address\n";
    print "\n";
    print "    --tag | -t\n";
    print "      print vhd disk tag stored in boot record\n";
    print "\n";
    print "  options\n";
    print "    [ --device | -d <image file or disk device> ]\n";
    print "      Specify device for tag discovery. Default device\n";
    print "      is set to '/dev/sda'\n";
    print "    [ --xml | -x ]\n";
    print "      Wrap information in XML markup\n";
    print "    [ --bare | -b ]\n";
    print "      Just print information without markup\n";
    print "--\n";
}

sub writeMetaData {
    my $json_data = shift;
    my $output_style = shift;
    my $json_ref = JSON->new->allow_nonref;
    $json_ref -> pretty;
    my $json_text = $json_ref ->encode( $json_data );
    chomp $json_text;
    if ($output_style eq 'xml') {
        return "<document>".$json_text."</document>";
    } elsif ($output_style eq 'json') {
        return $json_text;
    } else {
        my @result = ();
        foreach my $value (values %{$json_data}) {
            push @result, $value;
        }
        return join ':', @result;
    }
}

main();
