virtualbox 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Readme.md +9 -9
  2. data/VERSION +1 -1
  3. data/docs/GettingStarted.md +11 -11
  4. data/docs/WhatsNew.md +1 -1
  5. data/lib/virtualbox.rb +10 -1
  6. data/lib/virtualbox/abstract_model.rb +47 -29
  7. data/lib/virtualbox/abstract_model/attributable.rb +16 -16
  8. data/lib/virtualbox/abstract_model/dirty.rb +10 -10
  9. data/lib/virtualbox/abstract_model/relatable.rb +22 -22
  10. data/lib/virtualbox/abstract_model/validatable.rb +4 -4
  11. data/lib/virtualbox/attached_device.rb +23 -23
  12. data/lib/virtualbox/command.rb +9 -9
  13. data/lib/virtualbox/dvd.rb +7 -7
  14. data/lib/virtualbox/ext/subclass_listing.rb +1 -1
  15. data/lib/virtualbox/extra_data.rb +17 -17
  16. data/lib/virtualbox/forwarded_port.rb +23 -23
  17. data/lib/virtualbox/hard_drive.rb +27 -27
  18. data/lib/virtualbox/image.rb +25 -18
  19. data/lib/virtualbox/nic.rb +22 -22
  20. data/lib/virtualbox/proxies/collection.rb +4 -4
  21. data/lib/virtualbox/shared_folder.rb +25 -25
  22. data/lib/virtualbox/storage_controller.rb +16 -16
  23. data/lib/virtualbox/vm.rb +95 -42
  24. data/test/virtualbox/abstract_model/attributable_test.rb +28 -28
  25. data/test/virtualbox/abstract_model/dirty_test.rb +13 -13
  26. data/test/virtualbox/abstract_model/relatable_test.rb +36 -36
  27. data/test/virtualbox/abstract_model/validatable_test.rb +22 -22
  28. data/test/virtualbox/abstract_model_test.rb +46 -36
  29. data/test/virtualbox/attached_device_test.rb +47 -47
  30. data/test/virtualbox/command_test.rb +12 -12
  31. data/test/virtualbox/dvd_test.rb +15 -19
  32. data/test/virtualbox/ext/subclass_listing_test.rb +2 -2
  33. data/test/virtualbox/extra_data_test.rb +37 -37
  34. data/test/virtualbox/forwarded_port_test.rb +31 -31
  35. data/test/virtualbox/hard_drive_test.rb +40 -48
  36. data/test/virtualbox/image_test.rb +36 -33
  37. data/test/virtualbox/nic_test.rb +22 -22
  38. data/test/virtualbox/proxies/collection_test.rb +6 -6
  39. data/test/virtualbox/shared_folder_test.rb +36 -36
  40. data/test/virtualbox/storage_controller_test.rb +14 -14
  41. data/test/virtualbox/vm_test.rb +121 -70
  42. data/test/virtualbox_test.rb +19 -0
  43. data/virtualbox.gemspec +5 -3
  44. metadata +4 -2
@@ -16,11 +16,11 @@ module VirtualBox
16
16
  # @abstract
17
17
  class Image < AbstractModel
18
18
  include SubclassListing
19
-
19
+
20
20
  attribute :uuid, :readonly => true
21
21
  attribute :location
22
22
  attribute :accessible, :readonly => true
23
-
23
+
24
24
  class <<self
25
25
  # Parses the raw output of virtualbox into image objects. Used by
26
26
  # subclasses to parse the output of their respective listing functions.
@@ -31,7 +31,7 @@ module VirtualBox
31
31
  def parse_raw(raw)
32
32
  parse_blocks(raw).collect { |v| new(v) }
33
33
  end
34
-
34
+
35
35
  # Parses the blocks of the output from virtualbox. VirtualBox outputs
36
36
  # image listing in "blocks" which are then parsed down to their attributes.
37
37
  #
@@ -41,8 +41,8 @@ module VirtualBox
41
41
  def parse_blocks(raw)
42
42
  raw.split(/\n\n/).collect { |v| parse_block(v.chomp) }.compact
43
43
  end
44
-
45
- # Parses a single block from VirtualBox output.
44
+
45
+ # Parses a single block from VirtualBox output.
46
46
  #
47
47
  # **This method typically won't be used except internally.**
48
48
  #
@@ -57,14 +57,14 @@ module VirtualBox
57
57
  next unless line =~ /^(.+?):\s+(.+?)$/
58
58
  hd[$1.downcase.to_sym] = $2.to_s
59
59
  end
60
-
60
+
61
61
  # If we don't have a location but have a path, use that, as they
62
62
  # are equivalent but not consistent.
