prlbackup 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/prlbackup.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'mixlib/cli'
2
2
  require 'logger'
3
3
  require 'shellwords'
4
+ require 'date'
4
5
  require 'prlbackup/version'
5
6
  require 'prlbackup/cli'
6
7
  require 'prlbackup/virtual_machine'
8
+ require 'prlbackup/backup'
7
9
 
8
10
  module PrlBackup
9
11
  class << self
@@ -0,0 +1,66 @@
1
+ module PrlBackup
2
+ class Backup
3
+ class << self
4
+
5
+ include PrlBackup
6
+
7
+ def all(uuid)
8
+ backup_list(uuid).split("\n").map { |b| create(b) }.compact.sort
9
+ end
10
+
11
+ def backup_list(uuid)
12
+ run('prlctl', 'backup-list', uuid)
13
+ end
14
+
15
+ def create(line)
16
+ re = /^
17
+ (\{[0-9a-f-]+\}) # VM UUID
18
+ \s+
19
+ (\{[0-9a-f-]+\}[\.\d]*) # Backup UUID
20
+ \s+
21
+ (\S+) # Node
22
+ \s+
23
+ ([\d\/]+\s[\d:]+) # Time
24
+ \s+
25
+ ([fi]) # Type
26
+ \s+
27
+ (\d+) # Size
28
+ $/x
29
+ new(:uuid => $2, :time => $4, :type => $5) if re.match(line)
30
+ end
31
+ end
32
+
33
+ include PrlBackup
34
+ include Comparable
35
+
36
+ attr_reader :properties
37
+
38
+ def initialize(properties)
39
+ @properties = properties
40
+ end
41
+
42
+ def uuid
43
+ properties[:uuid]
44
+ end
45
+
46
+ def time
47
+ DateTime.parse(properties[:time])
48
+ end
49
+
50
+ def full?
51
+ properties[:type] == 'f'
52
+ end
53
+
54
+ def delete
55
+ conditionally_run('prlctl', 'backup-delete', '--tag', uuid)
56
+ end
57
+
58
+ def <=>(other)
59
+ time <=> other.time
60
+ end
61
+
62
+ def to_s
63
+ "Backup: #{time.strftime('%Y-%m-%d %H:%M:%S')}"
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module PrlBackup
2
- VERSION = '1.1.3'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -38,8 +38,7 @@ module PrlBackup
38
38
 
39
39
  # Cleanup (delete) old backups.
40
40
  def cleanup
41
- backups = full_backups
42
- delete_backup(backups.shift) while backups.count > config[:keep_only]
41
+ full_backups.shift.delete while full_backups.count > config[:keep_only]
43
42
  end
44
43
 
45
44
  # Return the virtual machine's name.
@@ -97,14 +96,11 @@ module PrlBackup
97
96
  update_info[/^State:\s+stopped$/]
98
97
  end
99
98
 
100
- # List of full backups for the virtual machine.
99
+ # Cached list of virtual machine's full backups.
100
+ # @return [Array<Backup>] full backups
101
101
  def full_backups
102
- run('prlctl', 'backup-list', uuid).split("\n").map { |l| $1 if l[/^\{[a-f0-9-]+\}\s+(\{[a-f0-9-]+\})[^(\.\d+)]/] }.compact
102
+ @full_backups ||= Backup.all(uuid).select { |b| b.full? }
103
103
  end
104
104
 
105
- # Delete the backup given by backup UUID.
106
- def delete_backup(backup_uuid)
107
- conditionally_run('prlctl', 'backup-delete', '--tag', backup_uuid)
108
- end
109
105
  end
110
106
  end
data/man/prlbackup.1 CHANGED
@@ -1,7 +1,7 @@
1
1
  .\" generated with Ronn/v0.7.3
2
2
  .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
3
  .
4
- .TH "PRLBACKUP" "1" "May 2012" "" "PRLBACKUP V1.1.3"
4
+ .TH "PRLBACKUP" "1" "May 2012" "" "PRLBACKUP V1.2.0"
5
5
  .
6
6
  .SH "NAME"
