File Coverage

blib/lib/Linux/LVM2/Snapshot.pm
Criterion Covered Total %
statement 11 13 84.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 16 18 88.8


line stmt bran cond sub pod time code
1             package Linux::LVM2::Snapshot;
2             {
3             $Linux::LVM2::Snapshot::VERSION = '0.14';
4             }
5             BEGIN {
6 1     1   1559 $Linux::LVM2::Snapshot::AUTHORITY = 'cpan:TEX';
7             }
8             # ABSTRACT: a class representing a LV snapshot in an Linux LVM2
9              
10 1     1   23 use 5.010_000;
  1         3  
  1         40  
11 1     1   5 use mro 'c3';
  1         2  
  1         16  
12 1     1   29 use feature ':5.10';
  1         2  
  1         87  
13              
14 1     1   435 use Moose;
  0            
  0            
15             use namespace::autoclean;
16              
17             # use IO::Handle;
18             # use autodie;
19             # use MooseX::Params::Validate;
20              
21             use Carp;
22             use File::Temp;
23              
24             use Sys::FS;
25             use Sys::Run;
26              
27             has 'name' => (
28             'is' => 'ro',
29             'isa' => 'Str',
30             'lazy' => 1,
31             'builder' => '_init_name',
32             );
33              
34             has 'logger' => (
35             'is' => 'rw',
36             'isa' => 'Log::Tree',
37             'required' => 1,
38             );
39              
40             has 'lv' => (
41             'is' => 'ro',
42             'isa' => 'Linux::LVM2::LV',
43             );
44              
45             has 'source' => (
46             'is' => 'ro',
47             'isa' => 'Linux::LVM2::LV',
48             'required' => 1,
49             );
50              
51             has 'parent' => (
52             'is' => 'ro',
53             'isa' => 'Linux::LVM2',
54             'required' => 1,
55             );
56              
57             has 'clear_caches' => (
58             'is' => 'ro',
59             'isa' => 'Bool',
60             'default' => 0,
61             );
62              
63             has 'snapspace' => (
64             'is' => 'rw',
65             'isa' => 'Int',
66             'default' => '5', # GB
67             );
68              
69             has 'mount_point' => (
70             'is' => 'ro',
71             'isa' => 'Str',
72             );
73              
74             has 'verbose' => (
75             'is' => 'rw',
76             'isa' => 'Int', # Bool
77             'default' => 0,
78             );
79              
80             has 'sys' => (
81             'is' => 'rw',
82             'isa' => 'Sys::Run',
83             'lazy' => 1,
84             'builder' => '_init_sys',
85             );
86              
87             has 'fs' => (
88             'is' => 'rw',
89             'isa' => 'Sys::FS',
90             'lazy' => 1,
91             'builder' => '_init_fs',
92             );
93              
94             has '_created_mount_point' => (
95             'is' => 'rw',
96             'isa' => 'Bool',
97             'default' => 0,
98             );
99              
100             sub _init_sys {
101             my $self = shift;
102              
103             my $Sys = Sys::Run::->new( { 'logger' => $self->logger(), } );
104              
105             return $Sys;
106             }
107              
108             sub _init_fs {
109             my $self = shift;
110              
111             my $FS = Sys::FS::->new(
112             {
113             'logger' => $self->logger(),
114             'sys' => $self->sys(),
115             }
116             );
117              
118             return $FS;
119             }
120              
121             sub full_path {
122             my $self = shift;
123              
124             return $self->lv()->full_path();
125             }
126              
127             sub mapper_path {
128             my $self = shift;
129              
130             return $self->lv()->mapper_path();
131             }
132              
133             sub BUILD {
134             my $self = shift;
135              
136             # clear caches, free pagecache, dentries and inodes
137             $self->sys()->clear_caches() if $self->clear_caches();
138              
139             # sync; sync; sync; lvcreate ...
140             my $cmd =
141             'sync; lvcreate -L'
142             . $self->snapspace()
143             . 'G --snapshot --name '
144             . $self->name()
145             . ' /dev/'
146             . $self->source()->vg()->name() . '/'
147             . $self->source()->name();
148             if ( !$self->sys()->run_cmd( $cmd, { RaiseError => 1, Verbose => 1, } ) ) {
149             my $msg = 'lvcreate failed. Could not create snapshot!';
150             $self->logger()->log( message => $msg, level => 'error' );
151             return;
152             }
153              
154             # set our lv object
155             $self->source()->vg()->update();
156             if ( $self->source()->vg()->lvs()->{ $self->name() } && $self->source()->vg()->lvs()->{ $self->name() }->isa('Linux::LVM2::LV') ) {
157             $self->{'lv'} = $self->source()->vg()->lvs()->{ $self->name() };
158             $self->lv()->fs_type( $self->source()->fs_type() );
159             $self->lv()->fs_options( $self->source()->fs_options() );
160             return 1;
161             }
162             else {
163             my $msg = 'LV ' . $self->name() . ' not found!';
164             $self->logger()->log( message => $msg, level => 'error' );
165             return;
166             }
167             }
168              
169             sub _init_name {
170             my $self = shift;
171              
172             # finds a free replisnapname
173             my $basename = 'replisnap';
174             my $try = 0;
175             while ( $self->parent()->is_lv( $self->source()->vg()->name(), $basename . $try ) ) {
176             $try++;
177              
178             # safety guard
179             if ( $try > 1024 ) {
180             my $msg = 'Could not find a free replisnap name within $try tries! Giving up.';
181             $self->logger()->log( message => $msg, level => 'error' );
182             return;
183             }
184             }
185              
186             # found an unused name for the
187             # snapshot
188             return $basename . $try;
189             }
190              
191             sub mount {
192             my $self = shift;
193             my $mount_point = shift;
194              
195             if ( !$mount_point || !-d $mount_point ) {
196             $mount_point = File::Temp::tempdir( CLEANUP => 0 );
197             $self->_created_mount_point(1);
198             }
199              
200             if ( $self->fs()->mount( $self->full_path(), $mount_point, $self->lv()->fs_type(), 'ro,noatime', { Verbose => $self->verbose(), } ) ) {
201             $self->{'mount_point'} = $mount_point;
202             return $mount_point;
203             }
204             else {
205             my $msg = 'Could not mount ' . $self->full_path() . ' at '.$mount_point;
206             $self->logger()->log( message => $msg, level => 'error' );
207             return;
208             }
209             }
210              
211             sub umount {
212             my $self = shift;
213              
214             my $mounted_dev = $self->mapper_path();
215             if ( !$self->fs()->is_mounted($mounted_dev) ) {
216             $mounted_dev = $self->full_path();
217             if ( !$self->fs()->is_mounted($mounted_dev) ) {
218             my $msg = 'Tried to unmount snapshot (' . $self->full_path() . ') which does not appear to be mounted.';
219             $self->logger()->log( message => $msg, level => 'warning' );
220             }
221             }
222             else {
223             my $msg = 'Trying to unmount device ' . $mounted_dev;
224             $self->logger()->log( message => $msg, level => 'debug' );
225             if ( $self->fs()->umount( $mounted_dev, ) ) {
226             if($self->_created_mount_point()) {
227             $self->sys()->run_cmd( 'rm -rf ' . $self->mount_point() );
228             }
229             $msg = 'Unmounted snapshot ' . $mounted_dev . ' from ' . $self->mount_point();
230             $self->logger()->log( message => $msg, level => 'debug' );
231             return 1;
232             }
233             else {
234             $msg = 'Could not unmount ' . $mounted_dev;
235             $self->logger()->log( message => $msg, level => 'error' );
236             }
237             }
238              
239             return;
240             }
241              
242             sub remove {
243             my $self = shift;
244              
245             $self->umount()
246             or return;
247              
248             # remove it
249             my $cmd = '/sbin/lvremove -f ' . $self->full_path();
250             if ( $self->sys()->run_cmd( $cmd, { Verbose => $self->verbose(), } ) ) {
251             my $msg = 'Removed snapshot LV ' . $self->full_path();
252             $self->logger()->log( message => $msg, level => 'debug' );
253             return 1;
254             }
255             else {
256             my $msg = 'Failed to remove snapshot LV ' . $self->full_path();
257             $self->logger()->log( message => $msg, level => 'debug' );
258             return;
259             }
260             }
261              
262             sub DEMOLISH {
263             my $self = shift;
264              
265             return $self->remove();
266             }
267              
268             sub valid {
269             my $self = shift;
270             return $self->lv()->valid();
271             }
272              
273             no Moose;
274             __PACKAGE__->meta->make_immutable;
275              
276             1;
277              
278             =pod
279              
280             =encoding utf-8
281              
282             =head1 NAME
283              
284             Linux::LVM2::Snapshot - a class representing a LV snapshot in an Linux LVM2
285              
286             =head1 SYNOPSIS
287              
288             use Linux::LVM2::Snapshot;
289             my $Mod = Linux::LVM2::Snapshot::->new();
290              
291             =head1 DESCRIPTION
292              
293             This class models a snapshoted LV from an Linux LVM2 LV.
294              
295             =head1 ATTRIBUTES
296              
297             =head2 name
298              
299             The name of this snapshot LV
300              
301             =head2 logger
302              
303             An instance of Log::Tree
304              
305             =head2 lv
306              
307             The snapshot LV
308              
309             =head2 source
310              
311             The snapshoted LV
312              
313             =head2 parent
314              
315             Our parent, must be an instance of Linux::LVM2
316              
317             =head2 clear_caches
318              
319             UNDOCUMENTED
320              
321             =head2 snapspace
322              
323             Use this much GB for the snapshot
324              
325             =head2 mount_point
326              
327             UNDOCUMENTED
328              
329             =head2 verbose
330              
331             UNDOCUMENTED
332              
333             =head2 sys
334              
335             UNDOCUMENTED
336              
337             =head2 fs
338              
339             UNDOCUMENTED
340              
341             =head1 METHODS
342              
343             =head2 BUILD
344              
345             Invoked by Moose on instantiation. Create the snapshot.
346              
347             =head2 DEMOLISH
348              
349             Invoked by Moose on destruction. Removes the snapshot.
350              
351             =head2 full_path
352              
353             Return the full path to this LV.
354              
355             =head2 mapper_path
356              
357             Return the dev-mapper path to this LV.
358              
359             =head2 mount
360              
361             Try to mount this LV snapshot to the given mount point.
362              
363             =head2 remove
364              
365             Try to unmount this LV, if mounted, and remove the LV afterwards.
366              
367             =head2 umount
368              
369             Try to unmount this LV.
370              
371             =head2 valid
372              
373             Returns true unless the snapshot is 100% full.
374              
375             =head1 NAME
376              
377             Linux::LVM2::Snapshot - Model a Snapshot LV.
378              
379             =head1 AUTHOR
380              
381             Dominik Schulz <dominik.schulz@gauner.org>
382              
383             =head1 COPYRIGHT AND LICENSE
384              
385             This software is copyright (c) 2012 by Dominik Schulz.
386              
387             This is free software; you can redistribute it and/or modify it under
388             the same terms as the Perl 5 programming language system itself.
389              
390             =cut
391              
392             __END__
393              
394              
395             1; # End of Linux::LVM2::Snapshot