63
63
  hd[:location] = hd[:path] if hd.has_key?(:path)
64
-
64
+
65
65
  hd
66
66
  end
67
-
67
+
68
68
  # Searches the subclasses which implement all method, searching for
69
69
  # a matching UUID and returning that as the relationship.
70
70
  #
@@ -74,17 +74,17 @@ module VirtualBox
74
74
  def populate_relationship(caller, data)
75
75
  return DVD.empty_drive if data[:medium] == "emptydrive"
76
76
  return nil if data[:uuid].nil?
77
-
77
+
78
78
  subclasses.each do |subclass|
79
79
  next unless subclass.respond_to?(:all)
80
-
80
+
81
81
  matching = subclass.all.find { |obj| obj.uuid == data[:uuid] }
82
82
  return matching unless matching.nil?
83
83
  end
84
-
84
+
85
85
  nil
86
86
  end
87
-
87
+
88
88
  # Sets an image onto a relationship and/or removes it from a
89
89
  # relationship. This method is automatically called by {Relatable}.
90
90
  #
@@ -95,29 +95,29 @@ module VirtualBox
95
95
  # We don't actually destroy any images using this method,
96
96
  # so just return the new value as long as its a valid object
97
97
  raise Exceptions::InvalidRelationshipObjectException.new if new_value && !new_value.is_a?(Image)
98
-
98
+
99
99
  return new_value
100
100
  end
101
101
  end
102
-
102
+
103
103
  # **This should never be called directly on {Image}.** Instead, initialize
104
104
  # one of the subclasses.
105
105
  def initialize(info=nil)
106
106
  super()
107
-
107
+
108
108
  populate_attributes(info) if info
109
109
  end
110
-
110
+
111
111
  # The image type as a string for the virtualbox command line. This
112
112
  # method should be overridden by any subclass and is expected to
113
- # return the type which is used in command line parameters for
113
+ # return the type which is used in command line parameters for
114
114
  # attaching to storage controllers.
115
115
  #
116
116
  # @return [String]
117
117
  def image_type
118
118
  raise "This must be implemented by any subclasses"
119
119
  end
120
-
120
+
121
121
  # Returns boolean showing if empty drive or not. This method should be
122
122
  # overriden by any subclass and is expected to return true of false
123
123
  # showing if this image represents an empty drive of whatever type
@@ -127,5 +127,12 @@ module VirtualBox
127
127
  def empty_drive?
128
128
  false
129
129
  end
130
+
131
+ # Returns the basename of the images location (the file name +extension)
132
+ #
133
+ # @return [String]
134
+ def filename
135
+ File.basename(location.to_s)
136
+ end
130
137
  end
131
138
  end
@@ -18,7 +18,7 @@ module VirtualBox
18
18
  #
19
19
  # Properties of the model are exposed using standard ruby instance
20
20
  # methods which are generated on the fly. Because of this, they are not listed
21
- # below as available instance methods.
21
+ # below as available instance methods.
22
22
  #
23
23
  # These attributes can be accessed and modified via standard ruby-style
24
24
  # `instance.attribute` and `instance.attribute=` methods. The attributes are
@@ -39,7 +39,7 @@ module VirtualBox
39
39
  attribute :macaddress
40
40
  attribute :cableconnected
41
41
  attribute :bridgeadapter
42
-
42
+
43
43
  class <<self
44
44
  # Retrives the Nic data from human-readable vminfo. Since some data about
45
45
  # nics is not exposed in the machine-readable virtual machine info, some
@@ -51,16 +51,16 @@ module VirtualBox
51
51
  # @return [Hash]
52
52
  def nic_data(vmname)
53
53
  raw = VM.human_info(vmname)
54
-
54
+
55
55
  # Complicated chain of methods just maps parse_nic over each line,
56
56
  # removing invalid ones, and then converting it into a single hash.
57
57
  raw.split("\n").collect { |v| parse_nic(v) }.compact.inject({}) do |acc, obj|
58
58
  acc.merge({ obj[0] => obj[1] })
59
59
  end
60
60
  end
61
-
61
+
62
62
  # Parses nic data out of a single line of the human readable output
63
- # of vm info.
63
+ # of vm info.
64
64
  #
65
65
  # **This method typically won't be used except internally.**
66
66
  #
@@ -68,18 +68,18 @@ module VirtualBox
68
68
  def parse_nic(raw)
69
69
  return unless raw =~ /^NIC\s(\d):\s+(.+?)$/
70
70
  return if $2.to_s.strip == "disabled"
71
-
71
+
72
72
  data = {}
73
73
  nicname = "nic#{$1}"
