File Coverage

blib/lib/DMTF/WSMan.pm
Criterion Covered Total %
statement 21 191 10.9
branch 0 60 0.0
condition 0 6 0.0
subroutine 7 22 31.8
pod 9 9 100.0
total 37 288 12.8


line stmt bran cond sub pod time code
1             package DMTF::WSMan;
2              
3 1     1   47452 use warnings;
  1         2  
  1         31  
4 1     1   5 use strict;
  1         2  
  1         33  
5              
6 1     1   1188 use version;
  1         2869  
  1         6  
7             our $VERSION = qv('0.05');
8 1     1   951 use LWP;
  1         147278  
  1         40  
9 1     1   959 use LWP::Authen::Digest;
  1         5206  
  1         40  
10 1     1   929 use Data::UUID;
  1         1126  
  1         80  
11 1     1   7 use Carp;
  1         1  
  1         3351  
12              
13             # Module implementation here
14             # We make our own specialization of LWP::UserAgent that
15             # uses the correct user ID and password
16             {
17             package DMTF::WSMan::PRIVATE::RequestAgent;
18             our @ISA = qw(LWP::UserAgent);
19              
20             sub new
21             {
22 0     0     my $class=shift;
23 0           my $awo=shift;
24 0           my $self = LWP::UserAgent::new($class, @_);
25 0           $self->{ASSOCIATED_WSMAN_OBJECT}=$awo;
26 0           return($self);
27             }
28              
29             sub get_basic_credentials
30             {
31 0     0     my $self=shift;
32 0           return($self->{ASSOCIATED_WSMAN_OBJECT}{Context}{user},$self->{ASSOCIATED_WSMAN_OBJECT}{Context}{pass});
33             }
34             }
35              
36             sub new
37             {
38 0     0 1   my $self={};
39 0           $self->{CLASS} = shift;
40 0           my %args=@_;
41 0           $self->{Context} = {
42             user=>'Administrator',
43             # password
44             # host
45             port=>623,
46             protocol=>'http',
47             xmlns=>{
48             soap=>{prefix=>'s', uri=>'http://www.w3.org/2003/05/soap-envelope'},
49             addressing=>{prefix=>'a', uri=>'http://schemas.xmlsoap.org/ws/2004/08/addressing'},
50             enumeration=>{prefix=>'n', uri=>'http://schemas.xmlsoap.org/ws/2004/09/enumeration'},
51             wsman=>{prefix=>'w', uri=>'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'},
52             cim=>{prefix=>'c', uri=>'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd'}
53             }
54             };
55 0 0         $self->{Context}{user} = $args{user} if(defined $args{user});
56 0 0         $self->{Context}{port} = $args{port} if(defined $args{port});
57 0 0         $self->{Context}{protocol} = $args{protocol} if(defined $args{protocol});
58 0 0         $self->{Context}{pass} = $args{pass} if(defined $args{pass});
59 0 0         $self->{Context}{host} = $args{host} if(defined $args{host});
60 0           $self->{RA} = DMTF::WSMan::PRIVATE::RequestAgent->new($self, keep_alive=>1);
61 0           $self->{challenge_str}=undef;
62 0           $self->{UUID} = Data::UUID->new();
63 0           bless($self, $self->{CLASS});
64 0           return($self);
65             }
66              
67             sub invoke
68             {
69 0     0 1   my $self=shift;
70 0           my %args=@_;
71 0 0         if(!defined $args{epr}) {
72 0           carp "No EPR specified";
73 0           return;
74             }
75 0           my $postdata;
76              
77 0 0         if(defined $args{method}) {
78 0           $postdata=$self->_genheaders($args{epr}{ResourceURI}."/".$args{method},$args{epr});
79             }
80             else {
81 0           $postdata=$self->_genheaders($args{epr}{ResourceURI},$args{epr});
82             }
83 0           $postdata .= "<$self->{Context}{xmlns}{soap}{prefix}:Body>";
84 0           $postdata .= $args{body};
85 0           $postdata .= "{Context}{xmlns}{soap}{prefix}:Body>{Context}{xmlns}{soap}{prefix}:Envelope>";
86              
87 0           my $res = $self->_request($postdata);
88 0           return $res->content;
89             }
90              
91             sub put
92             {
93 0     0 1   my $self=shift;
94 0           my %args=@_;
95 0 0         if(!defined $args{epr}) {
96 0           carp('No EPR specified');
97 0           return;
98             }
99              
100 0           my $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/transfer/Put',$args{epr});
101 0           $postdata .= "<$self->{Context}{xmlns}{soap}{prefix}:Body>".$args{body}."{Context}{xmlns}{soap}{prefix}:Body>{Context}{xmlns}{soap}{prefix}:Envelope>";
102              
103 0           my $res = $self->_request($postdata);
104 0           return $res->content;
105             }
106              
107             sub create
108             {
109 0     0 1   my $self=shift;
110 0           my %args=@_;
111 0 0         if(!defined $args{epr}) {
112 0           carp('No EPR specified');
113 0           return;
114             }
115              
116 0           my $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/transfer/Create',$args{epr});
117 0           $postdata .= "<$self->{Context}{xmlns}{soap}{prefix}:Body>".$args{body}."{Context}{xmlns}{soap}{prefix}:Body>{Context}{xmlns}{soap}{prefix}:Envelope>";
118              
119 0           my $res = $self->_request($postdata);
120 0           return $res->content;
121             }
122              
123             sub get
124             {
125 0     0 1   my $self=shift;
126 0           my %args=@_;
127 0 0         if(!defined $args{epr}) {
128 0           carp('No EPR specified');
129 0           return;
130             }
131 0           my $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/transfer/Get',$args{epr});
132 0           $postdata .= <
133             <$self->{Context}{xmlns}{soap}{prefix}:Body/>
134             {Context}{xmlns}{soap}{prefix}:Envelope>
135             ENDOFREQUEST
136              
137 0           my $res = $self->_request($postdata);
138 0           return($res->content);
139             }
140              
141             sub delete
142             {
143 0     0 1   my $self=shift;
144 0           my %args=@_;
145 0 0         if(!defined $args{epr}) {
146 0           carp('No EPR specified');
147 0           return;
148             }
149 0           my $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete',$args{epr});
150 0           $postdata .= <
151             <$self->{Context}{xmlns}{soap}{prefix}:Body/>
152             {Context}{xmlns}{soap}{prefix}:Envelope>
153             ENDOFREQUEST
154              
155 0           my $res = $self->_request($postdata);
156 0           return($res->content);
157             }
158              
159             sub enumerate
160             {
161 0     0 1   my $self=shift;
162 0           my %args=@_;
163 0 0         if(!defined $args{epr}) {
164 0           carp('No EPR specified');
165 0           return;
166             }
167              
168 0 0         $args{mode} = 'EnumerateObjectAndEPR' if(!defined $args{mode});
169 0 0         $args{filter} = '' if(!defined $args{filter});
170 0           my $cnt;
171 0           my $results='';
172              
173 0           my $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate',$args{epr});
174 0           $postdata.=<
175             <$self->{Context}{xmlns}{soap}{prefix}:Body>
176             <$self->{Context}{xmlns}{enumeration}{prefix}:Enumerate>
177             <$self->{Context}{xmlns}{wsman}{prefix}:OptimizeEnumeration/>
178             <$self->{Context}{xmlns}{wsman}{prefix}:MaxElements>10000{Context}{xmlns}{wsman}{prefix}:MaxElements>
179             <$self->{Context}{xmlns}{wsman}{prefix}:EnumerationMode>$args{mode}{Context}{xmlns}{wsman}{prefix}:EnumerationMode>$args{filter}
180             {Context}{xmlns}{enumeration}{prefix}:Enumerate>
181             {Context}{xmlns}{soap}{prefix}:Body>
182             {Context}{xmlns}{soap}{prefix}:Envelope>
183             ENDOFREQUEST
184              
185 0           my $res = $self->_request($postdata);
186 0 0         if($res->content=~/EnumerationContext(?:\s+[^>]*)?>([^<]*)
187 0           $cnt=$1;
188             }
189 0           $results .= $res->content;
190 0 0         undef $cnt if($res->content=~/<[^:>]+:EndOfSequence[\s\/>]/s);
191              
192 0           while(defined $cnt) {
193 0           $postdata=$self->_genheaders('http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull',$args{epr});
194 0           $postdata.=<
195             <$self->{Context}{xmlns}{soap}{prefix}:Body>
196             <$self->{Context}{xmlns}{enumeration}{prefix}:Pull>
197             <$self->{Context}{xmlns}{enumeration}{prefix}:EnumerationContext>$cnt{Context}{xmlns}{enumeration}{prefix}:EnumerationContext>
198             <$self->{Context}{xmlns}{enumeration}{prefix}:MaxElements>10000{Context}{xmlns}{enumeration}{prefix}:MaxElements>
199             {Context}{xmlns}{enumeration}{prefix}:Pull>
200             {Context}{xmlns}{soap}{prefix}:Body>
201             {Context}{xmlns}{soap}{prefix}:Envelope>
202             ENDOFREQUEST
203              
204 0           $res = $self->_request($postdata);
205 0 0         if($res->content=~/EnumerationContext(?:\s+[^>]*)?>([^<]*)
206 0           $cnt=$1;
207             }
208             else {
209 0           undef $cnt;
210             }
211 0 0         undef $cnt if($res->content=~/<[^:>]+:EndOfSequence[\s\/>]/s);
212             # TODO: Normalize namespaces
213 0           $results .= $res->content;
214             }
215              
216 0           return($results);
217             }
218              
219              
220             ###################
221             # Utility methods #
222             ###################
223              
224             sub get_selectorset_xml
225             {
226 0     0 1   my $self=shift;
227 0           my $epr=shift;
228 0           my $selectorset='';
229              
230 0 0         if(defined $epr->{SelectorSet}) {
231 0           $selectorset = " <$self->{Context}{xmlns}{wsman}{prefix}:SelectorSet>\n";
232 0           foreach my $name (keys %{$epr->{SelectorSet}}) {
  0            
233 0           $selectorset .= " <$self->{Context}{xmlns}{wsman}{prefix}:Selector Name=\"$name\">";
234 0 0         if(ref($epr->{SelectorSet}{$name}) eq 'HASH') {
235 0           $selectorset .= $self->epr_to_xml($epr->{SelectorSet}{$name});
236             }
237             else {
238 0           $selectorset .= _XML_escape($epr->{SelectorSet}{$name});
239             }
240 0           $selectorset .= "{Context}{xmlns}{wsman}{prefix}:Selector>\n";
241             }
242 0           $selectorset .= " {Context}{xmlns}{wsman}{prefix}:SelectorSet>\n";
243             }
244 0 0         $selectorset = "\n$selectorset" if($selectorset ne '');
245              
246 0           return $selectorset;
247             }
248              
249             sub epr_to_xml
250             {
251 0     0 1   my $self=shift;
252 0           my $epr=shift;
253 0           my $selectorset=$self->get_selectorset_xml($epr);
254              
255 0           return <
256             <$self->{Context}{xmlns}{addressing}{prefix}:EndpointReference>
257             <$self->{Context}{xmlns}{addressing}{prefix}:Address>http://$self->{Context}{host}:$self->{Context}{port}/wsman{Context}{xmlns}{addressing}{prefix}:Address>
258             <$self->{Context}{xmlns}{addressing}{prefix}:ReferenceParameters>
259             <$self->{Context}{xmlns}{wsman}{prefix}:ResourceURI>$epr->{ResourceURI}{Context}{xmlns}{wsman}{prefix}:ResourceURI>
260             $selectorset
261             {Context}{xmlns}{addressing}{prefix}:ReferenceParameters>
262             {Context}{xmlns}{addressing}{prefix}:EndpointReference>
263             EOF
264             }
265              
266             ################
267             # Non-exported #
268             ################
269             sub _XML_escape
270             {
271 0     0     my $val=shift;
272 0           $val=~s/&/&/g;
273 0           $val=~s/
274 0           $val=~s/"/"/g;
275 0           $val=~s/'/'/g;
276 0           return $val;
277             }
278              
279             sub _request
280             {
281 0     0     my $self=shift;
282 0           my $postdata=shift;
283              
284 0           my $req = HTTP::Request->new(POST => $self->{Context}{protocol}."://$self->{Context}{host}:$self->{Context}{port}/wsman");
285 0           $req->header('Content-Type', 'application/soap+xml;charset=UTF-8');
286 0           $req->header('Content-Length', length $postdata); # Not really needed
287 0           $req->content($postdata);
288 0           return $self->_authenticated_request($req);
289             }
290              
291             sub _genheaders
292             {
293 0     0     my $self=shift;
294 0           my $action=shift;
295 0           my $epr=shift;
296 0           my $selectorset=$self->get_selectorset_xml($epr);
297              
298 0           my $postdata="<$self->{Context}{xmlns}{soap}{prefix}:Envelope";
299 0           foreach my $ns (keys %{$self->{Context}{xmlns}}) {
  0            
300 0           $postdata .= "\n xmlns:$self->{Context}{xmlns}{$ns}{prefix}=\"$self->{Context}{xmlns}{$ns}{uri}\"";
301             }
302 0           $postdata .= ">\n";
303 0           my $uuid=$self->{UUID}->create_str();
304 0           $postdata .= <
305             <$self->{Context}{xmlns}{soap}{prefix}:Header>
306             <$self->{Context}{xmlns}{addressing}{prefix}:To>$self->{Context}{protocol}://$self->{Context}{host}:$self->{Context}{port}/wsman{Context}{xmlns}{addressing}{prefix}:To>
307             <$self->{Context}{xmlns}{wsman}{prefix}:ResourceURI s:mustUnderstand="true">$epr->{ResourceURI}{Context}{xmlns}{wsman}{prefix}:ResourceURI>
308             <$self->{Context}{xmlns}{addressing}{prefix}:ReplyTo>
309             <$self->{Context}{xmlns}{addressing}{prefix}:Address $self->{Context}{xmlns}{soap}{prefix}:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous{Context}{xmlns}{addressing}{prefix}:Address>
310             {Context}{xmlns}{addressing}{prefix}:ReplyTo>
311             <$self->{Context}{xmlns}{addressing}{prefix}:Action $self->{Context}{xmlns}{soap}{prefix}:mustUnderstand="true">$action{Context}{xmlns}{addressing}{prefix}:Action>
312             <$self->{Context}{xmlns}{addressing}{prefix}:MessageID>uuid:$uuid{Context}{xmlns}{addressing}{prefix}:MessageID>$selectorset
313             {Context}{xmlns}{soap}{prefix}:Header>
314             ENDOFREQUEST
315 0           return($postdata);
316             }
317              
318             sub _authenticated_request
319             {
320 0     0     my $self=shift;
321 0           my $req=shift;
322              
323 0 0         if(defined $self->{challenge_str}) {
324 0           my $challenge=$self->{challenge_str};
325              
326 0           $challenge =~ tr/,/;/; # "," is used to separate auth-params!!
327 0           ($challenge) = HTTP::Headers::Util::split_header_words($challenge);
328 0           $challenge = { @$challenge }; # make rest into a hash
329 0           for (keys %$challenge) { # make sure all keys are lower case
330 0           $challenge->{lc $_} = delete $challenge->{$_};
331             }
332 0           my $res;
333 0 0         if(exists $challenge->{digest}) {
    0          
334 0           $res=LWP::Authen::Digest->authenticate($self->{RA}, undef, $challenge, undef, $req, undef, undef);
335             }
336             elsif(exists $challenge->{basic}) {
337 0           $res=LWP::Authen::Basic->authenticate($self->{RA}, undef, $challenge, undef, $req, undef, undef);
338             }
339             else {
340 0           $res=$self->{RA}->request($req);
341             }
342 0 0         if($res->code == 401) {
343 0           $self->{challenge_str}=$res->www_authenticate;
344 0           $res=$self->_authenticated_request($req);
345 0 0         if($res->code == 200) {
346 0           return($res);
347             }
348             else {
349 0           print "!!!! Unable to authenticate!\n";
350             }
351             }
352 0 0 0       $self->{challenge_str}=$res->previous->www_authenticate if(defined $res->previous && $res->code==200);
353 0           return($res);
354             }
355 0           my $res=$self->{RA}->request($req);
356 0 0         if($res->code == 501) {
357 0 0         if($res->message =~ /SSLeay/) {
358 0           print "SSL support requires Crypt::SSLeay to be installed.\n";
359 0           print "Use the command \"ppm install http://theoryx5.uwinnipeg.ca/ppms/Crypt-SSLeay.ppd\"\n";
360             }
361             }
362 0 0 0       $self->{challenge_str}=$res->previous->www_authenticate if(defined $res->previous && $res->code==200);
363 0           return($res);
364             }
365              
366             1; # Magic true value required at end of module
367             __END__