|
|
@@ -1,106 +1,135 @@ |
|
|
## place this file to same directory of `Vagrantfile' then simply require "snap.rb" in Vagrantfile |
|
|
# Place this file in the same directory as `Vagrantfile' |
|
|
# then simply require "vagrant-snapshot.rb" at the top of Vagrantfile. |
|
|
|
|
|
require 'optparse' |
|
|
|
|
|
Vagrant.commands.register(:snap) { Snap::Commands } |
|
|
|
|
|
# Provide rake-like desc() 'inflected' documentation |
|
|
# See http://stackoverflow.com/questions/2948328/access-attributes-methods-comments-programmatically-in-ruby |
|
|
class Module |
|
|
private |
|
|
|
|
|
old_method_added = instance_method :method_added |
|
|
define_method :method_added do |meth| |
|
|
(@__doc__ ||= {})[meth] = @__last_doc__ if @__last_doc__ |
|
|
@__last_doc__ = nil |
|
|
old_method_added.bind(self).(meth) |
|
|
end |
|
|
|
|
|
def doc(usage, description) |
|
|
@__last_doc__ = [usage, description] |
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
module Kernel |
|
|
private |
|
|
|
|
|
def get_doc(klass, meth) |
|
|
klass.instance_variable_get(:@__doc__)[meth] |
|
|
end |
|
|
end |
|
|
|
|
|
# Snap commands |
|
|
module Snap |
|
|
module VBox |
|
|
class SnapShot |
|
|
require "forwardable" |
|
|
extend Forwardable |
|
|
def_delegators :@snap, :name, :uuid, :time_stamp |
|
|
@@snaps = [] |
|
|
class << self |
|
|
def is_endnode?() @@current.uuid == @@snaps.last.uuid end |
|
|
|
|
|
def snaps() @@snaps end |
|
|
|
|
|
def parse_tree(vmname) |
|
|
vm = VirtualBox::VM.find( vmname ) |
|
|
@@current = vm.current_snapshot |
|
|
return unless @@current |
|
|
new(vm.root_snapshot).walk |
|
|
end |
|
|
end |
|
|
class Commands < ::Vagrant::Command::Base |
|
|
|
|
|
def initialize(snap) |
|
|
@snap = snap |
|
|
self.class.snaps << self |
|
|
end |
|
|
def current? |
|
|
snap.uuid == @@current.uuid |
|
|
end |
|
|
def register |
|
|
end |
|
|
attr_reader :snap |
|
|
def initialize(argv,env) |
|
|
super |
|
|
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) |
|
|
end |
|
|
|
|
|
def have_child? |
|
|
! @snap.children.empty? |
|
|
def execute |
|
|
if @main_args.include?("-h") || @main_args.include?("--help") |
|
|
# Print the help for all the box commands. |
|
|
return help |
|
|
end |
|
|
|
|
|
def first_child |
|
|
snap.children.first |
|
|
# If we reached this far then we must have a subcommand. If not, |
|
|
# then we also just print the help and exit. |
|
|
command = sub_commands[@sub_command.to_sym] if @sub_command |
|
|
return help if !command || !@sub_command |
|
|
|
|
|
# If the command has wrong args. |
|
|
# Word count is used to determine if args required. |
|
|
if command[0].split.size - 1 != @sub_args.size |
|
|
return help command[0], command[1] |
|
|
end |
|
|
|
|
|
def walk |
|
|
if have_child? |
|
|
s = self.class.new first_child |
|
|
s.walk |
|
|
# Finally run the command |
|
|
@logger.debug("Invoking command : #{command} #{@sub_args.inspect}") |
|
|
self.send(@sub_command, *@sub_args) |
|
|
end |
|
|
|
|
|
def vmname |
|
|
@vagrant_env ||= Vagrant::Environment.new |
|
|
@instance_name ||= "#{@vagrant_env.vms[:default].uuid}" |
|
|
@instance_name |
|
|
end |
|
|
|
|
|
def sub_commands |
|
|
subs = {} |
|
|
methods.each do |m| |
|
|
doc_data = get_doc(Snap::Commands, m.to_sym) |
|
|
if doc_data |
|
|
subs[m] = doc_data |
|
|
end |
|
|
end |
|
|
subs |
|
|
end |
|
|
end |
|
|
|
|
|
class Command < Vagrant::Command::GroupBase |
|
|
register "snap","Manages a snap" |
|
|
doc "help", "Show this help" |
|
|
def help command = nil, description = nil |
|
|
options = OptionParser.new do |opts| |
|
|
if command && description |
|
|
opts.banner = description |
|
|
opts.separator "" |
|
|
opts.separator "Usage: vagrant snap #{command}" |
|
|
else |
|
|
opts.banner = "Usage: vagrant snap <command> [<args>]" |
|
|
opts.separator "" |
|
|
opts.separator "Available subcommands:" |
|
|
|
|
|
no_tasks { |
|
|
def vmname |
|
|
@vagrant_env ||= Vagrant::Environment.new |
|
|
@instance_name ||= "#{@vagrant_env.vms[:default].vm.name}" |
|
|
@instance_name |
|
|
# Add the available subcommands as separators in order to print them |
|
|
# out as well. |
|
|
sub_commands.each do |sub_command, doc_data| |
|
|
opts.separator " #{doc_data[0].ljust(18)} #{doc_data[1]}" |
|
|
end |
|
|
end |
|
|
end |
|
|
} |
|
|
|
|
|
desc "list", "list snapshot" |
|
|
@env.ui.info(options.help, :prefix => false) |
|
|
end |
|
|
|
|
|
doc "list", "List snapshots" |
|
|
def list |
|
|
format = " %s %-20s %s" |
|
|
VBox::SnapShot.parse_tree( vmname ) |
|
|
result = VBox::SnapShot.snaps.map do |s| |
|
|
cur_mark = s.current? ? "*" : " " |
|
|
format % [ cur_mark, s.time_stamp.strftime("%Y%m%d-%H:%M:%S"), s.name ] |
|
|
end |
|
|
puts result |
|
|
system "VBoxManage snapshot #{vmname} list --details" |
|
|
end |
|
|
|
|
|
desc "go [SNAPNAME]", "go to specified snapshot" |
|
|
doc "go [SNAPNAME]", "Go to specified snapshot" |
|
|
def go(snapshot_name) |
|
|
system "VBoxManage controlvm #{vmname} poweroff" |
|
|
system "VBoxManage snapshot #{vmname} restore #{snapshot_name}" |
|
|
system "VBoxManage startvm #{vmname} --type headless" |
|
|
end |
|
|
|
|
|
desc "back", "back to current snapshot" |
|
|
doc "back", "Back to current snapshot" |
|
|
def back |
|
|
system "VBoxManage controlvm #{vmname} poweroff" |
|
|
system "VBoxManage snapshot #{vmname} restorecurrent" |
|
|
system "VBoxManage startvm #{vmname} --type headless" |
|
|
end |
|
|
|
|
|
desc "take", "take snapshot" |
|
|
def take |
|
|
VBox::SnapShot.parse_tree( vmname ) |
|
|
last = VBox::SnapShot.snaps.last |
|
|
new_name = last ? last.name.succ : vmname + "-01" |
|
|
# new_name = last.name.succ |
|
|
if not last or VBox::SnapShot.is_endnode? |
|
|
system "VBoxManage snapshot #{vmname} take #{new_name} --pause" |
|
|
else |
|
|
puts "Can not create" |
|
|
end |
|
|
doc "take [SNAPNAME]", "Take snapshot" |
|
|
def take(snapshot_name) |
|
|
system "VBoxManage snapshot #{vmname} take #{snapshot_name} --pause" |
|
|
end |
|
|
|
|
|
desc "delete", "delete snapshot" |
|
|
doc "delete [SNAPNAME]", "Delete snapshot" |
|
|
def delete(snapshot_name) |
|
|
system "VBoxManage snapshot #{vmname} delete #{snapshot_name}" |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
end |