74
74
  $2.to_s.split(/,\s+/).each do |raw_property|
75
75
  next unless raw_property =~ /^(.+?):\s+(.+?)$/
76
-
76
+
77
77
  data[$1.downcase.to_sym] = $2.to_s
78
78
  end
79
-
79
+
80
80
  return nicname.to_sym, data
81
81
  end
82
-
82
+
83
83
  # Populates the nic relationship for anything which is related to it.
84
84
  #
85
85
  # **This method typically won't be used except internally.**
@@ -87,25 +87,25 @@ module VirtualBox
87
87
  # @return [Array<Nic>]
88
88
  def populate_relationship(caller, data)
89
89
  nic_data = nic_data(caller.name)
90
-
90
+
91
91
  relation = []
92
-
92
+
93
93
  counter = 1
94
94
  loop do
95
95
  break unless data["nic#{counter}".to_sym]
96
-
96
+
97
97
  nictype = nic_data["nic#{counter}".to_sym][:type] rescue nil
98
-
98
+
99
99
  nic = new(counter, caller, data.merge({
100
100
  "nictype#{counter}".to_sym => nictype
101
101
  }))
102
102
  relation.push(nic)
103
103
  counter += 1
104
104
  end
105
-
105
+
106
106
  relation
107
107
  end
108
-
108
+
109
109
  # Saves the relationship. This simply calls {#save} on every
110
110
  # member of the relationship.
111
111
  #
@@ -117,32 +117,32 @@ module VirtualBox
117
117
  end
118
118
  end
119
119
  end
120
-
121
- # Since there is currently no way to create a _new_ nic, this is
120
+
121
+ # Since there is currently no way to create a _new_ nic, this is
122
122
  # only used internally. Developers should NOT try to initialize their
123
123
  # own nic objects.
124
124
  def initialize(index, caller, data)
125
125
  super()
126
-
126
+
127
127
  @index = index
128
-
128
+
129
129
  # Setup the index specific attributes
130
130
  populate_data = {}
131
131
  self.class.attributes.each do |name, options|
132
132
  value = data["#{name}#{index}".to_sym]
133
133
  populate_data[name] = value
134
134
  end
135
-
135
+
136
136
  populate_attributes(populate_data.merge({
137
137
  :parent => caller
138
138
  }))
139
139
  end
140
-
140
+
141
141
  # Saves a single attribute of the nic. This method is automatically
142
142
  # called on {#save}.
143
143
  #
144
144
  # **This method typically won't be used except internally.**
145
- def save_attribute(key, value, vmname)
145
+ def save_attribute(key, value, vmname)
146
146
  Command.vboxmanage("modifyvm #{Command.shell_escape(vmname)} --#{key}#{@index} #{Command.shell_escape(value)}")
147
147
  super
148
148
  end
@@ -5,21 +5,21 @@ module VirtualBox
5
5
  class Collection < Array
6
6
  def initialize(parent)
7
7
  super()
8
-
8
+
9
9
  @parent = parent
10
10
  end
11
-
11
+
12
12
  def <<(item)
13
13
  item.added_to_relationship(@parent) if item.respond_to?(:added_to_relationship)
14
14
  push(item)
15
15
  end
16
-
16
+
17
17
  def clear
18
18
  each do |item|
19
19
  delete(item)
20
20
  end
21
21
  end
22
-
22
+
23
23
  def delete(item)
24
24
  return unless super
25
25
  item.removed_from_relationship(@parent) if item.respond_to?(:removed_from_relationship)
@@ -2,7 +2,7 @@ module VirtualBox
2
2
  # Represents a shared folder in VirtualBox. In VirtualBox, shared folders are a
3
3
  # method for basically "symlinking" a folder on the guest system to a folder which
4
4
  # exists on the host system. This allows for sharing of files across the virtual
5
- # machine.
5
+ # machine.
6
6
  #
7
7
  # **Note:** Whenever modifying shared folders on a VM, the changes won't take
8
8
  # effect until a _cold reboot_ occurs. This means actually closing the virtual
@@ -36,7 +36,7 @@ module VirtualBox
36
36
  # a local variable named `vm`.**
37
37
  #
38
38
  # Nothing tricky here: You treat existing shared folder objects just as if they
39
- # were new ones. Assign a new name and/or a new path, then save.
39
+ # were new ones. Assign a new name and/or a new path, then save.
40
40
  #
41
41
  # folder = vm.shared_folders.first
42
42
  # folder.name = "rufus"
@@ -65,7 +65,7 @@ module VirtualBox
65
65
  #
66
66
  # Properties of the model are exposed using standard ruby instance
67
67
  # methods which are generated on the fly. Because of this, they are not listed
