File Coverage

blib/lib/Ovirt/VM.pm
Criterion Covered Total %
statement 8 430 1.8
branch 0 238 0.0
condition 0 49 0.0
subroutine 3 28 10.7
pod n/a
total 11 745 1.4


line stmt bran cond sub pod time code
1             package Ovirt::VM;
2              
3 1     1   1113 use v5.10;
  1         2  
4 1     1   4 use Carp;
  1         0  
  1         42  
5 1     1   441 use Moo;
  1         8995  
  1         5  
6              
7             with 'Ovirt';
8             our $VERSION = '0.04';
9              
10             =head1 NAME
11              
12             Ovirt::VM - Bindings for oVirt VM API
13              
14             =head1 VERSION
15              
16             Version 0.04
17              
18             =cut
19              
20             =head1 SYNOPSIS
21              
22             use Ovirt::VM;
23              
24             my %con = (
25             username => 'admin',
26             password => 'password',
27             manager => 'ovirt-mgr.example.com',
28             vm_output_attrs => 'id,name,state,description', # optional
29             vm_output_delimiter => '||', # optional
30             );
31              
32             my $vm = Ovirt::VM->new(%con);
33              
34             # return xml / json output
35             print $vm->list_xml;
36             print $vm->list_json;
37            
38             # list vm separated by new line
39             # return attributes specified on 'vm_output_attrs'
40             print $vm->list;
41            
42             # list specific vm
43             print $vm->list('b4738b0f-b73d-4a66-baa8-2ba465d63132');
44            
45             # create, remove, get all running vm, stop all vm
46             $vm->create('vm1','Default','CentOS7');
47             $vm->remove('2d83bb51-9a77-432d-939c-35be207017b9');
48             $vm->get_running();
49             $vm->stop_all();
50            
51             # start, stop, reboot, migrate vm
52             $vm->start ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
53             $vm->stop ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
54             $vm->reboot ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
55             $vm->migrate ('b4738b0f-b73d-4a66-baa8-2ba465d63132');
56              
57             # get vm's statistics
58             $vm->get_statistics($vmid);
59            
60             # add/remove/list nic and disk
61             $vm->add_disk('6efc0cfa-8495-4a96-93e5-ee490328cf48', # vm id
62             'virtio', # driver interface
63             'cow', # format
64             '1073741824', # size
65             'mydisk1', # disk name
66             '9b952bdc-b7ec-4673-84b0-477b48945a9a' # storage domain id
67             );
68              
69             $vm->add_nic('6efc0cfa-8495-4a96-93e5-ee490328cf48', # vm id
70             'virtio', # driver interface
71             'nic1', # nic name
72             'rhevm' # network name
73             );
74              
75             # Output also available in hash
76             # for example to print all vm name and state
77             my $hash = $vm->hash_output;
78             for my $array (keys $hash->{vm}) {
79             print $hash->{vm}[$array]->{name} . " " .
80             $hash->{vm}[$array]->{status}->{state};
81             }
82            
83             # we can also specify specific vm 'id' when initiating an object
84             # so we can direct access the element for specific vm
85             print $vm->hash_output->{name};
86             print $vm->hash_output->{cluster}->{id};
87              
88             =head1 Attributes
89              
90             Other attributes is also inherited from Ovirt.pm
91             Check 'perldoc Ovirt' for detail
92            
93             notes :
94             ro = read only, can be specified only during initialization
95             rw = read write, user can set this attribute
96             rwp = read write protected, for internal class
97            
98             vm_url = (ro) store default vm url path
99             vm_cdrom_xml = (ro) store xml to be post on start/stop vm action with boot device set to cdrom
100             vm_hd_xml = (ro) store xml to be post on start/stop vm action with boot device set to hd
101             vm_boot_dev = (rw) set boot device when start / stopping vm, default to hd
102             vm_output_delimiter = (rw) specify output delimiter between attribute, default is '||'
103             vm_output_attrs = (rw) store vm attributes to be returned, default is (id, name, state)
104             supported attributes :
105             id name
106             memory description
107             state cpu_cores
108             cpu_sockets cpu_arch
109             cpu_shares os_type
110             boot_dev ha_enabled
111             ha_priority display_type
112             display_address display_port
113             cluster_id template_id
114             stop_time creation_time
115             timezone usb_enabled
116             host_id display_host_subject
117            
118             =cut
119              
120             has 'vm_url' => ( is => 'ro', default => '/api/vms' );
121             has 'vm_output_attrs' => ( is => 'rw', default => 'id,name,state',
122             isa =>
123             sub
124             {
125             # store all output attribute into array split by ','
126             # $_[0] is the arguments spefied during initialization
127             my @attrs = split ',' => $_[0];
128            
129             croak "vm_output_attrs can't be empty"
130             unless @attrs;
131            
132             # check if provided attribute is valid / supported
133             my @supported_attr = qw |
134             id name
135             memory description
136             state cpu_cores
137             cpu_sockets cpu_arch
138             cpu_shares os_type
139             boot_dev ha_enabled
140             ha_priority display_type
141             display_address display_port
142             cluster_id template_id
143             stop_time creation_time
144             timezone usb_enabled
145             host_id display_host_subject
146             |;
147             for my $attr (@attrs)
148             {
149             $attr = lc ($attr);
150             $attr = Ovirt->trim($attr);
151             croak "Attribute $attr is not valid / supported"
152             unless grep { /\b$attr\b/ } @supported_attr;
153             }
154             });
155            
156             has 'vm_output_delimiter' => ( is => 'rw', default => '||' );
157             has 'vm_cdrom_xml' => ( is => 'ro', default => '');
158             has 'vm_hd_xml' => ( is => 'ro', default => '');
159             has 'vm_boot_dev' => ( is => 'rw',
160             isa => sub
161             {
162             my $dev = $_[0];
163             $dev = lc ($dev);
164            
165             croak "supported boot device is hd or cdrom"
166             unless $dev =~ /^(hd|cdrom)/;
167             },
168             default => 'hd');
169              
170             =head1 SUBROUTINES/METHODS
171              
172             =head2 BUILD
173              
174             The Constructor, build logging, call pass_log_obj method
175             Built root_url with vm_url
176             set output with get_api_response method from Ovirt.pm
177             check if output attributes is valid
178             =cut
179              
180             sub BUILD
181             {
182 0     0     my $self = shift;
183            
184 0           $self->pass_log_obj;
185            
186 0 0         if ($self->id)
187             {
188 0           $self->_set_root_url($self->vm_url . '/' . $self->id);
189             }
190             else
191             {
192 0           $self->_set_root_url($self->vm_url);
193             }
194            
195 0           $self->get_api_response();
196             }
197              
198             =head2 vm_action_output
199              
200             this method handle the output e.g start / stop vm
201             required arguments ($xml), output passed by start/stop method
202             =cut
203              
204             sub vm_action_output
205             {
206 0     0     my $self = shift;
207            
208             # xml output from action (start,stop,reboot, etc)
209 0           my $xml = shift;
210            
211 0           $self->log->debug($xml);
212            
213 0           return $xml;
214             }
215              
216             =head2 remove
217              
218             remove vm
219              
220             =cut
221              
222             sub remove
223             {
224 0     0     my $self = shift;
225            
226 0   0       my $vmid = shift || undef;
227 0           my $move_url;
228            
229             # set the move final url
230 0 0         if ($self->id)
231             {
232 0           $move_url = $self->url;
233             }
234             else
235             {
236 0 0         if ($vmid)
237             {
238 0           my $is_valid = $self->is_vmid_valid($vmid);
239 0 0         croak "vm id not found" unless $is_valid;
240            
241 0           $move_url = $self->url . "/$vmid";
242             }
243             else
244             {
245 0           croak "vm id is required";
246             }
247             }
248            
249 0           $self->log->debug("move action url = $move_url");
250            
251             # set user agent
252 0           my $ua = LWP::UserAgent->new();
253 0           my $action = $ua->delete($move_url);
254            
255 0           my $parser = XML::LibXML->new();
256 0           my $output = $parser->parse_string($action->decoded_content);
257            
258 0           $self->vm_action_output($output);
259             }
260              
261             =head2 list_snapshot
262              
263             list vm's snapshot
264             required argument ('vm id')
265              
266             =cut
267              
268             sub list_snapshot
269             {
270 0     0     my $self = shift;
271 0   0       my $vmid = shift || undef;
272            
273 0           my $snapshot_url;
274            
275             # don't proceed if vm not exist
276 0           my $is_valid = $self->is_vmid_valid($vmid);
277 0 0         croak "vm id not found"
278             unless $is_valid;
279            
280             # set the url
281 0           $snapshot_url = $self->url . "/$vmid/snapshots";
282            
283 0           $self->get_api_output($snapshot_url);
284             }
285              
286             =head2 create_snapshot
287              
288             required arguments ('vm id', 'snapshot description', 'memorystate')
289             memorystate can be true/false
290            
291             $vm->create_snapshot('ec191b8c-b3ec-41b9-9f9f-6d92bc32732b', 'test3', 'false');
292              
293             =cut
294              
295             sub create_snapshot
296             {
297 0     0     my $self = shift;
298            
299 0           my ($vmid, $description, $memorystate) = @_;
300            
301             # don't proceed if vm not exist
302 0           my $is_valid = $self->is_vmid_valid($vmid);
303 0 0         croak "vm id not found"
304             unless $is_valid;
305            
306             # set the url to be post
307 0           my $snapshot_url = $self->url . "/$vmid/snapshots";
308            
309 0 0         croak "memory state not supported, use true/false."
310             unless $memorystate =~ /\b(true|false)\b/;
311            
312 0 0         croak "snapshot description required"
313             unless $description;
314            
315             # set xml string to be post
316 0           my $xml = <
317            
318             $description
319             $memorystate
320            
321            
322             EOF
323            
324 0           $self->log->debug($xml);
325            
326             # set user agent
327 0           my $ua = LWP::UserAgent->new();
328 0           my $action = $ua->post($snapshot_url, Content_Type => 'application/xml', Content => $xml);
329            
330 0           my $parser = XML::LibXML->new();
331 0           my $output = $parser->parse_string($action->decoded_content);
332            
333 0           $self->log->debug($output);
334            
335 0           return $output;
336             }
337              
338             =head2 delete_snapshot
339              
340             required arguments ('vm id', 'snapshot id')
341             $vm->delete_snapshot('ec191b8c-b3ec-41b9-9f9f-6d92bc32732b', 'b0e9198c-a404-4659-8a82-421ae85f54e1');
342              
343             =cut
344              
345             sub delete_snapshot
346             {
347 0     0     my $self = shift;
348            
349 0           my ($vmid, $snapshot_id) = @_;
350            
351 0           my $snapshot_url;
352            
353             # don't proceed if vm not exist
354 0           my $is_valid = $self->is_vmid_valid($vmid);
355 0 0         croak "vm id not found"
356             unless $is_valid;
357            
358             # set the url to be post
359 0           $snapshot_url = $self->url . "/$vmid/snapshots/$snapshot_id";
360            
361 0 0         croak "snapshot id required"
362             unless $snapshot_id;
363            
364 0           $self->log->debug("delete snapshot action url = $snapshot_url");
365            
366             # set user agent
367 0           my $ua = LWP::UserAgent->new();
368 0           my $action = $ua->delete($snapshot_url);
369            
370 0           my $parser = XML::LibXML->new();
371 0           my $output = $parser->parse_string($action->decoded_content);
372            
373 0           $self->vm_action_output($output);
374             }
375              
376             =head2 list_disk
377              
378             list vm's disk
379             required argument ('vm id')
380              
381             =cut
382              
383             sub list_disk
384             {
385 0     0     my $self = shift;
386 0   0       my $vmid = shift || undef;
387            
388 0           my $disk_url;
389            
390             # don't proceed if vm not exist
391 0           my $is_valid = $self->is_vmid_valid($vmid);
392 0 0         croak "vm id not found"
393             unless $is_valid;
394            
395             # set the url
396 0           $disk_url = $self->url . "/$vmid/disks";
397            
398 0           $self->get_api_output($disk_url);
399             }
400              
401             =head2 add_disk
402              
403             create vm disk
404             required arguments ('vm id', 'driver type', 'format' , 'size in bytes', 'disk name', 'storage domain id' )
405            
406             supported driver type :
407             - virtio
408             - ide
409            
410             supported format:
411             - cow
412             - raw
413            
414             use Ovirt::Storage to get more information for the storage domain id
415             see 'perldoc Ovirt::Storage' for example
416            
417             example :
418             $vm->add_disk('6efc0cfa-8495-4a96-93e5-ee490328cf48', # vm id
419             'virtio', # driver interface
420             'cow', # format
421             '1073741824', # size
422             'mydisk1', # disk name
423             '9b952bdc-b7ec-4673-84b0-477b48945a9a' # storage domain id
424             );
425              
426             =cut
427              
428             sub add_disk
429             {
430 0     0     my $self = shift;
431            
432 0           my ($vmid, $driver, $format, $size, $disk_name, $storage_domain_id) = @_;
433            
434 0           my $disk_url;
435            
436             # don't proceed if vm not exist
437 0           my $is_valid = $self->is_vmid_valid($vmid);
438 0 0         croak "vm id not found"
439             unless $is_valid;
440            
441             # set the url to be post
442 0           $disk_url = $self->url . "/$vmid/disks";
443            
444 0 0         croak "driver type not supported, use virtio/e1000/rtl8139."
445             unless $driver =~ /\b(virtio|ide)\b/;
446            
447 0 0         croak "disk format required"
448             unless $format;
449            
450 0 0         croak "disk size required"
451             unless $size;
452            
453 0 0         croak "disk name required"
454             unless $disk_name;
455            
456 0 0         croak "storage domain id required"
457             unless $storage_domain_id;
458            
459             # set xml string to be post
460 0           my $xml = <
461            
462             $disk_name
463            
464            
465            
466             $size
467             $driver
468             $format
469             false
470            
471             EOF
472            
473 0           $self->log->debug($xml);
474            
475             # set user agent
476 0           my $ua = LWP::UserAgent->new();
477 0           my $action = $ua->post($disk_url, Content_Type => 'application/xml', Content => $xml);
478            
479 0           my $parser = XML::LibXML->new();
480 0           my $output = $parser->parse_string($action->decoded_content);
481            
482 0           $self->log->debug($output);
483            
484 0           return $output;
485             }
486              
487             =head2 remove_disk
488              
489             remove vm disk
490             required arguments ('vm id', 'disk id')
491             use method list_disk to get the 'disk id' for particular vm
492            
493             example :
494             $vm->remove_disk('6efc0cfa-8495-4a96-93e5-ee490328cf48', '99a4f585-9a39-465b-ae24-0068bd4eaad6');
495            
496             =cut
497              
498             sub remove_disk
499             {
500 0     0     my $self = shift;
501            
502 0           my ($vmid, $disk_id) = @_;
503            
504 0           my $disk_url;
505            
506             # don't proceed if vm not exist
507 0           my $is_valid = $self->is_vmid_valid($vmid);
508 0 0         croak "vm id not found"
509             unless $is_valid;
510            
511             # set the url to be post
512 0           $disk_url = $self->url . "/$vmid/disks/$disk_id";
513            
514 0 0         croak "disk id required"
515             unless $disk_id;
516            
517 0           $self->log->debug("move action url = $disk_url");
518            
519             # set user agent
520 0           my $ua = LWP::UserAgent->new();
521 0           my $action = $ua->delete($disk_url);
522            
523 0           my $parser = XML::LibXML->new();
524 0           my $output = $parser->parse_string($action->decoded_content);
525            
526 0           $self->vm_action_output($output);
527             }
528              
529             =head2 list_nic
530              
531             list vm's nic
532             required argument ('vm id')
533              
534             =cut
535              
536             sub list_nic
537             {
538 0     0     my $self = shift;
539 0   0       my $vmid = shift || undef;
540            
541 0           my $nic_url;
542            
543             # don't proceed if vm not exist
544 0           my $is_valid = $self->is_vmid_valid($vmid);
545 0 0         croak "vm id not found"
546             unless $is_valid;
547            
548             # set the url
549 0           $nic_url = $self->url . "/$vmid/nics";
550            
551 0           $self->get_api_output($nic_url);
552             }
553              
554             =head2 add_nic
555              
556             create vm nic
557             required arguments ('vm id', 'driver type', 'nic name', 'network name' )
558            
559             supported driver type :
560             - virtio
561             - e1000
562             - rtl8139
563            
564             use Ovirt::Network to get more information for the network name
565             see 'perldoc Ovirt::Network' for example
566            
567             example :
568             $vm->add_nic('6efc0cfa-8495-4a96-93e5-ee490328cf48', 'virtio', 'nic1', 'rhevm');
569              
570             =cut
571              
572             sub add_nic
573             {
574 0     0     my $self = shift;
575            
576 0           my ($vmid, $driver, $nic_name, $network_name) = @_;
577            
578 0           my $nic_url;
579            
580             # don't proceed if vm not exist
581 0           my $is_valid = $self->is_vmid_valid($vmid);
582 0 0         croak "vm id not found"
583             unless $is_valid;
584            
585             # set the url to be post
586 0           $nic_url = $self->url . "/$vmid/nics";
587            
588 0 0         croak "driver type not supported, use virtio/e1000/rtl8139."
589             unless $driver =~ /\b(virtio|e1000|rtl8139)\b/;
590            
591 0 0         croak "nic name required"
592             unless $nic_name;
593            
594 0 0         croak "network name required"
595             unless $network_name;
596            
597             # set xml string to be post
598 0           my $xml = <
599            
600             $driver
601             $nic_name
602            
603             $network_name
604            
605            
606             EOF
607            
608 0           $self->log->debug($xml);
609            
610             # set user agent
611 0           my $ua = LWP::UserAgent->new();
612 0           my $action = $ua->post($nic_url, Content_Type => 'application/xml', Content => $xml);
613            
614 0           my $parser = XML::LibXML->new();
615 0           my $output = $parser->parse_string($action->decoded_content);
616            
617 0           $self->log->debug($output);
618            
619 0           return $output;
620             }
621              
622             =head2 remove_nic
623              
624             remove vm nic
625             required arguments ('vm id', 'nic id')
626             use method list_nic to get the 'nic id' for particular vm
627            
628             example :
629             $vm->remove_nic('6efc0cfa-8495-4a96-93e5-ee490328cf48', '82416c65-d8b2-4c82-8134-0e73e5ead624');
630            
631             =cut
632              
633             sub remove_nic
634             {
635 0     0     my $self = shift;
636            
637 0           my ($vmid, $nic_id) = @_;
638            
639 0           my $nic_url;
640            
641             # don't proceed if vm not exist
642 0           my $is_valid = $self->is_vmid_valid($vmid);
643 0 0         croak "vm id not found"
644             unless $is_valid;
645            
646             # set the url to be post
647 0           $nic_url = $self->url . "/$vmid/nics/$nic_id";
648            
649 0 0         croak "nic id required"
650             unless $nic_id;
651            
652 0           $self->log->debug("move action url = $nic_url");
653            
654             # set user agent
655 0           my $ua = LWP::UserAgent->new();
656 0           my $action = $ua->delete($nic_url);
657            
658 0           my $parser = XML::LibXML->new();
659 0           my $output = $parser->parse_string($action->decoded_content);
660            
661 0           $self->vm_action_output($output);
662             }
663              
664             =head2 create
665              
666             create vm using template
667             required arguments (vm name, cluster name, template name)
668             optional argument 'memory in bytes'
669             example :
670             # with memory specified
671             $vm->create('vm1', 'production_cluster', 'RHEL7', 1073741824);
672            
673             # without memory specified (will be based on template's memory)
674             $vm->create('vm1', 'production_cluster', 'RHEL7');
675            
676             =cut
677              
678             sub create
679             {
680 0     0     my $self = shift;
681            
682 0           my ($vm_name, $cluster_name, $template_name, $memory) = @_;
683 0 0         croak "vm name required" unless $vm_name;
684 0 0         croak "cluster name required" unless $cluster_name;
685 0 0         croak "template name required" unless $template_name;
686            
687 0           $vm_name = $self->trim($vm_name);
688 0           $cluster_name = $self->trim($cluster_name);
689 0           $template_name = $self->trim($template_name);
690            
691 0           $self->log->debug("vm name = $vm_name");
692 0           $self->log->debug("cluster name = $cluster_name");
693 0           $self->log->debug("template name = $template_name");
694            
695             # create xml to be post
696 0           my $xml;
697            
698 0 0         if ($memory)
699             {
700 0           $memory = $self->trim($memory);
701 0 0         croak "Memory did not look like number" unless $memory =~ /^\d+$/;
702            
703 0           $self->log->debug("memory = $memory");
704            
705 0           $xml = <
706            
707             $vm_name
708            
709             $cluster_name
710            
711            
714             $memory
715            
716            
717            
718            
719             EOF
720              
721             }
722             else
723             {
724 0           $self->log->debug("memory not specified");
725 0           $xml = <
726            
727             $vm_name
728            
729             $cluster_name
730            
731            
734            
735            
736            
737            
738             EOF
739             }
740            
741 0           $self->log->debug($xml);
742            
743 0           my $create_url = $self->base_url . $self->vm_url;
744 0           $self->log->debug("create url = $create_url");
745            
746             # set user agent
747 0           my $ua = LWP::UserAgent->new();
748 0           my $action = $ua->post($create_url, Content_Type => 'application/xml', Content => $xml);
749            
750 0           my $parser = XML::LibXML->new();
751 0           my $output = $parser->parse_string($action->decoded_content);
752            
753 0           $self->log->debug($output);
754            
755 0           return $output;
756             }
757              
758             =head2 start
759              
760             start vm
761             required arguments ($vmid)
762             if $self->id is set during initialization, argument is not required
763             =cut
764              
765             sub start
766             {
767 0     0     my $self = shift;
768            
769 0   0       my $vmid = shift || undef;
770 0           my $start_url;
771            
772             # set the start final url
773 0 0         if ($self->id)
774             {
775 0           $start_url = $self->url . "/start";
776             }
777             else
778             {
779 0 0         if ($vmid)
780             {
781 0           my $is_valid = $self->is_vmid_valid($vmid);
782 0 0         croak "vm id not found" unless $is_valid;
783            
784 0           $start_url = $self->url . "/$vmid/start";
785             }
786             else
787             {
788 0           croak "vm id is required";
789             }
790             }
791            
792 0           $self->log->debug("start action url = $start_url");
793            
794             # set user agent
795 0           my $ua = LWP::UserAgent->new();
796 0           my $action;
797            
798 0 0         if ($self->vm_boot_dev eq 'hd')
799             {
800 0           $action = $ua->post($start_url, Content_Type => 'application/xml', Content => $self->vm_hd_xml);
801             }
802             else
803             {
804 0           $action = $ua->post($start_url, Content_Type => 'application/xml', Content => $self->vm_cdrom_xml);
805             }
806            
807 0           my $parser = XML::LibXML->new();
808 0           my $output = $parser->parse_string($action->decoded_content);
809            
810 0           $self->vm_action_output($output);
811             }
812              
813             =head2 stop
814              
815             stop vm
816             required arguments ($vmid)
817             if $self->id is set during initialization, argument is not required
818             =cut
819              
820             sub stop
821             {
822 0     0     my $self = shift;
823            
824 0   0       my $vmid = shift || undef;
825 0           my $stop_url;
826            
827             # set the stop final url
828 0 0         if ($self->id)
829             {
830 0           $stop_url = $self->url . "/stop";
831             }
832             else
833             {
834 0 0         if ($vmid)
835             {
836 0           my $is_valid = $self->is_vmid_valid($vmid);
837 0 0         croak "vm id not found" unless $is_valid;
838 0           $stop_url = $self->url . "/$vmid/stop";
839             }
840             else
841             {
842 0           croak "vm id is required";
843             }
844             }
845            
846 0           $self->log->debug("stop action url = $stop_url");
847            
848             # set user agent
849 0           my $ua = LWP::UserAgent->new();
850 0           my $action;
851            
852 0 0         if ($self->vm_boot_dev eq 'hd')
853             {
854 0           $action = $ua->post($stop_url, Content_Type => 'application/xml', Content => $self->vm_hd_xml);
855             }
856             else
857             {
858 0           $action = $ua->post($stop_url, Content_Type => 'application/xml', Content => $self->vm_cdrom_xml);
859             }
860            
861 0           my $parser = XML::LibXML->new();
862 0           my $output = $parser->parse_string($action->decoded_content);
863            
864 0           $self->vm_action_output($output);
865             }
866              
867             =head2 stop_all
868              
869             stop all vm if vm state = 'up'
870             do not set 'id' during initialization because it will not return all vm
871             this will loop using stop method
872             $vm->stop_all();
873            
874             =cut
875              
876             sub stop_all
877             {
878 0     0     my $self = shift;
879            
880             # croak if id is defined because it will not return all vm
881 0 0         croak "Don't set id during initialization since it will not return all vm"
882             if $self->id;
883            
884 0           my $running_vms = $self->get_running();
885            
886 0 0         if ($running_vms)
887             {
888 0           my @vms = split '\n' => $running_vms;
889            
890 0           for my $vm (@vms)
891             {
892 0           $self->log->debug($vm);
893 0           my @vmid = split ',' => $vm;
894 0           $self->stop($vmid[0]);
895             }
896             }
897             else
898             {
899 0           print "no vm in up state\n";
900 0           exit;
901             }
902             }
903              
904             =head2 get_running
905              
906             return vmid,name if state = 'up'
907             each vm separated by new line
908             do not set 'id' during initialization because it will not return all vm
909             $vm->get_running();
910            
911             =cut
912              
913             sub get_running
914             {
915 0     0     my $self = shift;
916            
917             # store the output and return it at the end
918 0           my $output;
919            
920             # store id, name to array to be looped
921 0           my @attrs = qw |id name|;
922            
923             # store the last element to escape the vm_output_delimeter
924 0           my $last_element = pop (@attrs);
925 0           $self->log->debug("last element = $last_element");
926            
927 0           for my $element_id ( 0 .. $#{ $self->hash_output->{vm} } )
  0            
928             {
929             # check the state first, if up, proceed
930 0           $self->log->debug("requesting vm state for element $element_id");
931            
932 0 0 0       if (my $state = $self->get_vm_by_element_id($element_id, 'state') || $self->not_available)
933             {
934 0 0         if ($state ne 'up')
935             {
936 0           $self->log->debug("not interested, vm state is = " . $state);
937            
938             # not interested if vm is not up, go to next loop
939 0           next;
940             }
941             }
942            
943             # in case there's no any element left, the last element become the only attribute requested
944 0 0         if (@attrs)
945             {
946 0           for my $attr (@attrs)
947             {
948 0           $self->log->debug("requesting attribute $attr for element $element_id");
949              
950 0   0       my $attr_output = $self->get_vm_by_element_id($element_id, $attr) || $self->not_available;
951 0           $output .= $attr_output . ',';
952 0           $self->log->debug("output for attribute $attr element $element_id = " . $attr_output);
953             }
954             }
955            
956             #handle last element
957 0           $self->log->debug("requesting attribute $last_element for element $element_id");
958            
959 0 0 0       if (my $last_output = $self->get_vm_by_element_id($element_id, $last_element) || $self->not_available)
960             {
961 0           $output .= $last_output . "\n";
962 0           $self->log->debug("output for attribute $last_element element $element_id = " . $last_output);
963             }
964            
965             #$output .= "\n";
966             }
967            
968 0           return $output;
969             }
970              
971             =head2 reboot
972              
973             reboot vm
974             required arguments ($vmid)
975             if $self->id is set during initialization, argument is not required
976             =cut
977              
978             sub reboot
979             {
980 0     0     my $self = shift;
981            
982 0   0       my $vmid = shift || undef;
983 0           my $reboot_url;
984            
985             # set the reboot final url
986 0 0         if ($self->id)
987             {
988 0           $reboot_url = $self->url . "/reboot";
989             }
990             else
991             {
992 0 0         if ($vmid)
993             {
994 0           my $is_valid = $self->is_vmid_valid($vmid);
995 0 0         croak "vm id not found" unless $is_valid;
996            
997 0           $reboot_url = $self->url . "/$vmid/reboot";
998             }
999             else
1000             {
1001 0           croak "vm id is required";
1002             }
1003             }
1004            
1005 0           $self->log->debug("reboot action url = $reboot_url");
1006            
1007             # set user agent
1008 0           my $ua = LWP::UserAgent->new();
1009 0           my $action;
1010            
1011 0 0         if ($self->vm_boot_dev eq 'hd')
1012             {
1013 0           $action = $ua->post($reboot_url, Content_Type => 'application/xml', Content => $self->vm_hd_xml);
1014             }
1015             else
1016             {
1017 0           $action = $ua->post($reboot_url, Content_Type => 'application/xml', Content => $self->vm_cdrom_xml);
1018             }
1019            
1020 0           my $parser = XML::LibXML->new();
1021 0           my $output = $parser->parse_string($action->decoded_content);
1022            
1023 0           $self->vm_action_output($output);
1024             }
1025              
1026             =head2 suspend
1027              
1028             suspend vm
1029             required arguments ($vmid)
1030             if $self->id is set during initialization, argument is not required
1031             =cut
1032              
1033             sub suspend
1034             {
1035 0     0     my $self = shift;
1036            
1037 0   0       my $vmid = shift || undef;
1038 0           my $suspend_url;
1039            
1040             # set the suspend final url
1041 0 0         if ($self->id)
1042             {
1043 0           $suspend_url = $self->url . "/suspend";
1044             }
1045             else {
1046 0 0         if ($vmid)
1047             {
1048 0           my $is_valid = $self->is_vmid_valid($vmid);
1049 0 0         croak "vm id not found" unless $is_valid;
1050            
1051 0           $suspend_url = $self->url . "/$vmid/suspend";
1052             }
1053             else
1054             {
1055 0           croak "vm id is required";
1056             }
1057             }
1058            
1059 0           $self->log->debug("suspend action url = $suspend_url");
1060            
1061             # set user agent
1062 0           my $ua = LWP::UserAgent->new();
1063 0           my $action;
1064            
1065 0 0         if ($self->vm_boot_dev eq 'hd') {
1066 0           $action = $ua->post($suspend_url, Content_Type => 'application/xml', Content => $self->vm_hd_xml);
1067             }
1068             else {
1069 0           $action = $ua->post($suspend_url, Content_Type => 'application/xml', Content => $self->vm_cdrom_xml);
1070             }
1071            
1072 0           my $parser = XML::LibXML->new();
1073 0           my $output = $parser->parse_string($action->decoded_content);
1074            
1075 0           $self->vm_action_output($output);
1076             }
1077              
1078             =head2 migrate
1079              
1080             migrate vm
1081             required arguments ($vmid)
1082             if $self->id is set during initialization, argument is not required
1083             =cut
1084              
1085             sub migrate
1086             {
1087 0     0     my $self = shift;
1088            
1089 0   0       my $vmid = shift || undef;
1090 0           my $migrate_url;
1091            
1092             # set the migrate final url
1093 0 0         if ($self->id)
1094             {
1095 0           $migrate_url = $self->url . "/migrate";
1096             }
1097             else
1098             {
1099 0 0         if ($vmid)
1100             {
1101 0           my $is_valid = $self->is_vmid_valid($vmid);
1102 0 0         croak "vm id not found" unless $is_valid;
1103            
1104 0           $migrate_url = $self->url . "/$vmid/migrate";
1105             }
1106             else
1107             {
1108 0           croak "vm id is required";
1109             }
1110             }
1111            
1112 0           $self->log->debug("migrate action url = $migrate_url");
1113            
1114             # set user agent
1115 0           my $ua = LWP::UserAgent->new();
1116 0           my $action;
1117            
1118 0 0         if ($self->vm_boot_dev eq 'hd')
1119             {
1120 0           $action = $ua->post($migrate_url, Content_Type => 'application/xml', Content => $self->vm_hd_xml);
1121             }
1122             else
1123             {
1124 0           $action = $ua->post($migrate_url, Content_Type => 'application/xml', Content => $self->vm_cdrom_xml);
1125             }
1126            
1127 0           my $parser = XML::LibXML->new();
1128 0           my $output = $parser->parse_string($action->decoded_content);
1129            
1130 0           $self->vm_action_output($output);
1131             }
1132              
1133             =head2 is_vmid_valid
1134              
1135             return false if vmid not valid
1136             =cut
1137              
1138             sub is_vmid_valid
1139             {
1140 0     0     my $self = shift;
1141 0           my $vmid = shift;
1142            
1143 0 0         croak "vm id required" unless $vmid;
1144            
1145 0           $vmid = $self->trim($vmid);
1146 0           $self->log->debug("vm id = $vmid");
1147            
1148             # if vm id match, return 1
1149 0           for my $element_id (0 .. $#{ $self->hash_output->{vm} })
  0            
1150             {
1151 0 0         if ($self->hash_output->{vm}[$element_id]->{id} eq $vmid)
1152             {
1153 0           $self->log->debug("$vmid is valid");
1154 0           return 1;
1155             }
1156             }
1157            
1158 0           $self->log->debug("$vmid is not valid");
1159 0           return 0;
1160             }
1161              
1162             =head2 get_statistics
1163              
1164             required argument ($vmid) or set 'id' during initialization
1165             return vm statistics
1166             $vm->get_statistics($vmid);
1167              
1168             =cut
1169              
1170             sub get_statistics
1171             {
1172 0     0     my $self = shift;
1173 0   0       my $vmid = $self->id || shift || die "vm id required\n";
1174            
1175 0           my $url = $self->base_url . $self->vm_url . '/' . $vmid . '/statistics';
1176 0           my $ua = LWP::UserAgent->new();
1177            
1178 0           $self->log->debug($url);
1179 0           $self->get_api_output($url);
1180             }
1181              
1182             =head2 list
1183            
1184             return vm's attributes text output from hash_output attribute
1185             if no argument spesified, it will return all vm attributes (based on vm_output_attrs)
1186             argument supported is 'vm id'
1187             example :
1188             $vm->list('b4738b0f-b73d-4a66-baa8-2ba465d63132');
1189            
1190             =cut
1191              
1192             sub list
1193             {
1194 0     0     my $self = shift;
1195            
1196 0   0       my $vmid = shift || undef;
1197            
1198             # store the output and return it at the end
1199 0           my $output;
1200            
1201             # store each attribute to array to be looped
1202 0           my @attrs = split ',' => $self->vm_output_attrs;
1203            
1204             # store the last element to escape the vm_output_delimeter
1205 0           my $last_element = pop (@attrs);
1206 0           $self->log->debug("last element = $last_element");
1207              
1208             # if the id is defined during initialization
1209             # the rest api output will only contain attributes for this id
1210             # so it's not necessary to loop on vm element
1211 0 0         if ($self->id)
    0          
1212             {
1213 0           for my $attr (@attrs)
1214             {
1215 0           $self->log->debug("requesting attribute $attr");
1216            
1217 0   0       my $attr_output = $self->get_vm_by_self_id($attr) || $self->not_available;
1218 0           $output .= $attr_output . $self->vm_output_delimiter;
1219 0           $self->log->debug("output for attribute $attr = " . $attr_output);
1220             }
1221            
1222             #handle last element or the only element
1223 0           $self->log->debug("requesting attribute $last_element");
1224            
1225 0 0 0       if (my $last_output = $self->get_vm_by_self_id($last_element) || $self->not_available)
1226             {
1227 0           $output .= $last_output;
1228 0           $self->log->debug("output for attribute $last_element = " . $last_output);
1229             }
1230            
1231 0           $output .= "\n";
1232             }
1233             elsif ($vmid)
1234             {
1235             #store vmid element
1236 0           my $vmid_element;
1237            
1238 0           $vmid = $self->trim($vmid);
1239            
1240             # store hash to avoid keys on reference
1241             #my %hash = $self->hash_output->{vm};
1242            
1243 0           for my $element_id ( 0 .. $#{ $self->hash_output->{vm} } )
  0            
1244             {
1245             next unless
1246 0 0         $self->hash_output->{vm}[$element_id]->{id} eq $vmid;
1247            
1248 0           $vmid_element = $element_id;
1249             }
1250            
1251 0 0         croak "vm id not found" unless $vmid_element >= 0;
1252            
1253 0           for my $attr (@attrs)
1254             {
1255 0           $self->log->debug("requesting attribute $attr for element $vmid_element");
1256            
1257 0   0       my $attr_output = $self->get_vm_by_element_id($vmid_element, $attr) || $self->not_available;
1258 0           $output .= $attr_output . $self->vm_output_delimiter;
1259 0           $self->log->debug("output for attribute $attr element $vmid_element = " . $attr_output);
1260             }
1261            
1262             #handle last element or the only element
1263 0           $self->log->debug("requesting attribute $last_element for element $vmid_element");
1264            
1265 0 0 0       if (my $last_output = $self->get_vm_by_element_id($vmid_element, $last_element) || $self->not_available)
1266             {
1267 0           $output .= $last_output;
1268 0           $self->log->debug("output for attribute $last_element element $vmid_element = " . $last_output);
1269             }
1270            
1271 0           $output .= "\n";
1272             }
1273             else
1274             {
1275 0           for my $element_id ( 0 .. $#{ $self->hash_output->{vm} } )
  0            
1276             {
1277             # in case there's no any element left, the last element become the only attribute requested
1278 0 0         if (@attrs)
1279             {
1280 0           for my $attr (@attrs)
1281             {
1282 0           $self->log->debug("requesting attribute $attr for element $element_id");
1283            
1284 0   0       my $attr_output = $self->get_vm_by_element_id($element_id, $attr) || $self->not_available;
1285 0           $output .= $attr_output . $self->vm_output_delimiter;
1286 0           $self->log->debug("output for attribute $attr element $element_id = " . $attr_output);
1287             }
1288             }
1289            
1290             #handle last element or the only element
1291 0           $self->log->debug("requesting attribute $last_element for element $element_id");
1292            
1293 0 0 0       if (my $last_output = $self->get_vm_by_element_id($element_id, $last_element) || $self->not_available)
1294             {
1295 0           $output .= $last_output;
1296 0           $self->log->debug("output for attribute $last_element element $element_id = " . $last_output);
1297             }
1298            
1299 0           $output .= "\n";
1300             }
1301             }
1302            
1303 0           return $output;
1304             }
1305              
1306             =head2 get_vm_by_element_id
1307            
1308             This method is used by list method to list all vm attribute requested
1309             An array element id and attribute name is required
1310             =cut
1311              
1312             sub get_vm_by_element_id
1313             {
1314 0     0     my $self = shift;
1315            
1316 0           my ($element_id, $attr) = @_;
1317            
1318 0 0         croak "hash output is not defined"
1319             unless $self->hash_output;
1320            
1321 0           $attr = $self->trim($attr);
1322 0           $self->log->debug("element id = $element_id, attribute = $attr");
1323            
1324 0 0         if ($attr eq 'id')
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1325             {
1326 0           return $self->hash_output->{vm}[$element_id]->{id};
1327             }
1328             elsif ($attr eq 'name')
1329             {
1330 0           return $self->hash_output->{vm}[$element_id]->{name};
1331             }
1332             elsif ($attr eq 'memory')
1333             {
1334 0           return $self->hash_output->{vm}[$element_id]->{memory};
1335             }
1336             elsif ($attr eq 'state')
1337             {
1338 0           return $self->hash_output->{vm}[$element_id]->{status}->{state};
1339             }
1340             elsif ($attr eq 'description')
1341             {
1342 0           return $self->hash_output->{vm}[$element_id]->{description};
1343             }
1344             elsif ($attr eq 'cpu_cores')
1345             {
1346 0           return $self->hash_output->{vm}[$element_id]->{cpu}->{topology}->{cores};
1347             }
1348             elsif ($attr eq 'cpu_sockets')
1349             {
1350 0           return $self->hash_output->{vm}[$element_id]->{cpu}->{topology}->{sockets};
1351             }
1352             elsif ($attr eq 'cpu_arch')
1353             {
1354 0           return $self->hash_output->{vm}[$element_id]->{cpu}->{architecture};
1355             }
1356             elsif ($attr eq 'cpu_shares')
1357             {
1358 0           return $self->hash_output->{vm}[$element_id]->{cpu_shares};
1359             }
1360             elsif ($attr eq 'os_type')
1361             {
1362 0           return $self->hash_output->{vm}[$element_id]->{os}->{type};
1363             }
1364             elsif ($attr eq 'boot_dev')
1365             {
1366 0           return $self->hash_output->{vm}[$element_id]->{os}->{boot}->{dev};
1367             }
1368             elsif ($attr eq 'ha_enabled')
1369             {
1370 0           return $self->hash_output->{vm}[$element_id]->{high_availability}->{enabled};
1371             }
1372             elsif ($attr eq 'ha_priority')
1373             {
1374 0           return $self->hash_output->{vm}[$element_id]->{high_availability}->{priority};
1375             }
1376             elsif ($attr eq 'display_type')
1377             {
1378 0           return $self->hash_output->{vm}[$element_id]->{display}->{type};
1379             }
1380             elsif ($attr eq 'display_address')
1381             {
1382 0           return $self->hash_output->{vm}[$element_id]->{display}->{address};
1383             }
1384             elsif ($attr eq 'display_port')
1385             {
1386             # spice will return secure_port
1387             return $self->hash_output->{vm}[$element_id]->{display}->{secure_port}
1388 0 0         if $self->hash_output->{vm}[$element_id]->{display}->{secure_port};
1389            
1390             # vnc will return port
1391             return $self->hash_output->{vm}[$element_id]->{display}->{port}
1392 0           }
1393             elsif ($attr eq 'display_host_subject')
1394             {
1395 0           return $self->hash_output->{vm}[$element_id]->{display}->{certificate}->{subject};
1396             }
1397             elsif ($attr eq 'cluster_id')
1398             {
1399 0           return $self->hash_output->{vm}[$element_id]->{cluster}->{id};
1400             }
1401             elsif ($attr eq 'template_id')
1402             {
1403 0           return $self->hash_output->{vm}[$element_id]->{template}->{id};
1404             }
1405             elsif ($attr eq 'stop_time')
1406             {
1407 0           return $self->hash_output->{vm}[$element_id]->{stop_time};
1408             }
1409             elsif ($attr eq 'creation_time')
1410             {
1411 0           return $self->hash_output->{vm}[$element_id]->{creation_time};
1412             }
1413             elsif ($attr eq 'timezone')
1414             {
1415 0           return $self->hash_output->{vm}[$element_id]->{timezone};
1416             }
1417             elsif ($attr eq 'usb_enabled')
1418             {
1419 0           return $self->hash_output->{vm}[$element_id]->{usb}->{enabled};
1420             }
1421             elsif ($attr eq 'host_id')
1422             {
1423 0           return $self->hash_output->{vm}[$element_id]->{host}->{id};
1424             }
1425             }
1426              
1427             =head2 get_vm_by_self_id
1428            
1429             This method is used by list method if $self->id is defined
1430             The id is set during initialization (id => 'vmid')
1431             attribute name is required
1432             =cut
1433              
1434             sub get_vm_by_self_id
1435             {
1436 0     0     my $self = shift;
1437            
1438 0           my $attr = shift;
1439            
1440 0 0         croak "hash output is not defined"
1441             unless $self->hash_output;
1442            
1443 0           $attr = $self->trim($attr);
1444 0           $self->log->debug("attribute = $attr");
1445            
1446 0 0         if ($attr eq 'id')
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1447             {
1448 0           return $self->hash_output->{id};
1449             }
1450             elsif ($attr eq 'name')
1451             {
1452 0           return $self->hash_output->{name};
1453             }
1454             elsif ($attr eq 'memory')
1455             {
1456 0           return $self->hash_output->{memory};
1457             }
1458             elsif ($attr eq 'state')
1459             {
1460 0           return $self->hash_output->{status}->{state};
1461             }
1462             elsif ($attr eq 'description')
1463             {
1464 0           return $self->hash_output->{description};
1465             }
1466             elsif ($attr eq 'cpu_cores')
1467             {
1468 0           return $self->hash_output->{cpu}->{topology}->{cores};
1469             }
1470             elsif ($attr eq 'cpu_sockets')
1471             {
1472 0           return $self->hash_output->{cpu}->{topology}->{sockets};
1473             }
1474             elsif ($attr eq 'cpu_arch')
1475             {
1476 0           return $self->hash_output->{cpu}->{architecture};
1477             }
1478             elsif ($attr eq 'cpu_shares')
1479             {
1480 0           return $self->hash_output->{cpu_shares};
1481             }
1482             elsif ($attr eq 'os_type')
1483             {
1484 0           return $self->hash_output->{os}->{type};
1485             }
1486             elsif ($attr eq 'boot_dev')
1487             {
1488 0           return $self->hash_output->{os}->{boot}->{dev};
1489             }
1490             elsif ($attr eq 'ha_enabled')
1491             {
1492 0           return $self->hash_output->{high_availability}->{enabled};
1493             }
1494             elsif ($attr eq 'ha_priority')
1495             {
1496 0           return $self->hash_output->{high_availability}->{priority};
1497             }
1498             elsif ($attr eq 'display_type')
1499             {
1500 0           return $self->hash_output->{display}->{type};
1501             }
1502             elsif ($attr eq 'display_address')
1503             {
1504 0           return $self->hash_output->{display}->{address};
1505             }
1506             elsif ($attr eq 'display_port')
1507             {
1508             # spice will return secure_port
1509             return $self->hash_output->{display}->{secure_port}
1510 0 0         if $self->hash_output->{display}->{secure_port};
1511            
1512             # vnc will return port
1513 0           return $self->hash_output->{display}->{port};
1514             }
1515             elsif ($attr eq 'display_host_subject')
1516             {
1517 0           return $self->hash_output->{display}->{certificate}->{subject};
1518             }
1519             elsif ($attr eq 'cluster_id')
1520             {
1521 0           return $self->hash_output->{cluster}->{id};
1522             }
1523             elsif ($attr eq 'template_id')
1524             {
1525 0           return $self->hash_output->{template}->{id};
1526             }
1527             elsif ($attr eq 'stop_time')
1528             {
1529 0           return $self->hash_output->{stop_time};
1530             }
1531             elsif ($attr eq 'creation_time')
1532             {
1533 0           return $self->hash_output->{creation_time};
1534             }
1535             elsif ($attr eq 'timezone')
1536             {
1537 0           return $self->hash_output->{timezone};
1538             }
1539             elsif ($attr eq 'usb_enabled')
1540             {
1541 0           return $self->hash_output->{usb}->{enabled};
1542             }
1543             elsif ($attr eq 'host_id')
1544             {
1545 0           return $self->hash_output->{host}->{id};
1546             }
1547             }
1548              
1549             =head1 AUTHOR
1550              
1551             "Heince Kurniawan", C<< <"heince at cpan.org"> >>
1552              
1553             =head1 BUGS
1554              
1555             Please report any bugs or feature requests to C, or through
1556             the web interface at L. I will be notified, and then you'll
1557             automatically be notified of progress on your bug as I make changes.
1558              
1559             =head1 SUPPORT
1560              
1561             You can find documentation for this module with the perldoc command.
1562              
1563             perldoc Ovirt::VM
1564              
1565             =head1 ACKNOWLEDGEMENTS
1566              
1567              
1568             =head1 LICENSE AND COPYRIGHT
1569              
1570             Copyright 2015 "Heince Kurniawan".
1571              
1572             This program is free software; you can redistribute it and/or modify it
1573             under the terms of the the Artistic License (2.0). You may obtain a
1574             copy of the full license at:
1575              
1576             L
1577              
1578             Any use, modification, and distribution of the Standard or Modified
1579             Versions is governed by this Artistic License. By using, modifying or
1580             distributing the Package, you accept this license. Do not use, modify,
1581             or distribute the Package, if you do not accept this license.
1582              
1583             If your Modified Version has been derived from a Modified Version made
1584             by someone other than you, you are nevertheless required to ensure that
1585             your Modified Version complies with the requirements of this license.
1586              
1587             This license does not grant you the right to use any trademark, service
1588             mark, tradename, or logo of the Copyright Holder.
1589              
1590             This license includes the non-exclusive, worldwide, free-of-charge
1591             patent license to make, have made, use, offer to sell, sell, import and
1592             otherwise transfer the Package with respect to any patent claims
1593             licensable by the Copyright Holder that are necessarily infringed by the
1594             Package. If you institute patent litigation (including a cross-claim or
1595             counterclaim) against any party alleging that the Package constitutes
1596             direct or contributory patent infringement, then this Artistic License
1597             to you shall terminate on the date that such litigation is filed.
1598              
1599             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1600             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1601             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1602             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1603             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1604             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1605             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1606             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1607              
1608              
1609             =cut
1610              
1611             1;