File Coverage

blib/lib/DracPerl/Client.pm
Criterion Covered Total %
statement 66 66 100.0
branch 12 20 60.0
condition 1 2 50.0
subroutine 11 11 100.0
pod 0 3 0.0
total 90 102 88.2


line stmt bran cond sub pod time code
1             package DracPerl::Client;
2              
3             our $VERSION = "0.01";
4              
5 2     2   14589 use Data::Dumper;
  2         11135  
  2         102  
6 2     2   756 use Log::Any qw($log);
  2         6447  
  2         7  
7 2     2   6531 use Log::Any::Adapter;
  2         417  
  2         7  
8             Log::Any::Adapter->set('Stdout');
9              
10 2     2   1093 use LWP::UserAgent;
  2         58385  
  2         52  
11 2     2   941 use Moose;
  2         594451  
  2         12  
12 2     2   11222 use XML::Simple qw(XMLin);
  2         12573  
  2         13  
13              
14             has 'ua' => (lazy => 1, is => 'ro', builder => '_build_ua');
15             has 'url' => (is => 'ro', isa => 'Str', required => 1);
16             has 'user' => (is => 'ro', isa => 'Str', required => 1);
17             has 'password' => (is => 'ro', isa => 'Str', required => 1);
18             has 'max_retries' => (is => 'ro', isa => 'Int', default => 5);
19             has 'single_use' => (is => 'ro', isa => 'Int', default => 1);
20             has 'token' => (is => 'rw', isa => 'Str', default => 0);
21              
22             sub _build_ua {
23 1     1   9 my $ua = LWP::UserAgent->new();
24 1         2013 $ua->ssl_opts(verify_hostname => 0);
25 1         22 $ua->cookie_jar({});
26              
27             #Thanks Spotify for theses two headers.
28             #https://github.com/spotify/moob/blob/master/lib/moob/idrac7.rb#L23
29 1         5776 $ua->default_header('Accept-Encoding' => 'gzip,deflate,sdch');
30 1         34 $ua->default_header('Accept-Language' => 'en-US,en;q=0.8,sv;q=0.6');
31              
32 1         27 $ua->default_header('Accept' => '*/*');
33              
34 1         51 return $ua;
35             }
36              
37             sub login {
38 1     1 0 2127 my $self = shift;
39              
40 1         29 my $login_form = $self->ua->get($self->url."/login.html");
41 1 50       2378 $self->_check_res("Login Step 0 performed", $login_form->is_success) || die("iDrac login page is unreacheable : ".$self->url."/login.html");
42              
43 1         2 my $response_raw;
44             my $response_xml;
45 1         2 my $need_to_retry = 1;
46 1         1 my $current_tries = 1;
47 1         1 my $logged;
48              
49 1         3 while($need_to_retry) {
50              
51 2         93 $response_raw = $self->ua->post($self->url."/data/login",{
52             user => $self->user,
53             password => $self->password
54             });
55              
56 2 50       1062 if($response_raw->is_success) {
57 2         28 $response_xml = XMLin($response_raw->decoded_content);
58 2         66684 $logged = !$response_xml->{authResult};
59             }
60              
61 2 100       8 $need_to_retry = 0 if $logged;
62 2 50       63 $need_to_retry = 0 if $current_tries > $self->max_retries-1;
63              
64 2         4605 $self->_check_res("Login Step 1 performed (Attempt ".$current_tries."/".$self->max_retries.")", $logged);
65 2         7 $current_tries++;
66             }
67              
68 1 50       5 die("Logging failed after ".$self->max_retries." attempts") unless $logged;
69              
70 1         5 $log->debug("Login Step 1 response : ".$response_raw->decoded_content);
71              
72 1         395 my @tokens_parts = reverse split("=",$response_xml->{forwardUrl});
73            
74 1         6 $log->info("ST2 Token: ".$tokens_parts[0]);
75              
76 1 50       132 $self->token($tokens_parts[0]) if $tokens_parts[0];
77 1         23 $self->ua->default_header("ST2", $self->token);
78 1         87 return 1;
79             }
80              
81             sub logout {
82 1     1 0 2 my $self = shift;
83 1         26 my $logout_page = $self->ua->post($self->url."/data/logout");
84 1         1386 $self->token(0);
85 1         8 $log->debug("Logging out : ".$logout_page->decoded_content);
86             }
87              
88             sub get {
89 1     1 0 2 my($self, $query) = @_;
90            
91 1 50       29 $self->login() unless $self->token; #If the Login token already exist, no need to regenerate a new one
92              
93 1         22 my $response = $self->ua->post($self->url."/data?get=".$query);
94              
95 1         238 $self->_check_res("Getting : ".$query, $response->is_success);
96              
97 1         2 my $parsed_response;
98              
99 1 50       4 $parsed_response = XMLin($response->decoded_content) if $response->is_success;
100            
101 1 50       18881 $self->logout() if $self->single_use;
102              
103 1   50     390 return $parsed_response || 0;
104             }
105              
106             sub _check_res {
107 4     4   28 my($self, $message,$condition) = @_;
108              
109 4 100       12 if($condition) {
110 3         13 $log->info("[SUCCESS] ".$message);
111 3         435 return 1;
112             } else {
113 1         30 $log->error("[FAILURE] ".$message);
114 1         311 return 0;
115             }
116             }
117              
118             =head1 NAME
119              
120             DracPerl::Client - API Client for Dell's management interface (iDRAC)
121              
122             =head1 AUTHOR
123              
124             Jules Decol (@Apcros)
125              
126             =head1 SYNOPSIS
127              
128             # Create the client
129             my $drac_client = DracPerl::Client->new({
130             user => "username",
131             password => "password",
132             url => "https://dracip",
133             });
134              
135             # Get what you're interested in
136             # Login is done implicitly
137             my $parsed_xml = $drac_client->get("fans");
138              
139             =head1 DESCRIPTION
140              
141             This has been created because I wanted to create my own version of the web Interface of iDRAC
142             and an API Client was needed for that purpose.
143              
144             This allow you to get all the informations that you can get from the iDRAC web interface.
145             (The interface actually use a backend XML API)
146              
147             =head1 OBJECT ARGUMENTS
148              
149              
150             =head2 max_retries
151              
152             Login can be extremely capricious, Max retries avoid being too
153             annoyed by that. Defaulted to 5.
154              
155             =head2 single_use
156              
157             Because there's a max number of connections, we need to logout
158             After each request to avoid leaving a connection opened
159             But because the login is quite slow, we don't want to prevent
160             Use cases where multiple gets are going to be done sucessively
161              
162             =cut
163              
164             1;