prlbackup 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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