package Net::SolarWinds::REST;

=head1 NAME

Net::SolarWinds::REST - SolarWinds Rest interface

=head1 SYNOPSIS

  use strict;
  use warnings;
  use Net::SolarWinds::REST;
  use Data::Dumper;

  my $rest=new Net::SolarWinds::REST();

  my $result=$rest->DiscoverInterfacesOnNode(444);

  if($result) {
    print Dumper($result->get_data);
  } else {
    print $result,"\n";
  }

=head1 DESCRIPTION

Perl Module for interfacing with SolarWinds.

=cut

use 5.010001;
use strict;
use warnings;
use Data::Dumper;
use IO::Socket::SSL;
use LWP::UserAgent;
use HTTP::Request;
use MIME::Base64 qw();
use JSON qw();
use URI::Encode qw(uri_encode);

our $VERSION=0.04;

use base qw(Net::SolarWinds::ConstructorHash Net::SolarWinds::LogMethods Net::SolarWinds::Helper);
use constant RESULT_CLASS=>'Net::SolarWinds::Result';
use Net::SolarWinds::Result;

=head1 GLOBAL OBJECTS

=over 4

=item * $Net::SolarWinds::REST::JSON

This is a JSON object with the following options endabled: JSON->new->allow_nonref->utf8

=cut

# Global JSON OBJECT
our $JSON=JSON->new->allow_nonref->utf8;

=item * my $json=$class->get_json;

Returns the class level JSON object.

=cut

sub get_json { return $JSON; }

=item * $Net::SolarWinds::REST::UA

This is a LWP::UserAgent used to talk to CPM servers

=cut

# Global UA object
our $UA=LWP::UserAgent->new;
# Turn off SSL Verification
$UA->ssl_opts( 
  SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE, 
  SSL_hostname => '', 
  verify_hostname => 0 
);


=item * my $ua=$class->get_ua;

Returns the Class level UA object.

=cut

sub get_ua { return $UA; }

=back

=cut

=head1 Class Constants

The following constants are accessable via: Net::SolarWinds::REST->CONSTANT

  DEFAULT_USER=>'admin';
  DEFAULT_PASS=>'ChangeMe';
  DEFAULT_SERVER=>'SolarWindsServer';
  DEFAULT_PORT=>17778;
  DEFAULT_PROTO=>'https';
  BASE_URI=>'%s://%s:%i/SolarWinds/InformationService/v3/Json/%s';

=cut

use constant DEFAULT_USER=>'admin';
use constant DEFAULT_PASS=>'ChangeMe';
use constant DEFAULT_SERVER=>'SolarWindsServer';
use constant DEFAULT_PORT=>17778;
use constant DEFAULT_PROTO=>'https';
use constant BASE_URI=>'%s://%s:%i/SolarWinds/InformationService/v3/Json/%s';

=head2 OO Methods

This section covers the OO Methods of the class.

=over 4

=item * Object Constructor.

The object constructor takes key=>value pairs all aguments are optional, default values are pulled from the constants DEFAULT_*.

  my $sw=new Net::SolarWinds::REST(
    USER   =>'User',
    PASS   =>'Passxxx',
    SERVER =>'SolarwindsServer',
    PORT   =>17778,
    PROTO  =>'https',
    # Logging is not enabled by default!
    log=>  Net::SolarWinds::Log->new('/var/log/Solarwinds.log')
  );

=cut

sub new {
  my ($class,%args)=@_;

  foreach my $key (qw(USER PASS SERVER PORT PROTO)) {
    my $method="DEFAULT_$key";
    next if exists $args{$key};
    $args{$key}=$class->$method;
  }
  
  my $self=$class->SUPER::new(%args);

  $self->{header}=[
    Authorization=>'Basic '.MIME::Base64::encode_base64($self->{USER} . ':' . $self->{PASS}),
    'Content-Type' => 'application/json',
  ];
  return $self;
}

