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