File Coverage

blib/lib/PITA/Guest/Storage/Simple.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package PITA::Guest::Storage::Simple;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PITA::Guest::Storage::Simple - A (relatively) simple Guest Storage object
8              
9             =head1 DESCRIPTION
10              
11             The L class provides an API for cataloguing and
12             retrieving Guest images, with all the data stored on the filesystem using
13             the native XML file formats.
14              
15             B implements a very simple version of
16             the L API.
17              
18             Guest image location and searching is done the long way, with no indexing.
19              
20             =head1 METHODS
21              
22             =cut
23              
24 1     1   1533 use 5.008;
  1         5  
  1         41  
25 1     1   5 use strict;
  1         1  
  1         30  
26 1     1   6 use Carp ();
  1         2  
  1         14  
27 1     1   6 use File::Spec ();
  1         1  
  1         14  
28 1     1   4 use File::Path ();
  1         2  
  1         13  
29 1     1   4 use Params::Util ();
  1         2  
  1         12  
30 1     1   5 use Data::GUID ();
  1         2  
  1         31  
31 1     1   519 use PITA::XML::Guest ();
  0            
  0            
32             use PITA::Guest::Storage ();
33              
34             our $VERSION = '0.60';
35             our @ISA = 'PITA::Guest::Storage';
36             our $LOCKFILE = 'PITA-Guest-Storage-Simple.lock';
37              
38              
39              
40              
41              
42             #####################################################################
43             # Constructor and Accessors
44              
45             =pod
46              
47             =head2 new
48              
49             my $store = PITA::Guest::Storage::Simple->new(
50             storage_dir => '/var/PITA-Guest-Storable-Simple',
51             );
52              
53             The C method creates a new simple storage object. It takes a single
54             named param
55              
56             Returns a C object, or throws an exception
57             on error.
58              
59             =cut
60              
61             sub new {
62             my $class = shift;
63             my $self = $class->SUPER::new(@_);
64              
65             # Check params
66             unless ( $self->storage_dir and -d $self->storage_dir and -w _ ) {
67             Carp::croak('The storage_dir is not a writable directory');
68             }
69              
70             $self;
71             }
72              
73             =pod
74              
75             =head2 storage_dir
76              
77             The C accessor returns the location of the directory that
78             serves as the root of the data store.
79              
80             =cut
81              
82             sub storage_dir {
83             $_[0]->{storage_dir};
84             }
85              
86              
87              
88              
89              
90             #####################################################################
91             # PITA::Guest::Storage::Simple Methods
92              
93             =pod
94              
95             =head2 create
96              
97             my $store = PITA::Guest::Storage::Simple->new(
98             storage_dir => '/var/PITA-Guest-Storable-Simple',
99             );
100              
101             The C constructor creates a new C
102             repository.
103              
104             =cut
105              
106             sub create {
107             my $class = shift;
108             my $self = bless { @_ }, $class;
109              
110             # The storage_dir should not exist, we will create it
111             my $storage_dir = $self->storage_dir;
112             unless ( $storage_dir ) {
113             Carp::croak("The storage_dir param was not provided");
114             }
115             if ( -d $storage_dir ) {
116             Carp::croak("The storage_dir '$storage_dir' already exists");
117             }
118             eval { File::Path::mkpath( $storage_dir, 1, 0711 ) };
119             if ( $@ ) {
120             Carp::croak("Failed to create the storage_dir '$storage_dir': $@");
121             }
122              
123             $self;
124             }
125              
126             =pod
127              
128             =head2 storage_lock
129              
130             The C method takes a lock on the C file,
131             creating it if needed (in the C method case).
132              
133             It does not wait to take the lock, failing immediately if the lock
134             cannot be taken.
135              
136             Returns true if the lock is taken, false if the lock cannot be taken,
137             or throws an exception on error.
138              
139             =cut
140              
141             sub storage_lock {
142             return 1 if $^O eq 'MSWin32';
143              
144             # Only lock on Unix
145             require File::Flock;
146             File::Flock->new(
147             File::Spec->catfile( $_[0]->storage_dir, $LOCKFILE ),
148             );
149             }
150              
151              
152              
153              
154              
155             #####################################################################
156             # PITA::Guest::Storage Methods
157              
158             sub add_guest {
159             my $self = shift;
160             my $xml = Params::Util::_INSTANCE(shift, 'PITA::XML::Guest')
161             or Carp::croak('Did not provide a PITA::XML::Guest to add_guest');
162              
163             # Is the driver available for this guest
164             unless ( $xml->driver_available ) {
165             Carp::croak("The guest driver " . $xml->driver . " is not available");
166             }
167              
168             # Does the guest have a guid...
169             $xml->set_id( Data::GUID->new->as_string ) unless $xml->id;
170              
171             # Does the GUID match an existing one
172             if ( $self->guest( $xml->id ) ) {
173             Carp::croak("The guest " . $xml->id . " already exists");
174             }
175              
176             # Load a full PITA::Guest object from the file
177             my $guest = PITA::Guest->new( $xml )
178             or die "Failed to load PITA::Guest";
179              
180             # Can we ping the guest
181             unless ( $guest->ping ) {
182             Carp::croak("Ping failed, not a valid guest image");
183             }
184              
185             # Run discovery if needed
186             unless ( $guest->discovered ) {
187             $guest->discover or Carp::croak("Failed to discover platforms in guest");
188             }
189              
190             # Store the guest
191             my $lock = $self->storage_lock;
192             my $file = File::Spec->catfile( $self->storage_dir, $xml->id . '.pita' );
193             $xml->write($file) or Carp::croak("Failed to save guest XML file");
194              
195             return $xml;
196             }
197              
198             # Each guest has a matching directory name
199             sub guest {
200             my $self = shift;
201             my $id = shift;
202              
203             # Find the file
204             my $file = $self->guest_file($id);
205             unless ( -f $file ) {
206             return undef;
207             }
208              
209             # Load the guest metadata object
210             my $guest = PITA::XML::Guest->read($file);
211             unless ( $guest->id ) {
212             Carp::croak("Guest id mismatch for $file");
213             }
214              
215             return $guest;
216             }
217              
218             sub guest_exists {
219             -f shift->guest_file(shift);
220             }
221              
222             sub guest_file {
223             File::Spec->catfile(
224             $_[0]->storage_dir, "$_[1].pita",
225             );
226             }
227              
228             sub guests {
229             my $self = shift;
230              
231             # Find all *.pita files in the storage directory
232             opendir( STORAGE, $self->storage_dir ) or Carp::croak("opendir: $!");
233             my @files = readdir(STORAGE) or Carp::croak("readdir: $!");
234             closedir( STORAGE ) or Carp::croak("closedir: $!");
235              
236             # Load and check the metadata files
237             my @guests = ();
238             foreach my $file ( @files ) {
239             # Filter out unwanted things
240             next if $file =~ /^\./;
241             next unless -f $file;
242             next unless $file =~ /^(.+)\.pita$/;
243              
244             # Load the object
245             my $id = $1;
246             my $path = File::Spec->catfile( $self->storage_dir, $file );
247             my $guest = PITA::XML::Guest->read( $path );
248             unless ( $guest->id eq $id ) {
249             Carp::croak("Guest id mismatch for $path");
250             }
251              
252             push @guests, $guest;
253             }
254              
255             return @guests;
256             }
257              
258             sub platform {
259             my $self = shift;
260             die 'CODE INCOMPLETE';
261             }
262              
263             sub platforms {
264             my $self = shift;
265             die 'CODE INCOMPLETE';
266             }
267              
268             1;
269              
270             =pod
271              
272             =head1 SUPPORT
273              
274             Bugs should be reported via the CPAN bug tracker at
275              
276             L
277              
278             For other issues, contact the author.
279              
280             =head1 AUTHOR
281              
282             Adam Kennedy Eadamk@cpan.orgE
283              
284             =head1 SEE ALSO
285              
286             L, L, L
287              
288             =head1 COPYRIGHT
289              
290             Copyright 2005 - 2011 Adam Kennedy.
291              
292             This program is free software; you can redistribute
293             it and/or modify it under the same terms as Perl itself.
294              
295             The full text of the license can be found in the
296             LICENSE file included with this module.
297              
298             =cut