=item * my $request=$self->build_request('GET|POST|PUT|DELETE',$path,undef|$ref);

Creates an HTTP::Request object with the default options set.

=cut

sub build_request {
  my ($self,$method,$path,$data)=@_;
  my $uri=sprintf $self->BASE_URI,@{$self}{qw(PROTO SERVER PORT )},$path;

  my $content=undef;
  my $headers=[@{$self->{header}}];
  if(defined($data)) {
    $content=$JSON->encode($data);
    push @{$headers},'Content-Length'=>length($content);
  }
  my $request=HTTP::Request->new($method,$uri,$headers,$content);
  $self->log_debug($request->as_string);
  return $request;
}

=item * my $result=$self->run_request($request);

Takes a given HTTP::Request object and runs it returing a Net::SolarWinds::Result object.
What the object contains is relative to the request run..  If the result code of the request
was not a 20x value then the object is false.

=cut

sub run_request {
  my ($self,$request)=@_;
  my $response=$UA->request($request);
  $self->log_debug($response->as_string);
  my $content=$response->decoded_content;
  if($response->is_success) {
    if($content=~ /^\s*[\[\{]/s) {
      my $data=eval {$JSON->decode($content)};
      if($@) {
        return $self->RESULT_CLASS->new_false("Code: [".$response->code."] JSON Decode error [$@] Content:  $content",$response);
      } else {
        return $self->RESULT_CLASS->new_true($data,$response);
      }
    } else {
      return $self->RESULT_CLASS->new_true($content,$response);
    }
  } else {
    return $self->RESULT_CLASS->new_false("Code: [".$response->code."] http error [".$response->status_line."] Content:  $content",$response);
  }
}

=item * my $result=$self->DiscoverInterfacesOnNode($nodeId)

Returns a Net::SolarWinds::Result Object: When true it contains the results, when false it contains the error.

=cut

sub DiscoverInterfacesOnNode {
  my ($self,$nodeId)=@_;

  # force a string;
  $nodeId .='';

  my $request=$self->build_request('POST','Invoke/Orion.NPM.Interfaces/DiscoverInterfacesOnNode',[$nodeId]);
  my $result=$self->run_request($request);

  return $result;
}

=item * my $result=$self->DiscoverInterfaceMap($nodeId);

Returns a Net::SolarWinds::Result object:

When true it contains an anonymous hash that maps interface objects to interface names.
When false it contains why it failed.

=cut

sub DiscoverInterfaceMap {
  my ($self,$node_id)=@_;

  $self->log_info("starting node_id: $node_id");
  my $result=$self->DiscoverInterfacesOnNode($node_id);
  $self->log_info("stopping");
  return $self->build_interface_result_map($result);
}

sub build_interface_result_map {
  my ($self,$result)=@_;

  $self->log_info("starting");
  unless($result) {
    $self->log_error("failed to build interface map error was: $result");
    $self->log_info("stopping");
    return $result;
  }
  my $list=$result->get_data->{DiscoveredInterfaces};

  my $map={};
  foreach my $int (@{$list}) {
     my $caption=$int->{Caption};
     my @list=$self->InterfaceCaptionTranslation($caption);
     foreach my $ifname (@list) {
       $map->{$ifname}=$int;
     }
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($map,$list);
}

=item * my @names=$self->InterfaceCaptionTranslation($Caption);

Takes an interface caption and converts it to a list of valid interface names that should match what is on a given device.

=cut

sub InterfaceCaptionTranslation {
  my ($self,$caption)=@_;

  $self->log_info("starting");
  my @list;
  if($caption=~ s/\s\\x\{b7\}.*$//s > 0) {
    push @list,$caption;
  } else {
    push @list,split /\s+-\s+/,$caption;
  }

  $self->log_info("stopping");
  return @list;
}

=item * my $result=$self->NodeInterfaceAddDefaultPollers($nodeId,$interfaec_ref);

Returns a Net::SolarWinds::Result Object: When true it contains the results, when false it contains the error.
$interface_ref represents object listed from the DiscoverInterfacesOnNode that need to be added to the default pollers.

=cut

sub NodeInterfaceAddDefaultPollers {
  my ($self,$nodeId,$data)=@_;

  $self->log_info("starting");
  # force a string;
  $nodeId .='';
  
  my $request=$self->build_request('POST','Invoke/Orion.NPM.Interfaces/AddInterfacesOnNode',[$nodeId,$data,'AddDefaultPollers']);
  my $result=$self->run_request($request);

  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->NodeAddInterface($node_id,[$interface]);

Adds an interface to the cpm for the node but does not add any pollers.

=cut

sub NodeAddInterface {
  my ($self,$node_id,$data)=@_;

  $self->log_info("starting");
  my $request=$self->build_request('POST','Invoke/Orion.NPM.Interfaces/AddInterfacesOnNode',[$node_id,$data,'AddNoPollers']);
  my $result=$self->run_request($request);
  $self->log_info("stopping");

  return $result;
}

=item * my $result=$self->NodeInterfaceCustomProperties($nodeId,$interfaceId,$hash_ref|undef);

Used to get or set custom properties of an interfaec on a node.
Returns a Net::SolarWinds::Result Object: When true it contains the results, when false it contains the error.

=cut

sub NodeInterfaceCustomProperties {
  my ($self,$nodeId,$interfaceId,$data)=@_;

  my $request;
  $self->log_info("starting");
  if($data) {
    $request=$self->build_request('POST',"swis://localhost/Orion/Orion.Nodes/NodeID=$nodeId/Interfaces/InterfaceID=$interfaceId/CustomProperties",$data);
  } else {
    $request=$self->build_request('GET',"swis://localhost/Orion/Orion.Nodes/NodeID=$nodeId/Interfaces/InterfaceID=$interfaceId/CustomProperties");
  }
  my $result=$self->run_request($request);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getInterfacesOnNode($NodeID);

Returns a Net::SolarWinds::Result Object

when true: Gets the interfaces from the node
when false: returns why it failed

=cut

sub getInterfacesOnNode {
  my ($self,$node_id)=@_;
  my $query=qq{SELECT Caption, InterfaceID, DisplayName, FullName, ifname, interfacetype as ifType FROM Orion.NPM.Interfaces where NodeID=$node_id}; 

  $self->log_info("starting $node_id");
  my $result=$self->Query($query);

  return $result unless $result;

  my $list=$result->get_data->{results};
  my $ints={};

  foreach my $int (@{$list}) {
    my @list=$self->InterfaceCaptionTranslation($int->{Caption});
    foreach my $ifname (@list) {
      $ints->{$ifname}=$int;
    }
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($ints);
}

=item * my $result=$self->NodeCustomProperties($nodeId,$interfaceId,$hash_ref|undef);

Used to get or set custom properties of an interfaec on a node.
Returns a Net::SolarWinds::Result Object: When true it contains the results, when false it contains the error.

=cut

sub NodeCustomProperties {
  my ($self,$nodeId,$data)=@_;


  my $request;
  if($data) {
    
    $self->log_info("starting node_id: $nodeId mode: POST");
    $request=$self->build_request('POST',"swis://localhost/Orion/Orion.Nodes/NodeID=$nodeId/CustomProperties",$data);
  } else {
    $self->log_info("starting node_id: $nodeId mode: GET");
    $request=$self->build_request('GET',"swis://localhost/Orion/Orion.Nodes/NodeID=$nodeId/CustomProperties");
  }
  my $result=$self->run_request($request);
  $self->log_info("stopping");
  
  return $result;
}

=item * my $result=$self->Query($sql);

Used to run an sql query against CPM.
Returns a Net::SolarWinds::Result Object: When true it contains the results, when false it contains the error.

=cut

sub Query {
  my ($self,$sql)=@_;
  $self->log_info("starting");
  $self->log_debug("$sql");
  my $path='Query?query='.uri_encode($sql);
  my $request=$self->build_request('GET',$path);
  my $result=$self->run_request($request);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getNodesByIp($ip);

Find a list of nodes by a given ip.
Returns a Net::SolarWinds::Result Object: When true it contains an array ref of the results, when false it contains the error.

=cut

sub getNodesByIp {
  my ($self,$ip)=@_;
  $self->log_info("starting $ip");
  my $query=qq{SELECT
  NodeID,
  IPAddress,
  IPAddressGUID,
  Caption,
  DynamicIP,
  EngineID,
  Status,
  UnManaged,
  Allow64BitCounters,
  ObjectSubType,
  SysObjectID,
  MachineType,
  VendorIcon,
  SNMPVersion,
  Community,
  RediscoveryInterval,
  PollInterval,
  StatCollection,
  Uri,
  DisplayName
  FROM Orion.Nodes where IP_Address='$ip'
  };
  my $result=$self->Query($query);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getNodesByDisplayName($hostname);

Find a list of nodes by a given hostname.
Returns a Net::SolarWinds::Result Object: When true it contains an array ref of the results, when false it contains the error.

=cut

sub getNodesByDisplayName {
  my ($self,$ip)=@_;
  $self->log_info("starting $ip");
  my $query=qq{SELECT
  NodeID,
  IPAddress,
  IPAddressGUID,
  Caption,
  DynamicIP,
  EngineID,
  Status,
  UnManaged,
  Allow64BitCounters,
  ObjectSubType,
  SysObjectID,
  MachineType,
  VendorIcon,
  SNMPVersion,
  Community,
  RediscoveryInterval,
  PollInterval,
  StatCollection,
  Uri,
  DisplayName
  FROM Orion.Nodes where DisplayName='$ip' OR Caption='$ip'
  };
  my $result=$self->Query($query);
  $self->log_info("stopping");
  return $result;
}
sub getNodesByID {
  my ($self,$id)=@_;
  $self->log_info("starting");
  my $query=qq{SELECT
  NodeID,
  IPAddress,
  IPAddressGUID,
  Caption,
  DynamicIP,
  EngineID,
  Status,
  UnManaged,
  Allow64BitCounters,
  ObjectSubType,
  SysObjectID,
  MachineType,
  VendorIcon,
  SNMPVersion,
  Community,
  RediscoveryInterval,
  PollInterval,
  StatCollection,
  Uri
  FROM Orion.Nodes where NodeId='$id'
  };
  my $result=$self->Query($query);

  $self->log_info("stopping");
  return $result;
}
=item * my $result=$self->createNode(key=>value);

Creates a node.
Returns a Carter::Result Object: Retuns a data structure on sucess returns why it faield on false.

=cut

sub createNode {
  my ($self,%args)=@_;

  $self->log_info("starting");
  # Caption 
  %args=(
    qw(
      ObjectSubType      SNMP
      EntityType         Orion.Nodes
      DynamicIP          false
      EngineID           1
      Status             1
      UnManaged          false
      Allow64BitCounters true
      ObjectSubType      SNMP
      SNMPVersion        2
      Community          public
      VendorIcon         8072.gif
      NodeDescription    Hardware

    ),
      IOSImage=>"",
      IOSVersion=>"",
      Pollinterval=>60,
      SysObjectID=>"1.3.6.1.4.1.8072.3.2.10",
      MachineType=>"net-snmp - Linux",
      StatCollection=>10,
      CPULoad=>"-2",
      MemoryUsed=>"-2",
      PercentMemoryUsed=>"-2",
      BufferNoMemThisHour=>"0",
      BufferNoMemToday=>"0",
      BufferSmMissThisHour=>"0",
      BufferSmMissToday=>"0",
      BufferMdMissThisHour=>"0",
      BufferMdMissToday=>"0",
      BufferBgMissThisHour=>"0",
      BufferBgMissToday=>"0",
      BufferLgMissThisHour=>"0",
      BufferLgMissToday=>"0",
      BufferHgMissThisHour=>"0",
      BufferHgMissToday=>"0",
      %args
  );
  unless(exists $args{IPAddress}) {
    $self->log_error("IPAddress must be set");
    $self->log_info("stopping");
    return $self->RESULT_CLASS->new_false("IPAddress must be set");
  }

  # start building our required but often times missing key value pairs
  $args{IPAddressGUID}=$self->ip_to_gui($args{IPAddress}) unless exists $args{IPAddressGUID};
  $args{Caption}=$self->ip_to_reverse_hex($args{IPAddress}) unless exists $args{Caption};


  my $path='Create/Orion.Nodes';
  my $request=$self->build_request('POST',$path,{%args});
  my $result=$self->run_request($request);

  unless($result) {
    $self->log_error("Failed to create node error was: $result");
    $self->log_info("stopping");
    return $result;
  }
  my $swis=$result->get_data;
  $swis=~ s/(?:^"|"$)//sg;
  my ($node_id)=$swis=~ /(\d+)$/s;

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true({Uri=>$swis,NodeID=>$node_id});
}

=item * my $result=$self->getNodeUri($node_id);

When true the Net::SolarWinds::Result object contains the node uri.
When false it contains why it failed.

=cut

sub getNodeUri {
  my ($self,$node_id)=@_;

  $self->log_info("starting");
  my $query="Select Uri from Orion.Nodes where NodeId=$node_id";

  my $result=$self->Query($query);

  unless($result) { 
    $self->log_error("could not get node uri error was: $result");
    $self->log_info("stopping");
    return $result;
  }

  unless($#{$result->get_data->{results}} >-1) {
    $self->log_error("NodeId: $node_id not found!");
    $self->log_info("stopping");
    return $self->RESULT_CLASS->new_false("NodeId: $node_id not found!") 
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($result->get_data->{results}->[0]->{Uri});


}

=item * my $result=$self->deleteSwis($uri);

Returns a charter result object showing status.

=cut

sub deleteSwis {
  my ($self,$uri)=@_;
  $self->log_info("starting");
  my $request=$self->build_request('DELETE',$uri);
  my $result=$self->run_request($request);
  $self->log_info("stopping");

  return $result;
}

=item * my $result=$self->deleteNode($node_id);

Deletes a given node.

=cut

sub deleteNode {
  my ($self,$node_id)=@_;
  my $path;

  $self->log_info("starting");
  if(my $result=$self->getNodeUri($node_id)) {
    $path=$result->get_data;
  } else {
    $self->log_error("Failed to delete node: $node_id error was: $result");
    $self->log_info("stopping");
    return $result;
  }
  my $request=$self->build_request('DELETE',$path);
  my $result=$self->run_request($request);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getApplicationTemplate(@names);

This is a wrapper for the Query interface.  Returns the results that match applications by this name.

=cut

sub getApplicationTemplate {
  my ($self,@names)=@_;

  $self->log_info("starting");
  my $append=join ' OR ',map { sprintf q{Name='%s'},$_ } @names;
  my $query=qq{SELECT ApplicationTemplateID, Created, CustomApplicationType, Description, DisplayName, HasImportedView, InstanceType, IsMockTemplate, LastModified, Name, Uri, ViewID, ViewXml FROM Orion.APM.ApplicationTemplate where $append};
 
  my $result=$self->Query($query);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->addTemplateToNode($node_id,$template_id);
=item * my $result=$self->addTemplateToNode($node_id,$template_id,$cred_id);

Adds a monitoring template with the default credentals to the node.
Returns true on success false on failure.

=cut

sub addTemplateToNode {
  my ($self,$node_id,$template_id,$cred_id)=@_;
  $cred_id=-4 unless defined($cred_id);
  $self->log_info("starting node_id: $node_id template_id: $template_id credential_id: $cred_id");
  my $request=$self->build_request(
    'POST',
    'Invoke/Orion.APM.Application/CreateApplication',
    [$node_id,$template_id,$cred_id,JSON::true]
  );
  my $result=$self->run_request($request);
  unless($result) {
    $self->log_error("Failed to addTemplateToNode error was: $result");
    $self->log_info("stopping");
    return $result;
  }

  if($result->get_data == -1) {
    my $msg="TemplateID: $template_id all ready exists on node";
    $self->log_error($msg);
    $self->log_info("stopping");
    return $self->RESULT_CLASS->new_false($msg);
  }

  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getTemplatesOnNode($node_id);

Returns a Net::SolarWinds::Result object
When true it contains the templates on the node
when false it contains why it failed.

=cut

sub getTemplatesOnNode {
  my ($self,$node_id)=@_;
    $self->log_info("starting");
  my $query=qq{SELECT ApplicationID, ApplicationTemplateID, Description, DetailsUrl, DisplayName, HasCredentials, ID, InstanceType, Name, NodeID, Uri FROM Orion.APM.Application where nodeid=$node_id};
  my $result=$self->Query($query);
  unless($result) {
    $self->log_error("failed to getTemplatesOnNode");
    $self->log_info("stopping");
    return $result;
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($result->get_data->{results});
}

=item * my $result=$self->UpdateNodeProps($nodeID,key=>values);

Has 2 use cases Read/Write: When called with just the $nodeID the result object is populated with the node properties.  When called with a hash the given values are pushed to the node.  Returns a Net::SolarWinds::Result object: true on success false on failure.

=cut

sub UpdateNodeProps {
  my ($self,$nodeID,%args)=@_;

  $self->log_info("starting node_id: $nodeID");
  my $uri='swis://localhost/Orion/Orion.Nodes/NodeID='.$nodeID;

  my $request; 
  if(scalar(keys(%args))==0) {
    $self->log_info("called in Read mode");
    $request=$self->build_request('GET',$uri);
  } else {
    $self->log_info("called in Write mode");
    $request=$self->build_request('POST',$uri,{%args});
  }
  my $result=$self->run_request($request);
  $self->log_info("stopping $nodeID");
  return $result;
}


=item * my $result=$self->AddPollerToNode($nodeID,$Poller);

Adds a poller to a node.

=cut


sub AddPollerToNode {
  my ($self,$node_id,$poller)=@_;

  $self->log_info("starting node_id: $node_id poller: $poller");
  my $result=$self->add_poller($node_id,'N',$poller);
  $self->log_info("stopping node_id: $node_id poller: $poller");
  return $result;
}

=item * my $result=$self->add_poller($node_id,$t,$poller)

Returns a Net::SolarWinds::Result object when true it returns the result information that shows the results of the poller being added.

=cut

sub add_poller {
  my ($self,$node_id,$t,$poller)=@_;

  $self->log_info("starting object_id: $node_id type: $t poller: $poller");

  my $json={
    PollerType=>$poller,
    NetObjectType=>$t,
    NetObject=>$t.':'.$node_id,
    NetObjectID=>$node_id,
  };

  my $request=$self->build_request('POST','Create/Orion.Pollers',$json);
  my $result=$self->run_request($request);
  unless($result) {
    $self->log_error("failed to add poller to node_id: $node_id type: $t poller: $poller error was: $result");
    $self->log_info("stopping");
    return $result;
  }
  my $url=$result->get_data;
  $url=~ s/(?:^"|"$)//g;

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($url);
}

=item * my $result=$self->add_volume(key=>value);

Creates a volume given the arguments passed in.

Returns a Net::SolarWinds::Result object:
When false it contains why it failed
When true it returns a swis uri

=cut

sub add_volume {
  my ($self,%args)=@_;

  $self->log_info("starting");
  my $json={
    VolumeIndex=>2,
    Caption=>"/",
    VolumeDescription=>"/",
    Status=>1,
    VolumeType=>"Fixed Disk",
    VolumeTypeIcon=>"FixedDisk.gif",
    VolumeSpaceAvailable=>0,
    VolumeSize=>0,
    VolumePercentUsed=>0,
    VolumeSpaceUsed=>0,
    VolumeTypeID=>4,
    PollInterval=>240,
    StatCollection=>15,
    RediscoveryInterval=>30,
   ,%args
  };

  my $request=$self->build_request('POST','Create/Orion.Volumes',$json);
  my $result=$self->run_request($request);

  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->getVolumeTypeMap;

Returns a Net::SolarWinds::Result Object
When true Returns the volume type map.
When false it returns why it failed.

=cut

sub getVolumeTypeMap {
  my ($self)=@_;

  $self->log_info("starting");
  my $query=q{SELECT distinct VolumeType, VolumeTypeIcon, VolumeTypeID FROM Orion.Volumes};
  my $result=$self->Query($query);
  unless($result) {
    $self->log_error("Failed to get getVolumeTypeMap error was: $result");
    $self->log_info("stopping");
    return $result;
  }

  my $list=$result->get_data->{results};

  my $map={};
  foreach my $type (@{$list}) {
    $map->{$type->{VolumeType}}=$type;
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($map);
}


=item * my $result=$self->getEngines;

Returns a Net::SolarWinds::Result object:
When true it contains the list of poller engins.
When false it contains why it failed.

=cut

sub getEngines {
  my ($self)=@_;

  my $result=$self->Query(q{
SELECT AvgCPUUtil, BusinessLayerPort, CompanyName, CustomerID, Description, DisplayName, Elements, EngineID, EngineVersion, EvalDaysLeft, Evaluation, FailOverActive, FIPSModeEnabled, InstanceType, InterfacePollInterval, Interfaces, InterfaceStatPollInterval, IP, KeepAlive, LicensedElements, LicenseKey, MaxPollsPerSecond, MaxStatPollsPerSecond, MemoryUtil, MinutesSinceFailOverActive, MinutesSinceKeepAlive, MinutesSinceRestart, MinutesSinceStartTime, MinutesSinceSysLogKeepAlive, MinutesSinceTrapsKeepAlive, NodePollInterval, Nodes, NodeStatPollInterval, PackageName, Pollers, PollingCompletion, PrimaryServers, Restart, SerialNumber, ServerName, ServerType, ServicePack, StartTime, StatPollInterval, SysLogKeepAlive, TrapsKeepAlive, Uri, VolumePollInterval, Volumes, VolumeStatPollInterval, WindowsVersion FROM Orion.Engines
  });

  return $result;
}

=item * my $result=$self->getEngine($engine);

Returns a Net::SolarWinds::Result Object:
When true it contains the list of matching engines.
When false it cointains why it failed.

Notes: if no matching engines were found the result object can be true.. but will not contain any data.

=cut

sub getEngine {
  my ($self,$engine)=@_;

  my $result=$self->Query(qq{
SELECT AvgCPUUtil, BusinessLayerPort, CompanyName, CustomerID, Description, DisplayName, Elements, EngineID, EngineVersion, EvalDaysLeft, Evaluation, FailOverActive, FIPSModeEnabled, InstanceType, InterfacePollInterval, Interfaces, InterfaceStatPollInterval, IP, KeepAlive, LicensedElements, LicenseKey, MaxPollsPerSecond, MaxStatPollsPerSecond, MemoryUtil, MinutesSinceFailOverActive, MinutesSinceKeepAlive, MinutesSinceRestart, MinutesSinceStartTime, MinutesSinceSysLogKeepAlive, MinutesSinceTrapsKeepAlive, NodePollInterval, Nodes, NodeStatPollInterval, PackageName, Pollers, PollingCompletion, PrimaryServers, Restart, SerialNumber, ServerName, ServerType, ServicePack, StartTime, StatPollInterval, SysLogKeepAlive, TrapsKeepAlive, Uri, VolumePollInterval, Volumes, VolumeStatPollInterval, WindowsVersion FROM Orion.Engines where ServerName='$engine' or DisplayName='$engine'
  });

  return $result unless $result;
}
=item * my $result=$self->getVolumeMap($nodeID);

Returns a Net::SolarWinds::Result object:
When true it contains a hash that maps volumes to objects.
When false it returns why it failed.

=cut 

sub getVolumeMap {
  my ($self,$node_id)=@_;

  $self->log_info("starting");
  my $query=qq{SELECT 
  
    VolumeIndex,
    Caption,
    VolumeDescription,
    Status,
    Type ,
    Icon ,
    VolumeSpaceAvailable,
    VolumeSize,
    VolumePercentUsed,
    VolumeSpaceUsed,
    VolumeTypeID,
    PollInterval,
    StatCollection,
    RediscoveryInterval,
    VolumeID,
    NodeID
  
  FROM Orion.Volumes where NodeID=$node_id order by VolumeIndex desc};
  my $result=$self->Query($query);
  unless($result) {
    $self->log_error("Failed to get getVolumeMap for node_id: $node_id error was: $result");
    $self->log_info("stopping");
    return $result;
  }

  my $list=$result->get_data->{results};
  my $map={};

  # set our max volume index to 1 if we don't have any volumes on this node
  my $MaxVolumeIndex=$#{$list} > -1 ? $list->[0]->{VolumeIndex} : 1;

  foreach my $vol (@{$list}) {
    # assume no duplicates.. 
    $map->{$vol->{Caption}}=$vol;
  }

  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true({'map'=>$map,MaxVolumeIndex=>$MaxVolumeIndex});

}

=item * my $result=$self->getSwisProps($uri);

Returns a Net::SolarWinds::Result object:
When true it the uri is set in the result hash
when false the explanation as to why it failed is gven.

=cut

sub getSwisProps {
  my ($self,$uri)=@_;
  $self->log_info("starting");
  my $request=$self->build_request('GET',$uri);
  my $result=$self->run_request($request);
  $self->log_info("stopping");
  return $result;
}

=item * my $result=$self->GetNodePollers($node_id,"N|V|I");

Returns a Net::SolarWinds::Result Object that contains the nodes when true when false it contains why it failed.

=cut

sub GetNodePollers {
  my ($self,$node_id,$type)=@_;
  $self->log_info("starting node_id: $node_id type: $type");
  my $query=sprintf q{SELECT Description, DisplayName, Enabled, InstanceType, NetObject, NetObjectID, NetObjectType, PollerID, PollerType, Uri FROM Orion.Pollers where NetObjectID=%i},$node_id;
  my $result=$self->Query($query);

  $type='N' unless defined($type);
  unless($result) {
    $self->log_error("Failed to GetNodePollers on node_id: $node_id type: $type");
    $self->log_info("stopping");
    return $result;
  }


  my $list=$result->get_data->{results};
  my $pollers=[];
  foreach my $poller (@{$list}) {
    my $result=$self->getSwisProps('swis://localhost/Orion/Orion.Pollers/PollerID='.$poller);
    return unless $result;
    push @{$pollers},$result->get_data;
  }
  $self->log_info("stopping");
  return $self->RESULT_CLASS->new_true($pollers);
}

=back

=head1 SEE ALSO

Net::SolarWinds::REST::Batch

=head1 AUTHOR

Michael Shipper

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2016 by Mike Shipper

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.1 or,
at your option, any later version of Perl 5 you may have available.

=cut

1;
__END__
