prlbackup 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Guardfile +21 -0
- data/README.md +27 -0
- data/Rakefile +14 -0
- data/bin/prlbackup +5 -0
- data/features/cleanup.feature +29 -0
- data/features/create_backups.feature +53 -0
- data/features/dev_steps.feature +60 -0
- data/features/logging.feature +39 -0
- data/features/steps/dev_steps.rb +49 -0
- data/features/support/env.rb +2 -0
- data/lib/prlbackup/cli.rb +90 -0
- data/lib/prlbackup/version.rb +3 -0
- data/lib/prlbackup/virtual_machine.rb +111 -0
- data/lib/prlbackup.rb +51 -0
- data/man/prlbackup +31 -0
- data/man/prlbackup.1 +168 -0
- data/man/prlbackup.1.html +195 -0
- data/man/prlbackup.1.ronn +107 -0
- data/man/prlbackup.html +253 -0
- data/prlbackup.gemspec +32 -0
- data/spec/prlbackup/cli_spec.rb +84 -0
- data/spec/prlbackup/virtual_machine_spec.rb +252 -0
- data/spec/prlbackup_spec.rb +126 -0
- data/spec/spec_helper.rb +1 -0
- metadata +213 -0
data/man/prlbackup.html
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
5
|
+
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
6
|
+
<title>prlbackup</title>
|
7
|
+
<style type='text/css' media='all'>
|
8
|
+
/* style: man */
|
9
|
+
body#manpage {margin:0}
|
10
|
+
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
11
|
+
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
12
|
+
.mp h2 {margin:10px 0 0 0}
|
13
|
+
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
14
|
+
.mp h3 {margin:0 0 0 4ex}
|
15
|
+
.mp dt {margin:0;clear:left}
|
16
|
+
.mp dt.flush {float:left;width:8ex}
|
17
|
+
.mp dd {margin:0 0 0 9ex}
|
18
|
+
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
19
|
+
.mp pre {margin-bottom:20px}
|
20
|
+
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
21
|
+
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
22
|
+
.mp img {display:block;margin:auto}
|
23
|
+
.mp h1.man-title {display:none}
|
24
|
+
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
25
|
+
.mp h2 {font-size:16px;line-height:1.25}
|
26
|
+
.mp h1 {font-size:20px;line-height:2}
|
27
|
+
.mp {text-align:justify;background:#fff}
|
28
|
+
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
29
|
+
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
30
|
+
.mp u {text-decoration:underline}
|
31
|
+
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
32
|
+
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
33
|
+
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
34
|
+
.mp b.man-ref {font-weight:normal;color:#434241}
|
35
|
+
.mp pre {padding:0 4ex}
|
36
|
+
.mp pre code {font-weight:normal;color:#434241}
|
37
|
+
.mp h2+pre,h3+pre {padding-left:0}
|
38
|
+
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
39
|
+
ol.man-decor {width:100%}
|
40
|
+
ol.man-decor li.tl {text-align:left}
|
41
|
+
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
42
|
+
ol.man-decor li.tr {text-align:right;float:right}
|
43
|
+
</style>
|
44
|
+
<style type='text/css' media='all'>
|
45
|
+
/* style: toc */
|
46
|
+
.man-navigation {display:block !important;position:fixed;top:0;left:113ex;height:100%;width:100%;padding:48px 0 0 0;border-left:1px solid #dbdbdb;background:#eee}
|
47
|
+
.man-navigation a,.man-navigation a:hover,.man-navigation a:link,.man-navigation a:visited {display:block;margin:0;padding:5px 2px 5px 30px;color:#999;text-decoration:none}
|
48
|
+
.man-navigation a:hover {color:#111;text-decoration:underline}
|
49
|
+
</style>
|
50
|
+
</head>
|
51
|
+
<!--
|
52
|
+
The following styles are deprecated and will be removed at some point:
|
53
|
+
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
54
|
+
|
55
|
+
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
56
|
+
.man-navigation should be used instead.
|
57
|
+
-->
|
58
|
+
<body id='manpage'>
|
59
|
+
<div class='mp' id='man'>
|
60
|
+
|
61
|
+
<div class='man-navigation' style='display:none'>
|
62
|
+
<a href="#NAME">NAME</a>
|
63
|
+
</div>
|
64
|
+
|
65
|
+
<ol class='man-decor man-head man head'>
|
66
|
+
<li class='tl'>prlbackup</li>
|
67
|
+
<li class='tc'>PRLBACKUP MANUAL</li>
|
68
|
+
<li class='tr'>prlbackup</li>
|
69
|
+
</ol>
|
70
|
+
|
71
|
+
<h2 id="NAME">NAME</h2>
|
72
|
+
<p class="man-name">
|
73
|
+
<code>prlbackup</code>
|
74
|
+
</p>
|
75
|
+
<p>.\" generated with Ronn/v0.7.3
|
76
|
+
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
77
|
+
.
|
78
|
+
.TH "PRLBACKUP" "1" "May 2012" "" "PRLBACKUP MANUAL"
|
79
|
+
.
|
80
|
+
.SH "NAME"
|
81
|
+
\fBprlbackup\fR - an awesome backup tool for Parallels Server Virtual Machines
|
82
|
+
.
|
83
|
+
.SH "SYNOPSIS"
|
84
|
+
\fBprlbackup\fR \fB-h\fR|\fB--help\fR
|
85
|
+
.
|
86
|
+
.br
|
87
|
+
\fBprlbackup\fR [\fIOPTIONS\fR...] \fIvm_id|vm_name\fR...
|
88
|
+
.
|
89
|
+
.br
|
90
|
+
\fBprlbackup\fR [\fIOPTIONS\fR...] \fB-a\fR|\fB--all\fR
|
91
|
+
.
|
92
|
+
.br
|
93
|
+
\fBprlbackup\fR [\fIOPTIONS\fR...] \fB-a\fR|\fB--all\fR \fB-e\fR|\fB--exclude\fR \fIvm_id|vm_name\fR...
|
94
|
+
.
|
95
|
+
.SH "DESCRIPTION"
|
96
|
+
\fBprlbackup\fR simplifies the backup of one or multiple Virtual Machines (VM) running on Parallels Server by stoping them during backup and deleting old backups on demand. A working installation of Parallels Server is required.
|
97
|
+
.
|
98
|
+
.P
|
99
|
+
\fBprlbackup\fR was tested under Parallels Server for Mac 4.0 (PSfM).
|
100
|
+
.
|
101
|
+
.SH "OPTIONS"
|
102
|
+
.
|
103
|
+
.TP
|
104
|
+
\fB-h\fR, \fB--help\fR
|
105
|
+
Display a short help.
|
106
|
+
.
|
107
|
+
.TP
|
108
|
+
\fB-a\fR, \fB--all\fR
|
109
|
+
Backup all virtual machines which are registered in Parallels Server.
|
110
|
+
.
|
111
|
+
.TP
|
112
|
+
\fB-e\fR, \fB--exclude\fR
|
113
|
+
Backup all but the given virtual machines (only applicable in combination with option \fB--all\fR!).
|
114
|
+
.
|
115
|
+
.TP
|
116
|
+
\fB-f\fR, \fB--full\fR
|
117
|
+
Create full backups (by default incremental backups are created). Note that the first backup for a VM is always a full backup despide of this option.
|
118
|
+
.
|
119
|
+
.TP
|
120
|
+
\fB-v\fR, \fB--verbose\fR
|
121
|
+
Show commands with an impact on the VMs prior to their execution.
|
122
|
+
.
|
123
|
+
.TP
|
124
|
+
\fB-n\fR, \fB--dry-run\fR
|
125
|
+
Don\'t do anything with the VMs, just pretend to.
|
126
|
+
.
|
127
|
+
.TP
|
128
|
+
\fB-k\fR \fInumber_of_full_backups\fR, \fB--keep-only\fR \fInumber_of_full_backups\fR
|
129
|
+
Delete the oldest full backup(s) until only \fInumber_of_full_backups\fR exist. This cleanup action will be executed for each VM right after creating the corresponding backup but only if the backup was successfully created. Incremental backups are automatically deleted by Parallels Server when their full backup is deleted.
|
130
|
+
.
|
131
|
+
.SH "EXAMPLES"
|
132
|
+
Display short help:
|
133
|
+
.
|
134
|
+
.IP "" 4
|
135
|
+
.
|
136
|
+
.nf</p>
|
137
|
+
|
138
|
+
<p>$ prlbackup --help
|
139
|
+
.
|
140
|
+
.fi
|
141
|
+
.
|
142
|
+
.IP "" 0
|
143
|
+
.
|
144
|
+
.P
|
145
|
+
Backup VMs by name:
|
146
|
+
.
|
147
|
+
.IP "" 4
|
148
|
+
.
|
149
|
+
.nf</p>
|
150
|
+
|
151
|
+
<p>$ prlbackup Alpha Bravo Charlie
|
152
|
+
.
|
153
|
+
.fi
|
154
|
+
.
|
155
|
+
.IP "" 0
|
156
|
+
.
|
157
|
+
.P
|
158
|
+
Backup a VM by id:
|
159
|
+
.
|
160
|
+
.IP "" 4
|
161
|
+
.
|
162
|
+
.nf</p>
|
163
|
+
|
164
|
+
<p>$ prlbackup "{97351580-afd7-4aff-9960-814196b28e37}"
|
165
|
+
.
|
166
|
+
.fi
|
167
|
+
.
|
168
|
+
.IP "" 0
|
169
|
+
.
|
170
|
+
.P
|
171
|
+
Create full backups of all VMs
|
172
|
+
.
|
173
|
+
.IP "" 4
|
174
|
+
.
|
175
|
+
.nf</p>
|
176
|
+
|
177
|
+
<p>$ prlbackup --full --all
|
178
|
+
.
|
179
|
+
.fi
|
180
|
+
.
|
181
|
+
.IP "" 0
|
182
|
+
.
|
183
|
+
.P
|
184
|
+
Backup all but not the given VMs:
|
185
|
+
.
|
186
|
+
.IP "" 4
|
187
|
+
.
|
188
|
+
.nf</p>
|
189
|
+
|
190
|
+
<p>$ prlbackup --all --exclude Delta Echo
|
191
|
+
.
|
192
|
+
.fi
|
193
|
+
.
|
194
|
+
.IP "" 0
|
195
|
+
.
|
196
|
+
.P
|
197
|
+
Show which commands would have been run when backing up all VMs:
|
198
|
+
.
|
199
|
+
.IP "" 4
|
200
|
+
.
|
201
|
+
.nf</p>
|
202
|
+
|
203
|
+
<p>$ prlbackup --dry-run --verbose --all
|
204
|
+
.
|
205
|
+
.fi
|
206
|
+
.
|
207
|
+
.IP "" 0
|
208
|
+
.
|
209
|
+
.P
|
210
|
+
Create the weekly full backup for all VMs and delete backups older than four weeks:
|
211
|
+
.
|
212
|
+
.IP "" 4
|
213
|
+
.
|
214
|
+
.nf</p>
|
215
|
+
|
216
|
+
<p>$ prlbackup --all --keep-only 5
|
217
|
+
.
|
218
|
+
.fi
|
219
|
+
.
|
220
|
+
.IP "" 0
|
221
|
+
.
|
222
|
+
.SH "SEE ALSO"
|
223
|
+
.
|
224
|
+
.IP "(bu" 4
|
225
|
+
<span class="man-ref">prlctl<span class="s">(8)</span></span>
|
226
|
+
.
|
227
|
+
.IP "(bu" 4
|
228
|
+
prlbackup\'s homepage \fIhttps://github.com/bjoernalbers/prlbackup\fR
|
229
|
+
.
|
230
|
+
.IP "" 0
|
231
|
+
.
|
232
|
+
.SH "COPYRIGHT"
|
233
|
+
Copyright (c) 2012 Bjoern Albers (\fIbjoernalbers@googlemail.com\fR)
|
234
|
+
.
|
235
|
+
.P
|
236
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
237
|
+
.
|
238
|
+
.P
|
239
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
240
|
+
.
|
241
|
+
.P
|
242
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
243
|
+
|
244
|
+
|
245
|
+
<ol class='man-decor man-foot man foot'>
|
246
|
+
<li class='tl'></li>
|
247
|
+
<li class='tc'>May 2012</li>
|
248
|
+
<li class='tr'>prlbackup</li>
|
249
|
+
</ol>
|
250
|
+
|
251
|
+
</div>
|
252
|
+
</body>
|
253
|
+
</html>
|
data/prlbackup.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "prlbackup/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'prlbackup'
|
7
|
+
s.version = PrlBackup::VERSION
|
8
|
+
s.authors = ['Björn Albers']
|
9
|
+
s.email = ['bjoernalbers@googlemail.com']
|
10
|
+
s.homepage = "https://github.com/bjoernalbers/#{s.name}"
|
11
|
+
s.summary = 'an awesome backup tool for Parallels Server Virtual Machines'
|
12
|
+
s.description = %q{prlbackup simplifies the backup of one or multiple Virtual Machines
|
13
|
+
(VM) running on Parallels Server by stoping them during backup and
|
14
|
+
deleting old backups on demand.}
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.add_dependency 'mixlib-cli', '>= 1.2.0'
|
21
|
+
s.add_dependency 'gem-man', '>= 0.3.0'
|
22
|
+
|
23
|
+
s.add_development_dependency 'cucumber', '>= 1.1.4'
|
24
|
+
s.add_development_dependency 'aruba', '>= 0.4.11'
|
25
|
+
s.add_development_dependency 'aruba-doubles', '~> 1.2.1'
|
26
|
+
s.add_development_dependency 'guard-cucumber', '>= 0.7.5'
|
27
|
+
s.add_development_dependency 'guard-rspec', '>= 0.5.1'
|
28
|
+
s.add_development_dependency 'guard-ronn', '>= 0.1.2'
|
29
|
+
s.add_development_dependency 'ronn', '>= 0.7.3'
|
30
|
+
s.add_development_dependency 'rake'
|
31
|
+
s.add_development_dependency 'rb-fsevent', '>= 0.9.0' if RUBY_PLATFORM =~ /darwin/i
|
32
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PrlBackup
|
4
|
+
describe CLI do
|
5
|
+
describe '.run' do
|
6
|
+
before do
|
7
|
+
@cli = double('cli')
|
8
|
+
@cli.stub(:run)
|
9
|
+
CLI.stub(:new).and_return(@cli)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should initialize a new instance' do
|
13
|
+
CLI.should_receive(:new).and_return(@cli)
|
14
|
+
CLI.run
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should run the new instance with ARGV' do
|
18
|
+
@cli.should_receive(:run).with(ARGV)
|
19
|
+
CLI.run
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#run' do
|
24
|
+
before do
|
25
|
+
@vm = double('vm')
|
26
|
+
@vm.stub(:safe_backup)
|
27
|
+
@vm.stub(:cleanup)
|
28
|
+
VirtualMachine.stub(:new).and_return(@vm)
|
29
|
+
@cli = CLI.new
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'without options' do
|
33
|
+
before do
|
34
|
+
VirtualMachine.should_receive(:new).with('foo').and_return(@vm)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should backup each selected virtual machine' do
|
38
|
+
@vm.should_receive(:safe_backup).once
|
39
|
+
@cli.run %w[foo]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not perform cleanup actions' do
|
43
|
+
@vm.should_not_receive(:cleanup)
|
44
|
+
@cli.run %w[foo]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with option --all' do
|
49
|
+
it 'should backup all virtual machines' do
|
50
|
+
VirtualMachine.should_receive(:all).and_return([@vm, @vm])
|
51
|
+
@vm.should_receive(:safe_backup).twice
|
52
|
+
@cli.run %w[--all]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with options --all and --exclude' do
|
57
|
+
before do
|
58
|
+
VirtualMachine.should_receive(:all).and_return([@vm, @vm])
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should not backup virtual machines which are given' do
|
62
|
+
@cli.stub(:given_virtual_machines).and_return([@vm])
|
63
|
+
@vm.should_not_receive(:safe_backup)
|
64
|
+
@cli.run %w[--all --exclude foo]
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should backup virtual machines which are not given' do
|
68
|
+
@cli.stub(:given_virtual_machines).and_return([])
|
69
|
+
@vm.should_receive(:safe_backup).twice
|
70
|
+
@cli.run %w[--all --exclude foo]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with option --keep-only' do
|
75
|
+
it 'should perform cleanup actions after backing up' do
|
76
|
+
@vm.should_receive(:safe_backup).ordered
|
77
|
+
@vm.should_receive(:cleanup).ordered
|
78
|
+
@cli.stub(:config).and_return({:keep_only => 3})
|
79
|
+
@cli.run %w[foo]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PrlBackup
|
4
|
+
describe VirtualMachine do
|
5
|
+
before do
|
6
|
+
VirtualMachine.any_instance.stub(:command).and_return('')
|
7
|
+
@uuid = '{deadbeef}'
|
8
|
+
@vm = VirtualMachine.new('foo')
|
9
|
+
@vm.stub(:uuid).and_return(@uuid)
|
10
|
+
@vm.stub(:logger).and_return(stub(:info => nil))
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.each' do
|
14
|
+
it 'should iterate over all virtual machines' do
|
15
|
+
virtual_machines = []
|
16
|
+
3.times { |i| virtual_machines << mock("virtual_machine_#{i}") }
|
17
|
+
virtual_machines.map { |vm| vm.should_receive(:foo) }
|
18
|
+
VirtualMachine.stub(:all).and_return(virtual_machines)
|
19
|
+
VirtualMachine.each { |vm| vm.foo }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.all' do
|
24
|
+
before do
|
25
|
+
@stdout = %q{UUID
|
26
|
+
{264eab18-563e-4ccb-935d-50269130c592}
|
27
|
+
{b0749d89-27c5-4d0f-88e4-b8aeab95cd35}
|
28
|
+
{b893da77-f465-4de4-ab3f-f77e16f0c485} }
|
29
|
+
@uuids = ['{264eab18-563e-4ccb-935d-50269130c592}',
|
30
|
+
'{b0749d89-27c5-4d0f-88e4-b8aeab95cd35}',
|
31
|
+
'{b893da77-f465-4de4-ab3f-f77e16f0c485}']
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return a list of all virtual machines' do
|
35
|
+
vm = mock('virtual_machine')
|
36
|
+
VirtualMachine.stub(:command).and_return(@stdout)
|
37
|
+
VirtualMachine.stub(:new).and_return(vm)
|
38
|
+
VirtualMachine.all.should eql([vm, vm, vm])
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should return an empty list if no virtual machines exist' do
|
42
|
+
VirtualMachine.stub(:command).and_return('')
|
43
|
+
VirtualMachine.all.should eql([])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should instantiate all virtual machines by their uuid' do
|
47
|
+
VirtualMachine.stub(:command).and_return(@stdout)
|
48
|
+
@uuids.each { |uuid| VirtualMachine.should_receive(:new).with(uuid) }
|
49
|
+
VirtualMachine.all
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should query a list of all virtual machines via command' do
|
53
|
+
cmd = %w{prlctl list --all --output uuid}
|
54
|
+
VirtualMachine.should_receive(:command).with(*cmd).and_return(@stdout)
|
55
|
+
VirtualMachine.stub(:new)
|
56
|
+
VirtualMachine.all
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#config' do
|
61
|
+
it 'should return the global config' do
|
62
|
+
PrlBackup.should_receive(:config).and_return({:foo => 'bar'})
|
63
|
+
@vm.config.should eql({:foo => 'bar'})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
%w[start stop].each do |cmd|
|
68
|
+
describe "##{cmd}" do
|
69
|
+
it "should #{cmd} the virtual machine" do
|
70
|
+
@vm.should_receive(:command!).with('prlctl', cmd, @uuid).and_return('')
|
71
|
+
@vm.send(cmd)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#backup' do
|
77
|
+
it 'should create an incremental backup by default' do
|
78
|
+
@vm.stub(:config).and_return({})
|
79
|
+
@vm.should_receive(:command!).with('prlctl', 'backup', @uuid)
|
80
|
+
@vm.instance_eval { backup }
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should create a full backup when configured' do
|
84
|
+
@vm.stub(:config).and_return({:full => true})
|
85
|
+
@vm.should_receive(:command!).with('prlctl', 'backup', @uuid, '--full')
|
86
|
+
@vm.instance_eval { backup }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#safe_backup' do
|
91
|
+
it 'should stop the VM during the backup' do
|
92
|
+
@vm.stub(:stopped?).and_return(false)
|
93
|
+
@vm.should_receive(:stop).ordered
|
94
|
+
@vm.should_receive(:backup).ordered
|
95
|
+
@vm.should_receive(:start).ordered
|
96
|
+
@vm.safe_backup
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not stop the VM when already shutdown' do
|
100
|
+
@vm.stub(:stopped?).and_return(true)
|
101
|
+
@vm.should_receive(:backup)
|
102
|
+
@vm.safe_backup
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#cleanup' do
|
107
|
+
before do
|
108
|
+
@old_backup = double('old backup')
|
109
|
+
@new_backup = double('new backup')
|
110
|
+
@vm.stub(:full_backups).and_return([@old_backup, @new_backup])
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should delete 2 backups when there are 2 more backups than to keep' do
|
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
|
117
|
+
@vm.cleanup
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should delete the oldest backup when there is 1 more backup than to keep' do
|
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)
|
124
|
+
@vm.cleanup
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should not delete any backups when there are as many backups as to keep' do
|
128
|
+
@vm.stub(:config).and_return({:keep_only => 2})
|
129
|
+
@vm.should_not_receive(:delete_backup)
|
130
|
+
@vm.cleanup
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#delete_backup' do
|
135
|
+
it 'should delete the virtual machines backup' do
|
136
|
+
@vm.should_receive(:command!).with('prlctl', 'backup-delete', '--tag', '{some-backup-uuid}')
|
137
|
+
@vm.instance_eval { delete_backup('{some-backup-uuid}') }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#full_backups' do
|
142
|
+
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(:command).and_return(stdout)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should query the backup list by CLI' do
|
155
|
+
@vm.should_receive(:command).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}'])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#name' do
|
168
|
+
it 'should return the name of the virtual machine' do
|
169
|
+
@vm.stub(:info).and_return('Name: foo')
|
170
|
+
@vm.name.should eql('foo')
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should return nil if the name cannot be parsed' do
|
174
|
+
@vm.stub(:info).and_return(nil)
|
175
|
+
@vm.name.should be_nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#uuid' do
|
180
|
+
before do
|
181
|
+
@vm = VirtualMachine.new('foo')
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should return the virtual machine's UUID" do
|
185
|
+
@vm.stub(:info).and_return('ID: {423dba54-45e3-46f1-9aa2-87d61ce6b757}')
|
186
|
+
@vm.uuid.should eql('{423dba54-45e3-46f1-9aa2-87d61ce6b757}')
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should return nil if the uuid cannot be parsed' do
|
190
|
+
@vm.stub(:info).and_return(nil)
|
191
|
+
@vm.uuid.should be_nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe '#update_info' do
|
196
|
+
it 'should query infos about the virtual machine' do
|
197
|
+
@vm.should_receive(:command).with('prlctl', 'list', '--info', 'foo')
|
198
|
+
@vm.instance_eval { update_info }
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should update and return the infos' do
|
202
|
+
@vm.stub(:command).and_return('Foo: Bar', 'Foo: Baz')
|
203
|
+
@vm.instance_eval { update_info }.should eql('Foo: Bar')
|
204
|
+
@vm.instance_eval { info }.should eql('Foo: Bar')
|
205
|
+
@vm.instance_eval { update_info }.should eql('Foo: Baz')
|
206
|
+
@vm.instance_eval { info }.should eql('Foo: Baz')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#stopped?' do
|
211
|
+
it 'should return true when virtual machine is stopped' do
|
212
|
+
@vm.stub!(:update_info).and_return('State: stopped')
|
213
|
+
@vm.should be_stopped
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should return false when virtual machine is not stopped' do
|
217
|
+
@vm.stub!(:update_info).and_return('State: running')
|
218
|
+
@vm.should_not be_stopped
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '#==' do
|
223
|
+
before do
|
224
|
+
@other_vm = VirtualMachine.new('other_vm')
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should be true when UUIDs are equal' do
|
228
|
+
@other_vm.stub(:uuid).and_return(@vm.uuid)
|
229
|
+
@vm.should == @other_vm
|
230
|
+
@other_vm.should == @vm
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should be false when UUIDs are not equal' do
|
234
|
+
@other_vm.stub(:uuid).and_return('{a-completely-different-uuid}')
|
235
|
+
@vm.should_not == @other_vm
|
236
|
+
@other_vm.should_not == @vm
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe '#to_s' do
|
241
|
+
it 'should return the name' do
|
242
|
+
@vm.should_receive('name').and_return('name_of_the_vm')
|
243
|
+
@vm.to_s.should eql('name_of_the_vm')
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should return "Unknown VM" if name is nil' do
|
247
|
+
@vm.should_receive(:name).and_return(nil)
|
248
|
+
@vm.to_s.should eql('Unknown VM')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|