virtualbox 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +11 -0
  3. data/Rakefile +32 -0
  4. data/Readme.md +75 -0
  5. data/TODO +13 -0
  6. data/VERSION +1 -0
  7. data/lib/virtualbox.rb +11 -0
  8. data/lib/virtualbox/abstract_model.rb +95 -0
  9. data/lib/virtualbox/abstract_model/attributable.rb +193 -0
  10. data/lib/virtualbox/abstract_model/dirty.rb +164 -0
  11. data/lib/virtualbox/abstract_model/relatable.rb +163 -0
  12. data/lib/virtualbox/attached_device.rb +104 -0
  13. data/lib/virtualbox/command.rb +54 -0
  14. data/lib/virtualbox/dvd.rb +31 -0
  15. data/lib/virtualbox/errors.rb +7 -0
  16. data/lib/virtualbox/ext/subclass_listing.rb +24 -0
  17. data/lib/virtualbox/hard_drive.rb +169 -0
  18. data/lib/virtualbox/image.rb +94 -0
  19. data/lib/virtualbox/nic.rb +150 -0
  20. data/lib/virtualbox/storage_controller.rb +122 -0
  21. data/lib/virtualbox/vm.rb +287 -0
  22. data/test/test_helper.rb +25 -0
  23. data/test/virtualbox/abstract_model/attributable_test.rb +150 -0
  24. data/test/virtualbox/abstract_model/dirty_test.rb +66 -0
  25. data/test/virtualbox/abstract_model/relatable_test.rb +141 -0
  26. data/test/virtualbox/abstract_model_test.rb +146 -0
  27. data/test/virtualbox/attached_device_test.rb +92 -0
  28. data/test/virtualbox/command_test.rb +30 -0
  29. data/test/virtualbox/dvd_test.rb +58 -0
  30. data/test/virtualbox/ext/subclass_listing_test.rb +25 -0
  31. data/test/virtualbox/hard_drive_test.rb +161 -0
  32. data/test/virtualbox/image_test.rb +113 -0
  33. data/test/virtualbox/nic_test.rb +119 -0
  34. data/test/virtualbox/storage_controller_test.rb +79 -0
  35. data/test/virtualbox/vm_test.rb +313 -0
  36. metadata +103 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ bin/*
2
+ vendor/gems/*
3
+ doc/*
4
+ .yardoc/*
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