68
- # below as available instance methods.
68
+ # below as available instance methods.
69
69
  #
70
70
  # These attributes can be accessed and modified via standard ruby-style
71
71
  # `instance.attribute` and `instance.attribute=` methods. The attributes are
@@ -88,7 +88,7 @@ module VirtualBox
88
88
  attribute :parent, :readonly => :readonly
89
89
  attribute :name, :populate_key => "SharedFolderNameMachineMapping"
90
90
  attribute :hostpath, :populate_key => "SharedFolderPathMachineMapping"
91
-
91
+
92
92
  class <<self
93
93
  # Populates the shared folder relationship for anything which is related to it.
94
94
  #
@@ -97,19 +97,19 @@ module VirtualBox
97
97
  # @return [Array<SharedFolder>]
98
98
  def populate_relationship(caller, data)
99
99
  relation = Proxies::Collection.new(caller)
100
-
100
+
101
101
  counter = 1
102
102
  loop do
103
103
  break unless data["SharedFolderNameMachineMapping#{counter}".downcase.to_sym]
104
-
104
+
105
105
  folder = new(counter, caller, data)
106
106
  relation.push(folder)
107
107
  counter += 1
108
108
  end
109
-
109
+
110
110
  relation
111
111
  end
112
-
112
+
113
113
  # Saves the relationship. This simply calls {#save} on every
114
114
  # member of the relationship.
115
115
  #
@@ -121,7 +121,7 @@ module VirtualBox
121
121
  end
122
122
  end
123
123
  end
124
-
124
+
125
125
  # @overload initialize(data={})
126
126
  # Creates a new SharedFolder which is a new record. This
127
127
  # should be attached to a VM and saved.
@@ -136,7 +136,7 @@ module VirtualBox
136
136
  # to extract the relationship data.
137
137
  def initialize(*args)
138
138
  super()
139
-
139
+
140
140
  if args.length == 3
141
141
  initialize_for_relationship(*args)
142
142
  elsif args.length == 1
@@ -147,7 +147,7 @@ module VirtualBox
147
147
  raise NoMethodError.new
148
148
  end
149
149
  end
150
-
150
+
151
151
  # Initializes the record for use in a relationship. This
152
152
  # is automatically called by {#initialize} if it has three
153
153
  # parameters.
@@ -161,12 +161,12 @@ module VirtualBox
161
161
  value = data["#{key}#{index}".downcase.to_sym]
162
162
  populate_data[key] = value
163
163
  end
164
-
164
+
165
165
  populate_attributes(populate_data.merge({
166
166
  :parent => caller
167
167
  }))
168
168
  end
169
-
169
+
170
170
  # Initializes a record with initial data but keeping it a "new
171
171
  # record." This is called automatically if {#initialize} is given
172
172
  # only a single parameter. View {#initialize} for documentation.
@@ -174,52 +174,52 @@ module VirtualBox
174
174
  self.class.attributes.each do |name, options|
175
175
  data[options[:populate_key]] = data[name]
176
176
  end
177
-
177
+
178
178
  populate_attributes(data)
179
179
  new_record!
180
180
  end
181
-
181
+
182
182
  # Validates a shared folder.
183
183
  def validate
184
184
  super
185
-
185
+
186
186
  validates_presence_of :parent
187
187
  validates_presence_of :name
188
188
  validates_presence_of :hostpath
189
189
  end
190
-
190
+
191
191
  # Saves or creates a shared folder.
192
192
  #
193
193
  # @param [Boolean] raise_errors If true, {Exceptions::CommandFailedException}
194
194
  # will be raised if the command failed.
195
195
  # @return [Boolean] True if command was successful, false otherwise.
196
- def save(raise_errors=false)
196
+ def save(raise_errors=false)
197
197
  return true unless changed?
198
-
198
+
199
199
  if !valid?
200
200
  raise Exceptions::ValidationFailedException.new(errors) if raise_errors
201
201
  return false
202
202
  end
203
-
203
+
204
204
  # If this isn't a new record, we destroy it first
205
205
  destroy(raise_errors) if !new_record?
206
-
206
+
207
207
  Command.vboxmanage("sharedfolder add #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name)} --hostpath #{Command.shell_escape(hostpath)}")
208
208
  existing_record!
209
209
  clear_dirty!
210
-
210
+
211
211
  true
212
212
  rescue Exceptions::CommandFailedException
213
213
  raise if raise_errors
214
214
  false
215
215
  end
216
-
216
+
217
217
  # Relationship callback when added to a collection. This is automatically
218
218
  # called by any relationship collection when this object is added.
219
219
  def added_to_relationship(parent)
