virtualbox 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +11 -0
- data/Rakefile +32 -0
- data/Readme.md +75 -0
- data/TODO +13 -0
- data/VERSION +1 -0
- data/lib/virtualbox.rb +11 -0
- data/lib/virtualbox/abstract_model.rb +95 -0
- data/lib/virtualbox/abstract_model/attributable.rb +193 -0
- data/lib/virtualbox/abstract_model/dirty.rb +164 -0
- data/lib/virtualbox/abstract_model/relatable.rb +163 -0
- data/lib/virtualbox/attached_device.rb +104 -0
- data/lib/virtualbox/command.rb +54 -0
- data/lib/virtualbox/dvd.rb +31 -0
- data/lib/virtualbox/errors.rb +7 -0
- data/lib/virtualbox/ext/subclass_listing.rb +24 -0
- data/lib/virtualbox/hard_drive.rb +169 -0
- data/lib/virtualbox/image.rb +94 -0
- data/lib/virtualbox/nic.rb +150 -0
- data/lib/virtualbox/storage_controller.rb +122 -0
- data/lib/virtualbox/vm.rb +287 -0
- data/test/test_helper.rb +25 -0
- data/test/virtualbox/abstract_model/attributable_test.rb +150 -0
- data/test/virtualbox/abstract_model/dirty_test.rb +66 -0
- data/test/virtualbox/abstract_model/relatable_test.rb +141 -0
- data/test/virtualbox/abstract_model_test.rb +146 -0
- data/test/virtualbox/attached_device_test.rb +92 -0
- data/test/virtualbox/command_test.rb +30 -0
- data/test/virtualbox/dvd_test.rb +58 -0
- data/test/virtualbox/ext/subclass_listing_test.rb +25 -0
- data/test/virtualbox/hard_drive_test.rb +161 -0
- data/test/virtualbox/image_test.rb +113 -0
- data/test/virtualbox/nic_test.rb +119 -0
- data/test/virtualbox/storage_controller_test.rb +79 -0
- data/test/virtualbox/vm_test.rb +313 -0
- metadata +103 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Gems required for testing only. To install run
|
2
|
+
# gem bundle test
|
3
|
+
only :test do
|
4
|
+
gem "contest", ">= 0.1.2"
|
5
|
+
gem "mocha"
|
6
|
+
gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9'
|
7
|
+
end
|
8
|
+
|
9
|
+
# Makes sure that our code doesn't request gems outside
|
10
|
+
# of our dependency list.
|
11
|
+
disable_system_gems
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "virtualbox"
|
5
|
+
gemspec.summary = "Create and modify virtual machines in VirtualBox using pure ruby."
|
6
|
+
gemspec.description = "Create and modify virtual machines in VirtualBox using pure ruby."
|
7
|
+
gemspec.email = "mitchell.hashimoto@gmail.com"
|
8
|
+
gemspec.homepage = "http://github.com/mitchellh/virtualbox"
|
9
|
+
gemspec.authors = ["Mitchell Hashimoto"]
|
10
|
+
end
|
11
|
+
Jeweler::GemcutterTasks.new
|
12
|
+
rescue LoadError
|
13
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
task :default => :test
|
19
|
+
|
20
|
+
Rake::TestTask.new do |t|
|
21
|
+
t.libs << "test"
|
22
|
+
t.pattern = 'test/**/*_test.rb'
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'yard'
|
27
|
+
YARD::Rake::YardocTask.new do |t|
|
28
|
+
t.options = ['--main', 'Readme.md', '--markup', 'markdown', '--files', 'TODO']
|
29
|
+
end
|
30
|
+
rescue LoadError
|
31
|
+
puts "Yard not available. Install it with: gem install yard"
|
32
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# VirtualBox Ruby Gem
|
2
|
+
|
3
|
+
The VirtualBox ruby gem is a library which allows anyone to control VirtualBox
|
4
|
+
from ruby code! Create, destroy, start, stop, suspend, and resume virtual machines.
|
5
|
+
Also list virtual machines, list hard drives, network devices, etc.
|
6
|
+
|
7
|
+
## Installation and Requirements
|
8
|
+
|
9
|
+
First you need to install [VirtualBox](http://www.virtualbox.org/) which is available for
|
10
|
+
Windows, Linux, and OS X. After installation, install the gem:
|
11
|
+
|
12
|
+
sudo gem install virtualbox
|
13
|
+
|
14
|
+
The gem assumes that `VBoxManage` will be available on the `PATH`. If not, before using
|
15
|
+
the gem, you must set the path to your `VBoxManage` binary:
|
16
|
+
|
17
|
+
VirtualBox::Command.vboxmanage = "/path/to/my/VBoxManage"
|
18
|
+
|
19
|
+
## Basic Usage
|
20
|
+
|
21
|
+
The virtualbox gem is modeled after ActiveRecord. If you've used ActiveRecord, you'll
|
22
|
+
feel very comfortable using the virtualbox gem.
|
23
|
+
|
24
|
+
Complete documentation can be found at [http://mitchellh.github.com/virtualbox](http://mitchellh.github.com/virtualbox).
|
25
|
+
|
26
|
+
Below are some examples:
|
27
|
+
|
28
|
+
require 'virtualbox'
|
29
|
+
|
30
|
+
vm = VirtualBox::VM.find("my-vm")
|
31
|
+
|
32
|
+
# Let's first print out some basic info about the VM
|
33
|
+
puts "Memory: #{vm.memory}"
|
34
|
+
|
35
|
+
vm.storage_controllers.each do |sc
|
36
|
+
sc.attached_devices.each do |device|
|
37
|
+
puts "Attached Device: #{device.uuid}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Let's modify the memory and name...
|
42
|
+
vm.memory = 360
|
43
|
+
vm.name = "my-renamed-vm"
|
44
|
+
|
45
|
+
# Save it!
|
46
|
+
vm.save
|
47
|
+
|
48
|
+
Or here is an example of creating a hard drive:
|
49
|
+
|
50
|
+
require 'virtualbox'
|
51
|
+
|
52
|
+
hd = VirtualBox::HardDrive.new
|
53
|
+
hd.location = "foo.vdi"
|
54
|
+
hd.size = 2000 # megabytes
|
55
|
+
hd.save
|
56
|
+
|
57
|
+
## Known Issues or Uncompleted Features
|
58
|
+
|
59
|
+
VirtualBox has a _ton_ of features! As such, this gem is still not totally complete.
|
60
|
+
You can see the features that are still left to do in the TODO file.
|
61
|
+
|
62
|
+
## Reporting Bugs or Feature Requests
|
63
|
+
|
64
|
+
Please use the [issue tracker](https://github.com/mitchellh/virtualbox/issues).
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
If you'd like to contribute to VirtualBox, the first step to developing is to
|
69
|
+
clone this repo, get [wycat's bundler](http://github.com/wycats/bundler) if you
|
70
|
+
don't have it already, and do the following:
|
71
|
+
|
72
|
+
gem bundle test
|
73
|
+
rake
|
74
|
+
|
75
|
+
This will run the test suite, which should come back all green! Then you're good to go!
|
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
Not all these features will make it into initial releases of virtualbox ruby gem.
|
4
|
+
But they will definitely all make it for version 1.0.
|
5
|
+
|
6
|
+
* Creating a VM from scratch (non-import)
|
7
|
+
* Pause/Resume VMs
|
8
|
+
* Exporting a VM
|
9
|
+
* Modifying attached devices
|
10
|
+
* Parsing bridged IFs
|
11
|
+
* Shared folders
|
12
|
+
* Snapshots
|
13
|
+
* Getting/Setting guest properties
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/virtualbox.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
require 'virtualbox/errors'
|
3
|
+
require 'virtualbox/command'
|
4
|
+
require 'virtualbox/abstract_model'
|
5
|
+
require 'virtualbox/image'
|
6
|
+
require 'virtualbox/attached_device'
|
7
|
+
require 'virtualbox/dvd'
|
8
|
+
require 'virtualbox/hard_drive'
|
9
|
+
require 'virtualbox/nic'
|
10
|
+
require 'virtualbox/storage_controller'
|
11
|
+
require 'virtualbox/vm'
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'virtualbox/abstract_model/attributable'
|
2
|
+
require 'virtualbox/abstract_model/dirty'
|
3
|
+
require 'virtualbox/abstract_model/relatable'
|
4
|
+
|
5
|
+
module VirtualBox
|
6
|
+
# AbstractModel is the base class used for most of virtualbox's classes.
|
7
|
+
# It provides convenient ActiveRecord-style model behavior to subclasses.
|
8
|
+
#
|
9
|
+
# @abstract
|
10
|
+
class AbstractModel
|
11
|
+
include Attributable
|
12
|
+
include Dirty
|
13
|
+
include Relatable
|
14
|
+
|
15
|
+
# Returns a boolean denoting if the record is new or existing. This
|
16
|
+
# method is provided for subclasses to use to differentiate between
|
17
|
+
# creating a new object or saving an existing one. An example of this
|
18
|
+
# is {HardDrive#save} which will create a new hard drive if it didn't
|
19
|
+
# previously exist, or save an old one if it did exist.
|
20
|
+
def new_record?
|
21
|
+
@new_record = true if @new_record.nil?
|
22
|
+
@new_record
|
23
|
+
end
|
24
|
+
|
25
|
+
# Saves the model attributes and relationships.
|
26
|
+
#
|
27
|
+
# The method can be passed any arbitrary arguments, which are
|
28
|
+
# implementation specific (see {VM#save}, which does this).
|
29
|
+
def save(*args)
|
30
|
+
# Go through changed attributes and call save_attribute for
|
31
|
+
# those only
|
32
|
+
changes.each do |key, values|
|
33
|
+
save_attribute(key, values[1], *args)
|
34
|
+
end
|
35
|
+
|
36
|
+
save_relationships(*args)
|
37
|
+
|
38
|
+
# No longer a new record
|
39
|
+
@new_record = false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Saves a single attribute of the model. This method on the abstract
|
43
|
+
# model does nothing on its own, and is expected to be overridden
|
44
|
+
# by any subclasses.
|
45
|
+
#
|
46
|
+
# This method clears the dirty status of the attribute.
|
47
|
+
def save_attribute(key, value, *args)
|
48
|
+
clear_dirty!(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the initial attributes from a hash. This method is meant to be used
|
52
|
+
# once to initially setup the attributes. It is **not a mass-assignment**
|
53
|
+
# method for updating attributes.
|
54
|
+
#
|
55
|
+
# This method does **not** affect dirtiness, but also does not clear it.
|
56
|
+
# This means that if you call populate_attributes, the same attributes
|
57
|
+
# that were dirty before the call will be dirty after the call (but no
|
58
|
+
# more and no less). This distinction is important because most subclasses
|
59
|
+
# of AbstractModel only save changed attributes, and ignore unchanged
|
60
|
+
# attributes. Attempting to change attributes through this method will
|
61
|
+
# cause them to not be saved, which is surely unexpected behaviour for
|
62
|
+
# most users.
|
63
|
+
#
|
64
|
+
# Calling this method will also cause the model to assume that it is not
|
65
|
+
# a new record (see {#new_record?}).
|
66
|
+
def populate_attributes(attribs)
|
67
|
+
# No longer a new record
|
68
|
+
@new_record = false
|
69
|
+
|
70
|
+
ignore_dirty do
|
71
|
+
super
|
72
|
+
|
73
|
+
populate_relationships(attribs)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Overwrites {Attributable#write_attribute} to set the dirty state of
|
78
|
+
# the written attribute. See {Dirty#set_dirty!} as well.
|
79
|
+
def write_attribute(name, value)
|
80
|
+
set_dirty!(name, read_attribute(name), value)
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
# Destroys the model. The exact behaviour of this method is expected to be
|
85
|
+
# defined on the subclasses. This method on AbstractModel simply
|
86
|
+
# propagates the destroy to the dependent relationships. For more information
|
87
|
+
# on relationships, see {Relatable}.
|
88
|
+
def destroy(*args)
|
89
|
+
# Destroy dependent relationships
|
90
|
+
self.class.relationships.each do |name, options|
|
91
|
+
destroy_relationship(name, *args) if options[:dependent] == :destroy
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module VirtualBox
|
2
|
+
class AbstractModel
|
3
|
+
# Module which can be included into any other class which allows
|
4
|
+
# that class to have attributes using the "attribute" class method.
|
5
|
+
# This creates the reader/writer for the attribute but also provides
|
6
|
+
# other useful options such as readonly attributes, default values,
|
7
|
+
# and more.
|
8
|
+
#
|
9
|
+
# Make sure to also see the {ClassMethods}.
|
10
|
+
#
|
11
|
+
# ## Defining a Basic Attribute
|
12
|
+
#
|
13
|
+
# attribute :name
|
14
|
+
#
|
15
|
+
# The example above would put the "name" attribute on the class. This
|
16
|
+
# would give the class the following abilities
|
17
|
+
#
|
18
|
+
# instance.name = "Harry!"
|
19
|
+
# puts instance.name # => "Harry!"
|
20
|
+
#
|
21
|
+
# Basic attributes alone are not different than ruby's built-in
|
22
|
+
# `attr_*` methods.
|
23
|
+
#
|
24
|
+
# ## Defining a Readonly Attribute
|
25
|
+
#
|
26
|
+
# attribute :age, :readonly => true
|
27
|
+
#
|
28
|
+
# The example above allows age to be read, but not written to via the
|
29
|
+
# `age=` method. The attribute is still able to written using
|
30
|
+
# {#write_attribute} but this is generally only for
|
31
|
+
# inter-class use, and not for users of it.
|
32
|
+
#
|
33
|
+
# ## Defining Default Values
|
34
|
+
#
|
35
|
+
# attribute :format, :default => "VDI"
|
36
|
+
#
|
37
|
+
# The example above applies a default value to format. So if no value
|
38
|
+
# is ever given to it, `format` would return `VDI`.
|
39
|
+
#
|
40
|
+
# ## Populating Multiple Attributes
|
41
|
+
#
|
42
|
+
# Attributes can be mass populated using {#populate_attributes}. Below
|
43
|
+
# is an example of the use.
|
44
|
+
#
|
45
|
+
# class Person
|
46
|
+
# include Attributable
|
47
|
+
#
|
48
|
+
# attribute :name
|
49
|
+
# attribute :age, :readonly => true
|
50
|
+
#
|
51
|
+
# def initialize
|
52
|
+
# populate_attributes({
|
53
|
+
# :name => "Steven",
|
54
|
+
# :age => 27
|
55
|
+
# })
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# **Note:** Populating attributes is not the same as mass-updating attributes.
|
60
|
+
# {#populate_attributes} is meant to do initial population only. There is
|
61
|
+
# currently no method for mass assignment for updating.
|
62
|
+
#
|
63
|
+
# ## Custom Populate Keys
|
64
|
+
#
|
65
|
+
# Sometimes the attribute names don't match the keys of the hash that will be
|
66
|
+
# used to populate it. For this purpose, you can define a custom
|
67
|
+
# `populate_key`. Example:
|
68
|
+
#
|
69
|
+
# attribute :path, :populate_key => :location
|
70
|
+
#
|
71
|
+
# def initialize
|
72
|
+
# populate_attributes(:location => "Home")
|
73
|
+
# puts path # => "Home"
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
module Attributable
|
77
|
+
def self.included(base)
|
78
|
+
base.extend ClassMethods
|
79
|
+
end
|
80
|
+
|
81
|
+
# Defines the class methods for the {Attributable} module. For
|
82
|
+
# detailed overview documentation, see {Attributable}.
|
83
|
+
module ClassMethods
|
84
|
+
# Defines an attribute on the model.
|
85
|
+
#
|
86
|
+
# @param [Symbol] name The name of the attribute, which will also be
|
87
|
+
# used to set the accessor methods.
|
88
|
+
# @option options [Boolean] :readonly (false) If true, attribute will be readonly.
|
89
|
+
# More specifically, the `attribute=` method won't be defined for it.
|
90
|
+
# @option options [Object] :default (nil) Specifies a default value for the
|
91
|
+
# attribute.
|
92
|
+
# @option options [Symbol] :populate_key (attribute name) Specifies
|
93
|
+
# a custom populate key to use for {Attributable#populate_attributes}
|
94
|
+
def attribute(name, options = {})
|
95
|
+
attributes[name.to_sym] = options
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the hash of attributes and their associated options.
|
99
|
+
def attributes
|
100
|
+
@attributes ||= {}
|
101
|
+
end
|
102
|
+
|
103
|
+
# Used to propagate attributes to subclasses. This method makes sure that
|
104
|
+
# subclasses of a class with {Attributable} included will inherit the
|
105
|
+
# attributes as well, which would be the expected behaviour.
|
106
|
+
def inherited(subclass)
|
107
|
+
super rescue NoMethodError
|
108
|
+
|
109
|
+
attributes.each do |name, option|
|
110
|
+
subclass.attribute(name, option)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Does the initial population of the various attributes. It will
|
116
|
+
# ignore attributes which are not defined or have no value in the
|
117
|
+
# hash.
|
118
|
+
#
|
119
|
+
# Population uses the attributes `populate_key` if present to
|
120
|
+
# determine which value to take. Example:
|
121
|
+
#
|
122
|
+
# attribute :name, :populate_key => :namae
|
123
|
+
# attribute :age
|
124
|
+
#
|
125
|
+
# def initialize
|
126
|
+
# populate_attributes(:namae => "Henry", :age => 27)
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# The above example would set `name` to `Henry` since that is
|
130
|
+
# the `populate_key`. If a `populate_key` is not present, the
|
131
|
+
# attribute name is used.
|
132
|
+
def populate_attributes(attribs)
|
133
|
+
self.class.attributes.each do |key, options|
|
134
|
+
value_key = options[:populate_key] || key
|
135
|
+
write_attribute(key, attribs[value_key])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Writes an attribute. This method ignores the `readonly` option
|
140
|
+
# on attribute definitions. This method is mostly meant for
|
141
|
+
# internal use on setting attributes (including readonly
|
142
|
+
# attributes), whereas users of a class which includes this
|
143
|
+
# module should use the accessor methods, such as `name=`.
|
144
|
+
def write_attribute(name, value)
|
145
|
+
attributes[name] = value
|
146
|
+
end
|
147
|
+
|
148
|
+
# Reads an attribute. This method will return `nil` if the
|
149
|
+
# attribute doesn't exist. If the attribute does exist but
|
150
|
+
# doesn't have a value set, it'll use the `default` value
|
151
|
+
# if specified.
|
152
|
+
def read_attribute(name)
|
153
|
+
if has_attribute?(name)
|
154
|
+
attributes[name] || self.class.attributes[name][:default]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns a hash of all attributes and their options.
|
159
|
+
def attributes
|
160
|
+
@attribute_values ||= {}
|
161
|
+
end
|
162
|
+
|
163
|
+
# Returns boolean value denoting if an attribute exists.
|
164
|
+
def has_attribute?(name)
|
165
|
+
self.class.attributes.has_key?(name.to_sym)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns a boolean value denoting if an attribute is readonly.
|
169
|
+
# This method also returns false for **nonexistent attributes**
|
170
|
+
# so it should be used in conjunction with {#has_attribute?} if
|
171
|
+
# existence is important.
|
172
|
+
def readonly_attribute?(name)
|
173
|
+
name = name.to_sym
|
174
|
+
has_attribute?(name) && self.class.attributes[name][:readonly]
|
175
|
+
end
|
176
|
+
|
177
|
+
# Method missing is used to add dynamic handlers for accessors and
|
178
|
+
# setters. Due to the general ugliness of `method_missing`, this may
|
179
|
+
# change in the near future, but for now it is what it is.
|
180
|
+
def method_missing(meth, *args)
|
181
|
+
meth_string = meth.to_s
|
182
|
+
|
183
|
+
if has_attribute?(meth)
|
184
|
+
read_attribute(meth)
|
185
|
+
elsif meth_string =~ /^(.+?)=$/ && has_attribute?($1) && !readonly_attribute?($1)
|
186
|
+
write_attribute($1.to_sym, *args)
|
187
|
+
else
|
188
|
+
super
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|