File Coverage

blib/lib/Ovirt.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Ovirt;
2              
3 1     1   697 use v5.10;
  1         3  
  1         67  
4 1     1   731 use LWP::UserAgent;
  1         339710  
  1         36  
5 1     1   21 use Scalar::Util qw(looks_like_number);
  1         1  
  1         108  
6 1     1   4 use Carp;
  1         1  
  1         44  
7 1     1   369 use XML::LibXML;
  0            
  0            
8             use XML::Hash::XS qw();
9             use Moo::Role;
10              
11             =head1 NAME
12              
13             Ovirt - Bindings for oVirt REST API
14              
15             =head1 VERSION
16              
17             Version 0.04
18              
19             =cut
20              
21             our $VERSION = '0.04';
22              
23             =head1 SYNOPSIS
24              
25             use Ovirt::VM;
26             use Ovirt::Template;
27             use Ovirt::Cluster;
28             use Ovirt::Host;
29             use Ovirt::Display;
30              
31             my %con = (
32             username => 'admin',
33             password => 'password',
34             manager => 'ovirt-mgr.example.com',
35             vm_output_attrs => 'id,name,state,description', # optional
36             cluster_output_attrs => 'id,name,cpu_id,cpu_arch,description', # optional
37             );
38              
39             my $vm = Ovirt::VM ->new(%con);
40             my $cluster = Ovirt::Cluster ->new(%con);
41             my $template = Ovirt::Template ->new(%con);
42             my $host = Ovirt::Host ->new(%con);
43              
44             # return xml output
45             print $vm ->list_xml;
46             print $cluster ->list_xml;
47             print $template->list_xml;
48             print $host ->list_xml;
49              
50             # list attributes
51             print $vm ->list;
52             print $cluster ->list;
53             print $template->list;
54             print $host ->list;
55              
56             # create, remove vm
57             $vm->create('vm1','Default','CentOS7');
58             $vm->remove('2d83bb51-9a77-432d-939c-35be207017b9');
59            
60             # add/remove/list vm's nic and disk
61            
62             # start, stop, reboot, migrate vm
63             $vm->start ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
64             $vm->stop ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
65             $vm->reboot ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
66             $vm->migrate ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
67              
68             # the output also available in hash
69             # for example to print all vm name and state
70             my $hash = $vm->hash_output;
71             for my $array (keys $hash->{vm}) {
72             print $hash->{vm}[$array]->{name} . " " .
73             $hash->{vm}[$array]->{status}->{state};
74             }
75            
76             # we can also specify specific vm 'id' when initiating an object
77             # so we can direct access the element for specific vm
78             print $vm->hash_output->{name};
79             print $vm->hash_output->{cluster}->{id};
80            
81             # Generate display configuration for remote viewer
82             my $display = Ovirt::Display->new(%con);
83             print $display->generate();
84              
85             sample spice configuration output :
86             [virt-viewer]
87             type=spice
88             host=192.168.1.152
89             port=-1
90             password=+cnsq458Oq6T
91             # Password is valid for 300 seconds.
92             tls-port=5902
93             fullscreen=0
94             title=C1 : %d - Press SHIFT+F12 to Release Cursor
95             enable-smartcard=0
96             enable-usb-autoshare=1
97             delete-this-file=1
98             usb-filter=-1,-1,-1,-1,0
99             tls-ciphers=DEFAULT
100             host-subject=O=example.com,CN=192.168.1.152
101             ca=-----BEGIN CERTIFICATE-----\n -- output removed -- S2fE=\n-----END CERTIFICATE-----\n
102             toggle-fullscreen=shift+f11
103             release-cursor=shift+f12
104             secure-attention=ctrl+alt+end
105             secure-channels=main;inputs;cursor;playback;record;display;usbredir;smartcard
106              
107             you can save it to a file then use remote viewer to open it:
108             $ remote-viewer [your saved file].vv
109              
110             =head1 Attributes
111              
112             notes :
113             ro = read only, can be specified during initialization
114             rw = read write, user can set this attribute
115             rwp = read write protected, for internal class
116              
117             username = (ro, required) store Ovirt username
118             password = (ro, required) store Ovirt password
119             manager = (ro, required) store Ovirt Manager address
120             port = (ro) store Ovirt Manager's port (must be number)
121             id = (ro) store object id, if it's provided during initialization,
122             the rest api output will only contain attributes for this id
123             domain = (ro) store Ovirt Domain (default domain : internal)
124             ssl = (ro) if yes, use https (default is yes)
125             ssl_verify = (ro) disable host verification (default is no)
126             log_severity = (ro) store log severity level, valid value ERROR|OFF|FATAL|INFO|DEBUG|TRACE|ALL|WARN
127             (default is INFO)
128             not_available = (rw) store undef or empty output string, default to 'N/A'
129             url = (rwp) store final url to be requested to Ovirt
130             root_url = (rwp) store url on each object
131             log = (rwp) store log from log4perl
132             xml_output = (rwp) store xml output from Ovirt RestAPI
133             hash_output = (rwp) store hash output converted from xml output
134              
135             =cut
136              
137             has [qw/url root_url xml_output hash_output log/] => ( is => 'rwp' );
138             has [qw/id/] => ( is => 'ro' );
139             has [qw/username password manager/] => ( is => 'ro', required => 1 );
140              
141             has 'port' => ( is => 'ro',
142             isa =>
143             sub {
144             croak "$_[0] is not a number!" unless looks_like_number $_[0];
145             }
146             );
147            
148             has 'domain' => ( is => 'ro', default => 'internal' );
149             has 'ssl' => ( is => 'ro', default => 'yes' );
150             has 'ssl_verify' => ( is => 'ro',
151             isa => sub {
152             my $ssl_verify = $_[0];
153             $ssl_verify = lc ($ssl_verify);
154            
155             if ($ssl_verify eq 'yes') {
156             $ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 1;
157             }
158             elsif ($ssl_verify eq 'no') {
159             $ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0;
160             }
161             else {
162             croak "ssl_verify valid argument is yes/no";
163             }
164             },
165             default => sub { $ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0; return 'no'; } );
166             has 'not_available' => ( is => 'rw', default => 'N/A' );
167              
168             has 'log_severity' => (is => 'ro',
169             isa => sub { croak "log severity value not valid\n"
170             unless $_[0] =~ /(ERROR|OFF|FATAL|INFO|DEBUG|TRACE|ALL|WARN)/;
171             },
172             default => 'INFO'
173             );
174              
175              
176             =head1 SUBROUTINES/METHODS
177              
178             You may want to check :
179             - perldoc Ovirt::VM
180             - perldoc Ovirt::Template
181             - perldoc Ovirt::Cluster
182             - perldoc Ovirt::Host
183             - perldoc Ovirt::Display
184             - perldoc Ovirt::DataCenter
185             - perldoc Ovirt::Storage
186             - perldoc Ovirt::Network
187              
188             =head2 BUILD
189              
190             The Constructor, build logging, call pass_log_obj method
191             =cut
192              
193             sub BUILD {
194             my $self = shift;
195            
196             $self->pass_log_obj();
197             }
198              
199             =head2 pass_log_obj
200              
201             it will build the log which stored to $self->log
202             you can assign the severity level by assigning the log_severity
203            
204             # output to console / screen
205             # format :
206             # %d = current date with yyyy/MM/dd hh:mm:ss format
207             # %p = Log Severity
208             # %P = pid of the current process
209             # %L = Line number within the file where the log statement was issued
210             # %M = Method or function where the logging request was issued
211             # %m = The message to be logged
212             # %n = Newline (OS-independent)
213            
214             =cut
215              
216             sub pass_log_obj {
217             my $self = shift;
218            
219             # skip if already set
220             return if $self->log;
221            
222             my $severity = $self->log_severity;
223             my $log_conf =
224             qq /
225             log4perl.logger = $severity, Screen
226             log4perl.appender.Screen = Log::Log4perl::Appender::Screen
227             log4perl.appender.Screen.stderr = 0
228             log4perl.appender.Screen.layout = PatternLayout
229             log4perl.appender.Screen.layout.ConversionPattern = %d || %p || %P || %L || %M || %m%n
230             /;
231            
232             use Log::Log4perl;
233             Log::Log4perl::init(\$log_conf);
234             my $log = Log::Log4perl->get_logger();
235             $self->_set_log($log);
236             }
237              
238             =head2 base_url
239              
240             return the base url
241             =cut
242              
243             sub base_url {
244             my $self = shift;
245            
246             # '%40' is '@'
247             my $url = $self->username . '%40'. $self->domain . ":" .$self->password .
248             "\@" . $self->manager;
249            
250             if ($self->port) {
251             $url = $self->username . '%40'. $self->domain . ":" .$self->password .
252             "\@" . $self->manager . ":" . $self->port;
253             }
254            
255             if ($self->ssl eq 'yes') {
256             $url = "https://" . $url;
257             }
258             elsif ($self->ssl eq 'no') {
259             $url = "http://" . $url;
260             }
261            
262             $self->log->debug($url);
263             return $url;
264             }
265              
266             =head2 api_url
267              
268             build the final url
269             =cut
270              
271             sub api_url {
272             my $self = shift;
273            
274             # root_url is being set in each particular library
275             my $url = $self->base_url . $self->root_url;
276            
277             $self->log->debug("$url");
278             $self->_set_url($url);
279             }
280              
281             =head2 get_api_response
282              
283             get xml response, store to xml_output.
284             the xml output is also converted to hash and stored
285             at hash_output attribute.
286             xml2hash somehow complaining the xml declaration, so we
287             need to skip it and use 'toString' method on the xml string
288             parameter.
289             =cut
290              
291             sub get_api_response {
292             my $self = shift;
293            
294             my $ua = LWP::UserAgent->new();
295             my $tx = $ua->get($self->api_url);
296            
297             if ($tx->is_success) {
298            
299             local $XML::LibXML::skipXMLDeclaration = 1;
300             my $parser = XML::LibXML->new();
301             my $xml_string = $parser->parse_string($tx->decoded_content);
302             $self->_set_xml_output($xml_string);
303            
304             #store to hash
305             my $conv = XML::Hash::XS->new(utf8 => 1, encoding => 'utf8');
306             my $hash = $conv->xml2hash($xml_string->toString, encoding => 'cp1251');
307             $self->_set_hash_output($hash);
308            
309             }
310             else {
311             my $err = $tx->status_line;
312             $self->log->debug("LWP Error : " . $err);
313             $self->log->debug("LWP Decoded Content :" . $tx->decoded_content);
314            
315             croak "LWP Status line : " . $err;
316             croak "LWP Decoded Content :" . $tx->decoded_content;
317             }
318             }
319              
320             =head2 trim
321              
322             trim function to remove whitespace from the start and end of the string
323             =cut
324              
325             sub trim()
326             {
327             my ($self, $string) = @_;
328             $string =~ s/^\s+|\s+$//g;
329             return $string;
330             }
331              
332             =head2 ltrim
333              
334             Left trim function to remove leading whitespace
335             =cut
336              
337             sub ltrim()
338             {
339             my ($self, $string) = @_;
340             $string =~ s/^\s+//;
341             return $string;
342             }
343              
344             =head2 rtrim
345              
346             Right trim function to remove leading whitespace
347             =cut
348              
349             sub rtrim()
350             {
351             my ($self, $string) = @_;
352             $string =~ s/\s+$//;
353             return $string;
354             }
355              
356             =head1 AUTHOR
357              
358             "Heince Kurniawan", C<< <"heince at cpan.org"> >>
359              
360             =head1 BUGS
361              
362             Please report any bugs or feature requests to C, or through
363             the web interface at L. I will be notified, and then you'll
364             automatically be notified of progress on your bug as I make changes.
365              
366              
367              
368              
369             =head1 SUPPORT
370              
371             You can find documentation for this module with the perldoc command.
372              
373             perldoc Ovirt
374              
375              
376             You can also look for information at:
377              
378             =over 4
379              
380             =item * RT: CPAN's request tracker (report bugs here)
381              
382             L
383              
384             =item * AnnoCPAN: Annotated CPAN documentation
385              
386             L
387              
388             =item * CPAN Ratings
389              
390             L
391              
392             =item * Search CPAN
393              
394             L
395              
396             =back
397              
398              
399             =head1 ACKNOWLEDGEMENTS
400              
401              
402             =head1 LICENSE AND COPYRIGHT
403              
404             Copyright 2015 "Heince Kurniawan".
405              
406             This program is free software; you can redistribute it and/or modify it
407             under the terms of the the Artistic License (2.0). You may obtain a
408             copy of the full license at:
409              
410             L
411              
412             Any use, modification, and distribution of the Standard or Modified
413             Versions is governed by this Artistic License. By using, modifying or
414             distributing the Package, you accept this license. Do not use, modify,
415             or distribute the Package, if you do not accept this license.
416              
417             If your Modified Version has been derived from a Modified Version made
418             by someone other than you, you are nevertheless required to ensure that
419             your Modified Version complies with the requirements of this license.
420              
421             This license does not grant you the right to use any trademark, service
422             mark, tradename, or logo of the Copyright Holder.
423              
424             This license includes the non-exclusive, worldwide, free-of-charge
425             patent license to make, have made, use, offer to sell, sell, import and
426             otherwise transfer the Package with respect to any patent claims
427             licensable by the Copyright Holder that are necessarily infringed by the
428             Package. If you institute patent litigation (including a cross-claim or
429             counterclaim) against any party alleging that the Package constitutes
430             direct or contributory patent infringement, then this Artistic License
431             to you shall terminate on the date that such litigation is filed.
432              
433             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
434             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
435             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
436             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
437             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
438             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
439             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
440             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
441              
442              
443             =cut
444              
445             1; # End of Ovirt