220
220
  write_attribute(:parent, parent)
221
221
  end
222
-
222
+
223
223
  # Destroys the shared folder. This doesn't actually delete the folder
224
224
  # from the host system. Instead, it simply removes the mapping to the
225
225
  # virtual machine, meaning it will no longer be possible to mount it
@@ -232,7 +232,7 @@ module VirtualBox
232
232
  # If the name changed, we have to be sure to use the previous
233
233
  # one.
234
234
  name_value = name_changed? ? name_was : name
235
-
235
+
236
236
  Command.vboxmanage("sharedfolder remove #{Command.shell_escape(parent.name)} --name #{Command.shell_escape(name_value)}")
237
237
  true
238
238
  rescue Exceptions::CommandFailedException
@@ -1,7 +1,7 @@
1
1
  module VirtualBox
2
2
  # Represents a single storage controller which can be attached to a
3
3
  # virtual machine.
4
- #
4
+ #
5
5
  # **Currently, storage controllers can not be created from scratch.
6
6
  # Therefore, the only way to use this model is through a relationship
7
7
  # of a {VM} object.**
@@ -10,7 +10,7 @@ module VirtualBox
10
10
  #
11
11
  # Properties of the storage controller are exposed using standard ruby instance
12
12
  # methods which are generated on the fly. Because of this, they are not listed
13
- # below as available instance methods.
13
+ # below as available instance methods.
14
14
  #
15
15
  # These attributes can be accessed and modified via standard ruby-style
16
16
  # `instance.attribute` and `instance.attribute=` methods. The attributes are
@@ -46,7 +46,7 @@ module VirtualBox
46
46
  attribute :max_ports, :populate_key => :maxportcount
47
47
  attribute :ports, :populate_key => :portcount
48
48
  relationship :devices, AttachedDevice, :dependent => :destroy
49
-
49
+
50
50
  class <<self
51
51
  # Populates a relationship with another model.
52
52
  #
@@ -55,7 +55,7 @@ module VirtualBox
55
55
  # @return [Array<StorageController>]
56
56
  def populate_relationship(caller, data)
57
57
  relation = []
58
-
58
+
59
59
  counter = 0
60
60
  loop do
61
61
  break unless data["storagecontrollername#{counter}".to_sym]
@@ -63,17 +63,17 @@ module VirtualBox
63
63
  relation.push(nic)
64
64
  counter += 1
65
65
  end
66
-
66
+
67
67
  relation
68
68
  end
69
-
69
+
70
70
  # Destroys a relationship with another model.
71
71
  #
72
72
  # **This method typically won't be used except internally.**
73
73
  def destroy_relationship(caller, data, *args)
74
74
  data.each { |v| v.destroy(*args) }
75
75
  end
76
-
76
+
77
77
  # Saves the relationship. This simply calls {#save} on every
78
78
  # member of the relationship.
79
79
  #
@@ -84,16 +84,16 @@ module VirtualBox
84
84
  end
85
85
  end
86
86
  end
87
-
87
+
88
88
  # Since storage controllers still can't be created from scratch,
89
89
  # this method shouldn't be called. Instead, storage controllers
90
90
  # can be retrieved through relationships of other models such
91
91
  # as {VM}.
92
92
  def initialize(index, caller, data)
93
93
  super()
94
-
94
+
95
95
  @index = index
96
-
96
+
97
97
  # Setup the index specific attributes
98
98
  populate_data = {}
99
99
  self.class.attributes.each do |name, options|
@@ -101,16 +101,16 @@ module VirtualBox
101
101
  value = data["storagecontroller#{key}#{index}".to_sym]
102
102
  populate_data[key] = value
103
103
  end
104
-
104
+
105
105
  # Make sure to merge in device data so those relationships will be
106
106
  # setup properly
107
107
  populate_data.merge!(extract_devices(index, data))
108
-
108
+
109
109
  populate_attributes(populate_data.merge({
110
110
  :parent => caller
111
111
  }))
112
112
  end
113
-
113
+
114
114
  # Extracts related devices for a storage controller.
115
115
  #
116
116
  # **This method typically won't be used except internally.**
@@ -118,14 +118,14 @@ module VirtualBox
118
118
  # @return [Hash]
119
119
  def extract_devices(index, data)
120
120
  name = data["storagecontrollername#{index}".downcase.to_sym].downcase
121
-
121
+
122
122
  device_data = {}
123
123
  data.each do |k,v|
124
124
  next unless k.to_s =~ /^#{name}-/
125
-
125
+
126
126
  device_data[k] = v
127
127
  end
128
-
128
+
129
129
  device_data
130
130
  end
131
131
  end