7
7
  \fBprlbackup\fR \- an awesome backup tool for Parallels Server Virtual Machines
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module PrlBackup
4
+ describe Backup do
5
+ before do
6
+ @uuid = '{deadbeef}'
7
+ Backup.stub(:run).and_return('')
8
+ Backup.stub(:conditionally_run).and_return('')
9
+ end
10
+
11
+ describe '.all' do
12
+ it 'should query the backup list by UUID' do
13
+ Backup.should_receive(:backup_list).with(@uuid).and_return('')
14
+ Backup.all(@uuid)
15
+ end
16
+
17
+ it 'should initialize backups by line' do
18
+ Backup.stub(:backup_list).and_return("line 1\nline 2")
19
+ Backup.should_receive(:create).with('line 1')
20
+ Backup.should_receive(:create).with('line 2')
21
+ Backup.all(@uuid)
22
+ end
23
+
24
+ it 'should only return successfully initialized backups' do
25
+ Backup.stub(:backup_list).and_return("line 1\nline 2\nline 3")
26
+ Backup.stub(:create).and_return(nil, 'backup', nil)
27
+ Backup.all(@uuid).should eql(['backup'])
28
+ end
29
+
30
+ it 'should return the backups sorted' do
31
+ backup1 = Backup.new(:time => '02/27/2012 13:11:31')
32
+ backup2 = Backup.new(:time => '02/27/2012 13:11:32')
33
+ backup3 = Backup.new(:time => '02/27/2012 13:11:33')
34
+ Backup.stub(:create).and_return(backup2, backup3, backup1)
35
+ Backup.stub(:backup_list).and_return("line 1\nline 2\nline 3")
36
+ Backup.all(@uuid).should eql([backup1, backup2, backup3])
37
+ end
38
+ end
39
+
40
+ describe '.backup_list' do
41
+ it 'should query and return the backup list by UUID' do
42
+ Backup.should_receive(:run).with('prlctl', 'backup-list', @uuid).and_return('backup list')
43
+ Backup.backup_list(@uuid).should eql('backup list')
44
+ end
45
+ end
46
+
47
+ describe '.create' do
48
+ it 'should initialize a backup with properties' do
49
+ line = '{deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b} psfm.example.com 02/27/2012 13:11:32 f 10537597943'
50
+ backup = Backup.create(line)
51
+ backup.properties[:uuid].should eql('{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
52
+ backup.properties[:time].should eql('02/27/2012 13:11:32')
53
+ backup.properties[:type].should eql('f')
54
+ end
55
+
56
+ it 'should return nil when the backup line is not parsable' do
57
+ Backup.create('hello, world.').should be_nil
58
+ end
59
+ end
60
+
61
+ describe '#uuid' do
62
+ it 'should return the backup uuid' do
63
+ backup = Backup.new(:uuid => '{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
64
+ backup.uuid.should eql('{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
65
+ end
66
+ end
67
+
68
+ describe '#time' do
69
+ it 'should return the time object' do
70
+ backup = Backup.new(:time => '02/27/2012 13:11:32')
71
+ backup.time.year.should eql(2012)
72
+ backup.time.month.should eql(2)
73
+ backup.time.day.should eql(27)
74
+ backup.time.hour.should eql(13)
75
+ backup.time.min.should eql(11)
76
+ backup.time.sec.should eql(32)
77
+ end
78
+ end
79
+
80
+ describe '#full?' do
81
+ it 'should be true when full' do
82
+ backup = Backup.new(:type => 'f')
83
+ backup.should be_full
84
+ end
85
+
86
+ it 'should be false when incremental' do
87
+ backup = Backup.new(:type => 'i')
88
+ backup.should_not be_full
89
+ end
90
+ end
91
+
92
+ describe '#delete' do
93
+ it 'should delete itself' do
94
+ backup = Backup.new(:uuid => '{some-backup-uuid}')
95
+ backup.should_receive(:conditionally_run).with('prlctl', 'backup-delete', '--tag', '{some-backup-uuid}')
96
+ backup.delete
97
+ end
98
+ end
99
+
100
+ describe '#<=>' do
101
+ it 'should compare backups by time' do
102
+ first_backup = Backup.new(:time => '02/27/2012 13:11:32')
103
+ last_backup = Backup.new(:time => '02/27/2012 13:11:33')
104
+ first_backup.should < last_backup
105
+ end
106
+ end
107
+
108
+ describe '#to_s' do
109
+ it 'should display the backup time' do
110
+ backup = Backup.new(:time => '02/27/2012 13:11:32')
111
+ backup.to_s.should eql('Backup: 2012-02-27 13:11:32')
112
+ end
113
+ end
114
+ end
115
+ end
@@ -112,55 +112,43 @@ module PrlBackup
112
112
 
113
113
  it 'should delete 2 backups when there are 2 more backups than to keep' do
114
114
  @vm.stub(:config).and_return({:keep_only => 0})
115
- @vm.should_receive(:delete_backup).with(@old_backup).once
116
- @vm.should_receive(:delete_backup).with(@new_backup).once
115
+ @old_backup.should_receive(:delete).once
116
+ @new_backup.should_receive(:delete).once
117
117
  @vm.cleanup
118
118
  end
119
119
 
120
120
  it 'should delete the oldest backup when there is 1 more backup than to keep' do
121
121
  @vm.stub(:config).and_return({:keep_only => 1})
122
- @vm.should_receive(:delete_backup).with(@old_backup).once
123
- @vm.should_not_receive(:delete_backup).with(@new_backup)
122
+ @old_backup.should_receive(:delete).once
123
+ @new_backup.should_not_receive(:delete)
124
124
  @vm.cleanup
125
125
  end
126
126
 
127
127
  it 'should not delete any backups when there are as many backups as to keep' do
128
128
  @vm.stub(:config).and_return({:keep_only => 2})
129
- @vm.should_not_receive(:delete_backup)
129
+ @old_backup.should_not_receive(:delete)
130
+ @new_backup.should_not_receive(:delete)
130
131
  @vm.cleanup
131
132
  end
132
133
  end
133
134
 
134
- describe '#delete_backup' do
135
- it 'should delete the virtual machines backup' do
136
- @vm.should_receive(:conditionally_run).with('prlctl', 'backup-delete', '--tag', '{some-backup-uuid}')
137
- @vm.instance_eval { delete_backup('{some-backup-uuid}') }
138
- end
139
- end
140
-
141
135
  describe '#full_backups' do
142
136
  before do
143
- stdout = 'prlctl backup-list {bf364fd4-8f6b-4032-818d-4958f9c0945b}
144
- ID Backup_ID Node Date Type Size
145
- {deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b} psfm.example.com 02/27/2012 13:11:32 f 10537597943
146
- {deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b}.2 psfm.example.com 02/27/2012 15:26:02 i 2951747588
147
- {deadbeef} {5f9dd263-ec56-443e-9917-dab9b40d3027} psfm.example.com 03/13/2012 18:06:00 f 11748325372
148
- {deadbeef} {2aeb4ada-6623-4087-9fc5-f09aeaafd81e} psfm.example.com 03/23/2012 21:25:50 f 47315014888
149
- {deadbeef} {68f7e154-6755-46f6-ad1f-a79c5f488f35} psfm.example.com 03/28/2012 15:09:05 f 23462808438
150
- {deadbeef} {68f7e154-6755-46f6-ad1f-a79c5f488f35}.2 psfm.example.com 04/05/2012 17:21:12 i 12841952117'
151
- @vm.stub(:run).and_return(stdout)
152
- end
153
-
154
- it 'should query the backup list by CLI' do
155
- @vm.should_receive(:run).with('prlctl', 'backup-list', @uuid)
156
- @vm.instance_eval { full_backups }
157
- end
158
-
159
- it 'should return a list of the virtual machines full backup UUIDs' do
160
- @vm.instance_eval { full_backups }.should eql(['{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}',
161
- '{5f9dd263-ec56-443e-9917-dab9b40d3027}',
162
- '{2aeb4ada-6623-4087-9fc5-f09aeaafd81e}',
163
- '{68f7e154-6755-46f6-ad1f-a79c5f488f35}'])
137
+ Backup.stub(:all).and_return([])
138
+ end
139
+
140
+ it 'should query list of backups for given UUID once' do
141
+ Backup.should_receive(:all).with(@vm.uuid).once
142
+ 2.times { @vm.instance_eval { full_backups } }
143
+ end
144
+
145
+ it 'should return only full backups' do
146
+ full_backup = double('full backup')
147
+ full_backup.stub(:full?).and_return(true)
148
+ incremental_backup = double('incremental backup')
149
+ incremental_backup.stub(:full?).and_return(false)
150
+ Backup.stub(:all).and_return([full_backup, incremental_backup])
151
+ @vm.instance_eval { full_backups }.should eql([full_backup])
164
152
  end
165
153
  end
166
154
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prlbackup
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-05-05 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mixlib-cli
16
- requirement: &70149533786540 !ruby/object:Gem::Requirement
16
+ requirement: &70297487607280 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.2.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70149533786540
24
+ version_requirements: *70297487607280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: gem-man
27
- requirement: &70149533785600 !ruby/object:Gem::Requirement
27
+ requirement: &70297487605600 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.3.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70149533785600
35
+ version_requirements: *70297487605600
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: cucumber
38
- requirement: &70149533784760 !ruby/object:Gem::Requirement
38
+ requirement: &70297487603540 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.1.4
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70149533784760
46
+ version_requirements: *70297487603540
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: aruba
49
- requirement: &70149533783860 !ruby/object:Gem::Requirement
49
+ requirement: &70297487584840 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.4.11
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70149533783860
57
+ version_requirements: *70297487584840
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: aruba-doubles
60
- requirement: &70149533779320 !ruby/object:Gem::Requirement
60
+ requirement: &70297487583560 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.2.1
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70149533779320
68
+ version_requirements: *70297487583560
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-cucumber
71
- requirement: &70149533778340 !ruby/object:Gem::Requirement
71
+ requirement: &70297487581460 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.7.5
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70149533778340
79
+ version_requirements: *70297487581460
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: guard-rspec
82
- requirement: &70149533777420 !ruby/object:Gem::Requirement
82
+ requirement: &70297487579380 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 0.5.1
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70149533777420
90
+ version_requirements: *70297487579380
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: ronn
93
- requirement: &70149533776180 !ruby/object:Gem::Requirement
93
+ requirement: &70297487578400 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: 0.7.3
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70149533776180
101
+ version_requirements: *70297487578400
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rake
104
- requirement: &70149533775380 !ruby/object:Gem::Requirement
104
+ requirement: &70297487576720 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70149533775380
112
+ version_requirements: *70297487576720
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rb-fsevent
115
- requirement: &70149533773760 !ruby/object:Gem::Requirement
115
+ requirement: &70297487574660 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,7 +120,7 @@ dependencies:
120
120
  version: 0.9.0
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *70149533773760
123
+ version_requirements: *70297487574660
124
124
  description: an awesome backup tool for Parallels Server Virtual Machines
125
125
  email:
126
126
  - bjoernalbers@googlemail.com
@@ -142,12 +142,14 @@ files:
142
142
  - features/steps/dev_steps.rb
143
143
  - features/support/env.rb
144
144
  - lib/prlbackup.rb
145
+ - lib/prlbackup/backup.rb
145
146
  - lib/prlbackup/cli.rb
146
147
  - lib/prlbackup/version.rb
147
148
  - lib/prlbackup/virtual_machine.rb
148
149
  - man/prlbackup.1
149
150
  - man/prlbackup.1.ronn
150
151
  - prlbackup.gemspec
152
+ - spec/prlbackup/backup_spec.rb
151
153
  - spec/prlbackup/cli_spec.rb
152
154
  - spec/prlbackup/virtual_machine_spec.rb
153
155
  - spec/prlbackup_spec.rb
@@ -166,7 +168,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
166
168
  version: '0'
167
169
  segments:
168
170
  - 0
169
- hash: 1653831612304394743
171
+ hash: -855199531079728294
170
172
  required_rubygems_version: !ruby/object:Gem::Requirement
171
173
  none: false
172
174
  requirements:
@@ -175,13 +177,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
177
  version: '0'
176
178
  segments:
177
179
  - 0
178
- hash: 1653831612304394743
180
+ hash: -855199531079728294
179
181
  requirements: []
180
182
  rubyforge_project:
181
183
  rubygems_version: 1.8.10
182
184
  signing_key:
183
185
  specification_version: 3
184
- summary: prlbackup-1.1.3
186
+ summary: prlbackup-1.2.0
185
187
  test_files:
186
188
  - features/cleanup.feature
187
189
  - features/create_backups.feature
@@ -189,6 +191,7 @@ test_files:
189
191
  - features/logging.feature
190
192
  - features/steps/dev_steps.rb
191
193
  - features/support/env.rb
194
+ - spec/prlbackup/backup_spec.rb
192
195
  - spec/prlbackup/cli_spec.rb
193
196
  - spec/prlbackup/virtual_machine_spec.rb
194
197
  - spec/prlbackup_spec.rb