File Coverage

blib/lib/Cisco/UCS.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Cisco::UCS;
2              
3 1     1   20947 use warnings;
  1         2  
  1         31  
4 1     1   5 use strict;
  1         1  
  1         23  
5              
6 1     1   1069 use Cisco::UCS::Chassis;
  0            
  0            
7             use Cisco::UCS::Interconnect;
8             use Cisco::UCS::FEX;
9             use Cisco::UCS::Blade;
10             use Cisco::UCS::Fault;
11             use Cisco::UCS::MgmtEntity;
12             use Cisco::UCS::ServiceProfile;
13             use LWP;
14             use XML::Simple;
15             use Carp qw(croak carp cluck);
16              
17             use vars qw($VERSION);
18              
19             our $VERSION = '0.35';
20              
21             our @ATTRIBUTES = qw(dn cluster cookie);
22              
23             our %ATTRIBUTES = ();
24              
25             sub new {
26             my ($class, %args) = @_;
27             my $self = {};
28             bless $self, $class;
29             defined $args{cluster} ? $self->{cluster} = $args{cluster} : croak 'cluster not defined';
30             defined $args{username} ? $self->{username} = $args{username} : croak 'username not defined';
31             defined $args{passwd} ? $self->{passwd} = $args{passwd} : croak 'passwd not defined';
32             defined $args{verify_hostname} ? $self->{verify_hostname} = $args{verify_hostname} : 0;
33             $self->{port} = ($args{port} or 443);
34             $self->{proto} = ($args{proto} or 'https');
35             $self->{dn} = ($args{dn} or 'sys');
36             return $self;
37             }
38              
39             {
40             no strict 'refs';
41              
42             while ( my ($pseudo, $attribute) = each %ATTRIBUTES ) {
43             *{ __PACKAGE__ . '::' . $pseudo } = sub {
44             my $self = shift;
45             return $self->{$attribute}
46             }
47             }
48              
49             foreach my $attribute (@ATTRIBUTES) {
50             *{ __PACKAGE__ . '::' . $attribute } = sub {
51             my $self = shift;
52             return $self->{$attribute}
53             }
54             }
55             }
56              
57             sub login {
58             my $self = shift;
59              
60             undef $self->{error};
61             $self->{ua} = LWP::UserAgent->new( ssl_opts => { verify_hostname => $self->{verify_hostname} } );
62             $self->{uri} = $self->{proto}. '://' .$self->{cluster}. ':' .$self->{port}. '/nuova';
63             $self->{req} = HTTP::Request->new(POST => $self->{uri});
64             $self->{req}->content_type('application/x-www-form-urlencoded');
65             $self->{req}->content('');
66             my $res = $self->{ua}->request($self->{req});
67              
68             unless ($res->is_success) {
69             $self->{error} = 'Login failure: '.$res->status_line;
70             return 0
71             }
72              
73             $self->{parser} = XML::Simple->new;
74             my $xml = $self->{parser}->XMLin($res->content);
75              
76             if(defined $xml->{'errorCode'}) {
77             $self->{error} = 'Login failure: '. (defined $xml->{'errorDescr'} ? $xml->{'errorDescr'} : 'Unspecified error');
78             return 0
79             }
80              
81             $self->{cookie} = $xml->{'outCookie'};
82             return 1
83             }
84              
85             sub refresh {
86             my $self = shift;
87              
88             undef $self->{error};
89             $self->{req}->content('');
90             my $res = $self->{ua}->request($self->{req});
91              
92             unless ($res->is_success) {
93             $self->{error} = 'Refresh failed: ' . $res->status_line;
94             return 0
95             }
96              
97             my $xml = $self->{parser}->XMLin($res->content());
98              
99             if (defined $xml->{'errorCode'}) {
100             $self->{error} = 'Refresh failure: '. (defined $xml->{'errorDescr'} ? $xml->{'errorDescr'} : 'Unspecified error');
101             return 0
102             }
103              
104             $self->{cookie} = $xml->{'outCookie'};
105             return 1
106             }
107              
108             sub logout {
109             my $self = shift;
110             return unless $self->{cookie};
111             undef $self->{error};
112             return ( $self->_ucsm_request('') ? 1 : 0 )
113             }
114              
115             sub _ucsm_request {
116             my ($self, $content, $class_id) = @_;
117              
118             undef $self->{error};
119             $self->{req}->content($content);
120             my $res = $self->{ua}->request($self->{req});
121              
122             $self->error($res->status_line) unless $res->is_success;
123              
124             my $xml = ( $class_id
125             ? $self->{parser}->XMLin($res->content, KeyAttr => $class_id)
126             : $self->{parser}->XMLin($res->content, KeyAttr => [ 'name', 'key', 'id', 'intId' ] )
127             );
128              
129             return ( $xml->{errorCode}
130             ? do {
131             $self->{error} = ( $xml->{'errorDescr'} ? $xml->{'errorDescr'} : 'Unspecified error' );
132             undef
133             }
134             : $xml
135             );
136             }
137              
138             # This private method provides an abstract factory-type constructor for resolved child
139             # objects of the specified dn. To maintain compatibility with existing methods and to
140             # provide a single method for all child objects, there are a few important design
141             # considerations that have been made.
142             #
143             # Importantly we check for a 'class_filter' argument to the method which if present is
144             # used to call the 'resolve_class_filter' method rather than the 'resolve_children' method.
145             # This is necessary due to limitations and difficulties present in resolving child objects
146             # for some objects can lead to incorrect results. For example, resolving the child objects
147             # for a chassis to retrieve the blades in the chassis is difficult, instead we can use a much
148             # more efficient and simpler method of retrieving the blades in a specified chassis by
149             # resolving all objects in the specified class (i.e. computeBlade) and restricting the
150             # results returned using a filter method.
151             #
152             # The implication of doing this is that the call to 'resolve_class_filter' returns an array
153             # which cannot be processed using the same logic as for a hash returned by the 'resolve_children'
154             # method. Therefore we perform extra processing to convert the returned array results to a
155             # hash using a nominated attribute in the object as the hash index for the objects.
156             #
157             # SYNOPSIS
158             # get_child_object ( %ARGS )
159             #
160             # PARAMETERS
161             # id The optional identifier of a specific child object. This identifier is context
162             # dependent and may be either numerical (as in the case of a chassis) or alphanumeric
163             # (as is the case with a fabric interconnect - A or B).
164             #
165             # type The desired child object type to be resolved as according to the UCSM information
166             # management hierarchy name. e.g. etherPIo is the ethernet port child object type for line cards.
167             #
168             # class The class into which the child object will be blessed.
169             #
170             # attr The pseudo-namespace in the Cisco::UCS object in which the retrieved child onjects will be cached.
171             # For example; attr => 'interconnect' will mean that Cisco::UCS::Interconnect objects retrieved in
172             # a $ucs->get_interconnects method call will be stored in $ucs->{interconnects}->{$OBJ}.
173             #
174             # self A reference to a Cisco::UCS object. If not present $self is assumed to be a Cisco::UCS object.
175             #
176             # uid Where results are returned and parsed into an array and the array index is not aligned to an identifying
177             # attribute of the object (i.e. the array index has no relation to a unique identifier for the object) then
178             # the uid may be used to refer to a unique identifying attribute of the object that should be used.
179             # For example, in resolving all blades for a Cisco::UCS object, the uid value of bladeId is used to uniquely
180             # identify all Cisco::UCS::Blade objects as the array index has no relation to a uniquely identifying feature
181             # of the blade and is not guaranteed to be consistent.
182             #
183             # class_filter (%ARGS)
184             # A class filter may be specified to filter the results to a particular subset. This is useful for
185             # operations like retrieving all blades in a particular chassis rather than retrieving all blades and
186             # manually filtering the results.
187             #
188             # Where %ARGS:
189             #
190             # classId The UCSM class which should be used for the UCSM query. For example: classId => etherPio.
191             #
192             # filter Where filter is composed of any number of valid attribute/value pairs. For example: slotId => 1, switchId => $self->{id}.
193             #
194              
195             sub _get_child_objects {
196             my ($self,%args)= @_;
197             my $ucs = ( defined $self->{ucs} ? $self->{ucs} : $self );
198             my $ref = ( defined $args{self} ? $args{self} : $self );
199             my $xml = ( defined $args{class_filter}
200             ? $ucs->resolve_class_filter( %{$args{class_filter}} )
201             : $ucs->resolve_children(dn => $ref->{dn})
202             );
203              
204             if (ref($xml->{outConfigs}->{$args{type}}) eq 'ARRAY') {
205             $args{uid} ||= 'id';
206             my $res;
207            
208             foreach my $obj (@{$xml->{outConfigs}->{$args{type}}}) {
209             $res->{$obj->{$args{uid}}} = $obj
210             }
211              
212             $xml->{outConfigs}->{$args{type}} = $res
213             }
214             elsif ((ref($xml->{outConfigs}->{$args{type}}) eq 'HASH') and (exists $xml->{outConfigs}->{$args{type}}->{dn})) {
215             $args{uid} ||= 'id';
216             my $res;
217             $res->{$xml->{outConfigs}->{$args{type}}->{$args{uid}}} = $xml->{outConfigs}->{$args{type}};
218             $xml->{outConfigs}->{$args{type}} = $res
219             }
220              
221             return ( defined $xml->{outConfigs}->{$args{type}}
222             ? do { my @res;
223             foreach my $res (keys %{$xml->{outConfigs}->{$args{type}}}) {
224             my $obj = $args{class}->new( ucs => $ucs, dn => $xml->{outConfigs}->{$args{type}}->{$res}->{dn}, id => $res );
225             $ref->{$args{attr}}->{$res} = $obj; #print "Setting $ref\->{$args{attr}}->{$res} to $obj\n";
226             push @res, $obj;
227             }
228             return @res unless $args{id};
229             return $ref->{$args{attr}}->{$args{id}} if $args{id} and $ref->{$args{attr}}->{$args{id}};
230             return
231             }
232             : ()
233             );
234            
235             }
236              
237             sub get_error_id {
238             warn "get_error_id has been deprecated in future releases";
239             return get_error(@_)
240             }
241              
242             sub error {
243             my ($self, $id) = @_;
244             return ( defined $self->{fault}->{$id} ? $self->{fault}->{$id} : $self->get_error($id) )
245             }
246              
247             sub get_error {
248             my ($self, $id)=@_;
249             return $self->get_errors($id)
250             }
251              
252             sub get_errors {
253             my ($self, $id) =@_;
254             return $self->_get_child_objects(id => $id, type => 'faultInst', class => 'Cisco::UCS::Fault',
255             uid => 'id', attr => 'fault', class_filter => { classId => 'faultInst' } );
256             }
257              
258             sub _isInHierarchical {
259             my $inHierarchical = lc shift;
260              
261             return 'false' unless ($inHierarchical =~ /true|false|0|1/);
262              
263             return $inHierarchical if ($inHierarchical =~ /^true|false$/);
264              
265             return ($inHierarchical == 0 ? 'false' : 'true');
266             }
267              
268             sub _createFilter {
269             my ($self, %args) = @_;
270              
271             unless (defined $args{classId}) {
272             $self->{error} = 'No classId specified';
273             return
274             }
275              
276             my $filter = '';
277              
278             while (my($property,$value) = each %args) {
279             next if ($property eq 'inHierarchical' or $property eq 'classId');
280             $filter .= '';
281             }
282              
283             $filter .= '';
284              
285             return $filter;
286             }
287              
288             sub resolve_class {
289             my ($self,%args)= @_;
290              
291             unless ( defined $args{classId} ) {
292             $self->{error} = 'No classId specified';
293             return
294             }
295              
296             $args{inHierarchical} = (defined $args{inHierarchical} ? _isInHierarchical($args{inHierarchical}) : 'false');
297              
298             my $xml = $self->_ucsm_request(' 299             '" cookie="' . $self->{cookie} . '" classId="' . $args{classId} . '" />') or return;
300              
301             return $xml
302             }
303              
304             sub resolve_classes {
305             my ($self,%args)= @_;
306              
307             unless (defined $args{classId}) {
308             $self->{error} = 'No classID specified';
309             return
310             }
311              
312             $args{inHierarchical} = (defined $args{inHierarchical} ? _isInHierarchical($args{inHierarchical}) : 'false');
313              
314             my $xml = $self->_ucsm_request( ' 315             '" cookie="' . $self->{cookie} . '">' .
316             ' 317             '" />', 'classId'
318             ) or return;
319              
320             return $xml
321             }
322              
323             sub resolve_dn {
324             my ($self,%args)= @_;
325              
326             unless (defined $args{dn}) {
327             $self->{error} = 'No dn specified';
328             return
329             }
330              
331             $args{inHierarchical} = (defined $args{inHierarchical} ? _isInHierarchical($args{inHierarchical}) : 'false');
332              
333             my $xml = $self->_ucsm_request( ' 334             '" inHierarchical="' . $args{inHierarchical} .
335             '" cookie="' . $self->{cookie} . '" />'
336             ) or return;
337             return $xml;
338             }
339              
340             sub resolve_children {
341             my ($self,%args)= @_;
342              
343             unless (defined $args{dn}) {
344             $self->{error} = 'No dn specified';
345             return
346             }
347              
348             $args{inHierarchical} = ( defined $args{inHierarchical} ? _isInHierarchical($args{inHierarchical}) : 'false' );
349              
350             my $xml = $self->_ucsm_request( ' 351             '" cookie="' . $self->{cookie} .
352             '" inDn="' . $args{dn} .
353             '">'
354             ) or return;
355              
356             return $xml
357             }
358              
359             sub resolve_class_filter {
360             my($self,%args) = @_;
361            
362             $args{inHierarchical} = (defined $args{inHierarchical} ? _isInHierarchical($args{inHierarchical}) : 'false');
363              
364             my $filter = $self->_createFilter(%args) or return;
365              
366             my $xml = $self->_ucsm_request('' .
367             $filter . '', $args{classId}) or return;
368              
369             return $xml
370             }
371              
372             sub get_cluster_status {
373             my $self= shift;
374              
375             my $xml = $self->resolve_dn(dn => 'sys') or return;
376              
377             return (defined $xml->{outConfig}->{topSystem} ? $xml->{outConfig}->{topSystem} : undef)
378             }
379              
380             sub version {
381             my $self= shift;
382             my $xml = $self->resolve_dn(dn => 'sys/mgmt/fw-system') or return;
383             return (defined $xml->{outConfig}->{firmwareRunning}->{version} ? $xml->{outConfig}->{firmwareRunning}->{version} : undef)
384             }
385              
386             sub mgmt_entity {
387             my ($self, $id) = @_;
388             return ( defined $self->{mgmt_entity}->{$id} ? $self->{mgmt_entity}->{$id} : $self->mgmt_entity($id) )
389             }
390              
391             sub get_mgmt_entity {
392             my ($self, $id) = @_;
393             return $self->get_mgmt_entities($id)
394             }
395              
396             sub get_mgmt_entities {
397             my ($self, $id) = @_;
398             return $self->_get_child_objects(id => $id, type => 'mgmtEntity', class => 'Cisco::UCS::MgmtEntity', attr => 'mgmt_entity');
399             }
400              
401             sub get_primary_mgmt_entity {
402             my $self = shift;
403              
404             my $xml = $self->resolve_class_filter(classId => 'mgmtEntity', leadership => 'primary') or return;
405              
406             return (defined $xml->{outConfigs}->{mgmtEntity} ? $xml->{outConfigs}->{mgmtEntity} : undef)
407             }
408              
409             sub get_subordinate_mgmt_entity {
410             my $self= shift;
411              
412             my $xml = $self->resolve_class_filter(classId => 'mgmtEntity', leadership => 'subordinate') or return;
413              
414             return (defined $xml->{outConfigs}->{mgmtEntity} ? $xml->{outConfigs}->{mgmtEntity} : undef);
415             }
416              
417             sub service_profile {
418             my ($self, $id)=@_;
419             return ( defined $self->{service_profile}->{$id} ? $self->{service_profile}->{$id} : $self->get_service_profile($id) )
420             }
421              
422             sub get_service_profile {
423             my ($self, $id) = @_;
424             return $self->get_service_profiles($id)
425             }
426              
427             sub get_service_profiles {
428             my ($self, $id) =@_;
429             return $self->_get_child_objects(id => $id, type => 'lsServer', class => 'Cisco::UCS::ServiceProfile',
430             uid => 'name', attr => 'service_profile', class_filter => { classId => 'lsServer' });
431             }
432              
433             sub interconnect {
434             my ($self, $id) = @_;
435             return ( defined $self->{interconnect}->{$id} ? $self->{interconnect}->{$id} : $self->get_interconnect($id) )
436             }
437              
438             sub get_interconnect {
439             my ($self, $id)=@_;
440             return $self->get_interconnects($id)
441             }
442              
443             sub get_interconnects {
444             my ($self, $id) =@_;
445             return $self->_get_child_objects(id => $id, type => 'networkElement', class => 'Cisco::UCS::Interconnect', attr => 'interconnect');
446             }
447              
448             sub blade {
449             my ($self, $id) = @_;
450             return ( defined $self->{blade}->{$id} ? $self->{blade}->{$id} : $self->get_blade($id) )
451             }
452              
453             sub get_blade {
454             my ($self, $id)=@_;
455             return $self->get_blades($id)
456             }
457              
458             sub get_blades {
459             my ($self, $id, %args) =@_;
460             return $self->_get_child_objects(id => $id, type => 'computeBlade', class => 'Cisco::UCS::Blade', attr => 'blade',
461             uid => 'serverId', class_filter => { classId => 'computeBlade' });
462             }
463              
464             sub chassis {
465             my ($self, $id) = @_;
466             return ( defined $self->{chassis}->{$id} ? $self->{chassis}->{$id} : $self->get_chassis($id) )
467             }
468              
469             sub get_chassis {
470             my ($self, $id)=@_;
471             return $self->get_chassiss($id)
472             }
473              
474             sub get_chassiss {
475             my ($self, $id) =@_;
476             return $self->_get_child_objects(id => $id, type => 'equipmentChassis', class => 'Cisco::UCS::Chassis', attr => 'chassis');
477             }
478              
479             sub full_state_backup {
480             my ( $self, %args ) = @_;
481              
482             $args{backup_type}= 'full-state';
483              
484             return ( $self->_backup( %args ) );
485             }
486             sub all_config_backup {
487             my ( $self, %args ) = @_;
488              
489             $args{backup_type}= 'config-all';
490              
491             return ( $self->_backup( %args ) );
492             }
493              
494             sub system_config_backup {
495             my ( $self, %args ) = @_;
496              
497             $args{backup_type}= 'config-system';
498              
499             return ( $self->_backup( %args ) );
500             }
501              
502             sub logical_config_backup {
503             my ( $self, %args ) = @_;
504              
505             $args{backup_type}= 'config-logical';
506              
507             return ( $self->_backup( %args ) );
508             }
509              
510             sub _backup {
511             my ( $self, %args ) = @_;
512              
513             unless( defined $args{backup_type} and
514             defined $args{backup_proto} and
515             defined $args{backup_host} and
516             defined $args{backup_target} and
517             defined $args{backup_passwd} and
518             defined $args{backup_username} )
519             {
520             $self->{error} = 'Bad argument list';
521             return
522             }
523              
524             $args{admin_state} = ( defined $args{admin_state} ? $args{admin_state} : 'enabled' );
525             $args{preserve_pooled_values} = ( defined $args{preserve_pooled_values} ? $args{preserve_pooled_values} : 'yes' );
526              
527             unless ( $args{backup_type} =~ /(config-all|full-state|config-system|config-logical)/i ) {
528             $self->{error} = "Bad backup type ($args{backup_type})";
529             return
530             }
531              
532             unless ( $args{backup_proto} =~ /^((t|s)?ftp)|(scp)$/i ) {
533             $self->{error} = "Bad backup proto' ($args{backup_proto})";
534             return
535             }
536              
537             my $address = $self->get_cluster_status->{address};
538              
539             my $data = <<"XML";
540            
541            
542            
543            
544            
545             proto="$args{backup_proto}" pwd="$args{backup_passwd}" remoteFile="$args{backup_target}"
546             rn="backup-$args{backup_host}" type="$args{backup_type}"
547             user="$args{backup_username}" policyOwner="local">
548            
549            
550            
551            
552            
553             XML
554              
555             my $xml = $self->_ucsm_request( $data ) or return;
556              
557             if ( defined $xml->{'errorCode'} ) {
558             my $self->{error} = ( defined $xml->{'errorDescr'}
559             ? $xml->{'errorDescr'}
560             : "Unspecified error"
561             );
562             return
563             }
564              
565             return 1;
566             }
567              
568             1;
569              
570             __END__