Permalink
Browse files

Merge branch 'backup_class'

  • Loading branch information...
2 parents d2a8fe0 + 3a760a1 commit 229e0e9a8babab913d2847aea71e7a526df9b760 @bjoernalbers committed May 5, 2012
View
@@ -1,9 +1,11 @@
require 'mixlib/cli'
require 'logger'
require 'shellwords'
+require 'date'
require 'prlbackup/version'
require 'prlbackup/cli'
require 'prlbackup/virtual_machine'
+require 'prlbackup/backup'
module PrlBackup
class << self
View
@@ -0,0 +1,66 @@
+module PrlBackup
+ class Backup
+ class << self
+
+ include PrlBackup
+
+ def all(uuid)
+ backup_list(uuid).split("\n").map { |b| create(b) }.compact.sort
+ end
+
+ def backup_list(uuid)
+ run('prlctl', 'backup-list', uuid)
+ end
+
+ def create(line)
+ re = /^
+ (\{[0-9a-f-]+\}) # VM UUID
+ \s+
+ (\{[0-9a-f-]+\}[\.\d]*) # Backup UUID
+ \s+
+ (\S+) # Node
+ \s+
+ ([\d\/]+\s[\d:]+) # Time
+ \s+
+ ([fi]) # Type
+ \s+
+ (\d+) # Size
+ $/x
+ new(:uuid => $2, :time => $4, :type => $5) if re.match(line)
+ end
+ end
+
+ include PrlBackup
+ include Comparable
+
+ attr_reader :properties
+
+ def initialize(properties)
+ @properties = properties
+ end
+
+ def uuid
+ properties[:uuid]
+ end
+
+ def time
+ DateTime.parse(properties[:time])
+ end
+
+ def full?
+ properties[:type] == 'f'
+ end
+
+ def delete
+ conditionally_run('prlctl', 'backup-delete', '--tag', uuid)
+ end
+
+ def <=>(other)
+ time <=> other.time
+ end
+
+ def to_s
+ "Backup: #{time.strftime('%Y-%m-%d %H:%M:%S')}"
+ end
+ end
+end
View
@@ -1,3 +1,3 @@
module PrlBackup
- VERSION = '1.1.3'
+ VERSION = '1.2.0'
end
@@ -38,8 +38,7 @@ def safely_backup(full=false)
# Cleanup (delete) old backups.
def cleanup
- backups = full_backups
- delete_backup(backups.shift) while backups.count > config[:keep_only]
+ full_backups.shift.delete while full_backups.count > config[:keep_only]
end
# Return the virtual machine's name.
@@ -97,14 +96,11 @@ def stopped?
update_info[/^State:\s+stopped$/]
end
- # List of full backups for the virtual machine.
+ # Cached list of virtual machine's full backups.
+ # @return [Array<Backup>] full backups
def full_backups
- run('prlctl', 'backup-list', uuid).split("\n").map { |l| $1 if l[/^\{[a-f0-9-]+\}\s+(\{[a-f0-9-]+\})[^(\.\d+)]/] }.compact
+ @full_backups ||= Backup.all(uuid).select { |b| b.full? }
end
- # Delete the backup given by backup UUID.
- def delete_backup(backup_uuid)
- conditionally_run('prlctl', 'backup-delete', '--tag', backup_uuid)
- end
end
end
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+module PrlBackup
+ describe Backup do
+ before do
+ @uuid = '{deadbeef}'
+ Backup.stub(:run).and_return('')
+ Backup.stub(:conditionally_run).and_return('')
+ end
+
+ describe '.all' do
+ it 'should query the backup list by UUID' do
+ Backup.should_receive(:backup_list).with(@uuid).and_return('')
+ Backup.all(@uuid)
+ end
+
+ it 'should initialize backups by line' do
+ Backup.stub(:backup_list).and_return("line 1\nline 2")
+ Backup.should_receive(:create).with('line 1')
+ Backup.should_receive(:create).with('line 2')
+ Backup.all(@uuid)
+ end
+
+ it 'should only return successfully initialized backups' do
+ Backup.stub(:backup_list).and_return("line 1\nline 2\nline 3")
+ Backup.stub(:create).and_return(nil, 'backup', nil)
+ Backup.all(@uuid).should eql(['backup'])
+ end
+
+ it 'should return the backups sorted' do
+ backup1 = Backup.new(:time => '02/27/2012 13:11:31')
+ backup2 = Backup.new(:time => '02/27/2012 13:11:32')
+ backup3 = Backup.new(:time => '02/27/2012 13:11:33')
+ Backup.stub(:create).and_return(backup2, backup3, backup1)
+ Backup.stub(:backup_list).and_return("line 1\nline 2\nline 3")
+ Backup.all(@uuid).should eql([backup1, backup2, backup3])
+ end
+ end
+
+ describe '.backup_list' do
+ it 'should query and return the backup list by UUID' do
+ Backup.should_receive(:run).with('prlctl', 'backup-list', @uuid).and_return('backup list')
+ Backup.backup_list(@uuid).should eql('backup list')
+ end
+ end
+
+ describe '.create' do
+ it 'should initialize a backup with properties' do
+ line = '{deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b} psfm.example.com 02/27/2012 13:11:32 f 10537597943'
+ backup = Backup.create(line)
+ backup.properties[:uuid].should eql('{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
+ backup.properties[:time].should eql('02/27/2012 13:11:32')
+ backup.properties[:type].should eql('f')
+ end
+
+ it 'should return nil when the backup line is not parsable' do
+ Backup.create('hello, world.').should be_nil
+ end
+ end
+
+ describe '#uuid' do
+ it 'should return the backup uuid' do
+ backup = Backup.new(:uuid => '{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
+ backup.uuid.should eql('{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}')
+ end
+ end
+
+ describe '#time' do
+ it 'should return the time object' do
+ backup = Backup.new(:time => '02/27/2012 13:11:32')
+ backup.time.year.should eql(2012)
+ backup.time.month.should eql(2)
+ backup.time.day.should eql(27)
+ backup.time.hour.should eql(13)
+ backup.time.min.should eql(11)
+ backup.time.sec.should eql(32)
+ end
+ end
+
+ describe '#full?' do
+ it 'should be true when full' do
+ backup = Backup.new(:type => 'f')
+ backup.should be_full
+ end
+
+ it 'should be false when incremental' do
+ backup = Backup.new(:type => 'i')
+ backup.should_not be_full
+ end
+ end
+
+ describe '#delete' do
+ it 'should delete itself' do
+ backup = Backup.new(:uuid => '{some-backup-uuid}')
+ backup.should_receive(:conditionally_run).with('prlctl', 'backup-delete', '--tag', '{some-backup-uuid}')
+ backup.delete
+ end
+ end
+
+ describe '#<=>' do
+ it 'should compare backups by time' do
+ first_backup = Backup.new(:time => '02/27/2012 13:11:32')
+ last_backup = Backup.new(:time => '02/27/2012 13:11:33')
+ first_backup.should < last_backup
+ end
+ end
+
+ describe '#to_s' do
+ it 'should display the backup time' do
+ backup = Backup.new(:time => '02/27/2012 13:11:32')
+ backup.to_s.should eql('Backup: 2012-02-27 13:11:32')
+ end
+ end
+ end
+end
@@ -112,55 +112,43 @@ module PrlBackup
it 'should delete 2 backups when there are 2 more backups than to keep' do
@vm.stub(:config).and_return({:keep_only => 0})
- @vm.should_receive(:delete_backup).with(@old_backup).once
- @vm.should_receive(:delete_backup).with(@new_backup).once
+ @old_backup.should_receive(:delete).once
+ @new_backup.should_receive(:delete).once
@vm.cleanup
end
it 'should delete the oldest backup when there is 1 more backup than to keep' do
@vm.stub(:config).and_return({:keep_only => 1})
- @vm.should_receive(:delete_backup).with(@old_backup).once
- @vm.should_not_receive(:delete_backup).with(@new_backup)
+ @old_backup.should_receive(:delete).once
+ @new_backup.should_not_receive(:delete)
@vm.cleanup
end
it 'should not delete any backups when there are as many backups as to keep' do
@vm.stub(:config).and_return({:keep_only => 2})
- @vm.should_not_receive(:delete_backup)
+ @old_backup.should_not_receive(:delete)
+ @new_backup.should_not_receive(:delete)
@vm.cleanup
end
end
- describe '#delete_backup' do
- it 'should delete the virtual machines backup' do
- @vm.should_receive(:conditionally_run).with('prlctl', 'backup-delete', '--tag', '{some-backup-uuid}')
- @vm.instance_eval { delete_backup('{some-backup-uuid}') }
- end
- end
-
describe '#full_backups' do
before do
- stdout = 'prlctl backup-list {bf364fd4-8f6b-4032-818d-4958f9c0945b}
-ID Backup_ID Node Date Type Size
-{deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b} psfm.example.com 02/27/2012 13:11:32 f 10537597943
-{deadbeef} {ae6565dd-7f8f-42cb-a088-8b1d98f5160b}.2 psfm.example.com 02/27/2012 15:26:02 i 2951747588
-{deadbeef} {5f9dd263-ec56-443e-9917-dab9b40d3027} psfm.example.com 03/13/2012 18:06:00 f 11748325372
-{deadbeef} {2aeb4ada-6623-4087-9fc5-f09aeaafd81e} psfm.example.com 03/23/2012 21:25:50 f 47315014888
-{deadbeef} {68f7e154-6755-46f6-ad1f-a79c5f488f35} psfm.example.com 03/28/2012 15:09:05 f 23462808438
-{deadbeef} {68f7e154-6755-46f6-ad1f-a79c5f488f35}.2 psfm.example.com 04/05/2012 17:21:12 i 12841952117'
- @vm.stub(:run).and_return(stdout)
- end
-
- it 'should query the backup list by CLI' do
- @vm.should_receive(:run).with('prlctl', 'backup-list', @uuid)
- @vm.instance_eval { full_backups }
- end
-
- it 'should return a list of the virtual machines full backup UUIDs' do
- @vm.instance_eval { full_backups }.should eql(['{ae6565dd-7f8f-42cb-a088-8b1d98f5160b}',
- '{5f9dd263-ec56-443e-9917-dab9b40d3027}',
- '{2aeb4ada-6623-4087-9fc5-f09aeaafd81e}',
- '{68f7e154-6755-46f6-ad1f-a79c5f488f35}'])
+ Backup.stub(:all).and_return([])
+ end
+
+ it 'should query list of backups for given UUID once' do
+ Backup.should_receive(:all).with(@vm.uuid).once
+ 2.times { @vm.instance_eval { full_backups } }
+ end
+
+ it 'should return only full backups' do
+ full_backup = double('full backup')
+ full_backup.stub(:full?).and_return(true)
+ incremental_backup = double('incremental backup')
+ incremental_backup.stub(:full?).and_return(false)
+ Backup.stub(:all).and_return([full_backup, incremental_backup])
+ @vm.instance_eval { full_backups }.should eql([full_backup])
end
end

0 comments on commit 229e0e9

Please sign in to comment.