module VirtualBox # Represents a virtual machine snapshot. Snapshots allow users of virtual # machines to make a lightweight "snapshot" of a virtual machine at any # moment. This snapshot can be taken while the virtual machine is running # or while its powered off. Snapshotting creates a differencing image for # the hard drive which allows a virtual machine to restore itself to the # exact state of where it was snapshotted. # # # Getting Snapshots # # Snapshots are accessed from the `current_snapshot` relationship on {VM}. # There is no other way to access snapshots. After getting the current # snapshot, you can easily traverse the tree of snapshots by accessing # `parent` or `children` on the snapshot. An example follows: # # vm = VirtualBox::VM.find("MyWindowsXP") # p vm.current_snapshot # the current snapshot # p vm.current_snapshot.children # The children of the current snapshot # p vm.current_snapshot.parent # The current snapshot's parent # p vm.current_snapshot.parent.parent.children # You get the idea. # # # Taking a Snapshot # # To take a snapshot, call the {VM#take_snapshot} method. Please view # the documentation on that method for more information. # # # Restoring a Snapshot # # To restore a snapshot, call the {#restore} method. A simple example # is shown below: # # vm = VirtualBox::VM.find("MyWindowsXP") # snapshot = vm.current_snapshot.parent # snapshot.restore # # **Note:** The VM object will not immediately update to reflect any settings # changes or current snapshot changes from the restore. To grab the updates, # either load a new VM object or call {VM#reload}. # # # Deleting a Snapshot # # To delete a snapshot, simply find the snapshot of interest and call # it's {#destroy} method. A quick example is shown below: # # vm = VirtualBox::VM.find("MyWindowsXP") # snapshot = vm.current_snapshot # Grab the current snapshot # snapshot.destroy # Destroy it # # Note that this doesn't actually affect the `current_snapshot` # relationship on the VM. To update all the proper values, you have to call # {VM#reload}. # class Snapshot < AbstractModel attribute :uuid, :readonly => true, :property => :id attribute :name attribute :description attribute :time_stamp, :readonly => true attribute :online, :readonly => true, :boolean => true attribute :interface, :readonly => true, :property => false relationship :parent, :Snapshot, :lazy => true relationship :machine, :VM, :lazy => true relationship :children, :Snapshot, :lazy => true class << self # Populates a relationship with another model. Since a snapshot # can be in a relationship with multiple items, this method forwards # to other methods such as {populate_vm_relationship}. # # **This method typically won't be used except internally.** def populate_relationship(caller, data) if data.is_a?(COM::Util.versioned_interface(:Machine)) populate_machine_relationship(caller, data) elsif data.is_a?(Array) populate_children_relationship(caller, data) elsif data.is_a?(COM::Util.versioned_interface(:Snapshot)) || data.nil? populate_parent_relationship(caller, data) else raise Exceptions::Exception.new("Invalid relationship data for Snapshot: #{data}") end end # Populates the VM relationship as the "current snapshot." # # **This method typically won't be used except internally.** # # @return [Snapshot] def populate_machine_relationship(caller, machine) # The current snapshot can and will be nil if there are no snapshots # taken. In that case, we just return nil. snapshot = machine.current_snapshot snapshot ? new(machine.current_snapshot) : nil end # Populates the VM relationship as the "current snapshot." # # **This method typically won't be used except internally.** # # @return [Snapshot] def populate_parent_relationship(caller, parent) # If the parent is nil then that means the child is the root # snapshot parent ? new(parent) : nil end # Populates the snapshot child tree relationship. # # **This method typically won't be used except internally.** # # @return [Array] def populate_children_relationship(caller, snapshots) result = Proxies::Collection.new(caller) snapshots.each do |snapshot| result << new(snapshot) end result end end # Initializes a new snapshot. This should never be called on its own. # Snapshots should be accessed beginning with the `current_snapshot` on # a VM, and can be further accessed by traversing the parent/child tree # of the snapshot. def initialize(snapshot) write_attribute(:interface, snapshot) initialize_attributes(snapshot) end def initialize_attributes(snapshot) # Load the interface attributes load_interface_attributes(snapshot) # Clear dirtiness, since this should only be called initially and # therefore shouldn't affect dirtiness clear_dirty! # But this is an existing record existing_record! end # Loads the lazy relationships. # # **This method should only be called internally.** def load_relationship(name) populate_relationship(:parent, interface.parent) populate_relationship(:machine, interface.machine) populate_relationship(:children, interface.children) end # Timestamp that the snapshot was taken. This method overwrites the # typical attribute so it can return a Time object. def time_stamp # Seconds, microseconds, since the time stamp is in milliseconds value = read_attribute(:time_stamp) Time.at(value / 1000, (value % 1000) * 1000) end # Restore a snapshot. This will restore this snapshot's virtual machine # to the state that this snapshot represents. This method will block while # the restore occurs. # # If a block is given to the function, it will be yielded with a progress # object which can be used to track the progress of the operation. def restore(&block) machine.with_open_session do |session| session.console.restore_snapshot(interface).wait(&block) end end # Destroy a snapshot. This will physically remove the snapshot. Once this # method is called, there is no undo. If this snapshot is a parent of other # snapshots, the differencing image of this snapshot will be merged with # the child snapshots so no data is lost. This process can sometimes take # some time. This method will block while this process occurs. # # If a block is given to the function, it will be yielded with a progress # object which can be used to track the progress of the operation. def destroy(&block) machine.with_open_session do |session| session.console.delete_snapshot(uuid).wait(&block) end end end end