Class: VirtualBox::ForwardedPort

Inherits:
AbstractModel show all
Defined in:
lib/virtualbox/forwarded_port.rb

Overview

When a VM uses NAT as its NIC type, VirtualBox acts like its own private router for all virtual machines. Because of this, the host machine can’t access services within the guest machine. To get around this, NAT supports port forwarding, which allows the guest machine services to be forwarded to some port on the host machine. Port forwarding is done completely through ExtraData, but is a complicated enough procedure that this class was made to faciliate it.

Note: After changing any forwarded ports, the entire VirtualBox process must be restarted completely for them to take effect. When working with the ruby library, this isn’t so much of an issue, but if you have any VMs running, they must all be shut down and restarted.

# Adding a new Forwarded Port

Since forwarded ports rely on being part of a VM, we’re going to assume that ‘vm` points to a VM which has already been found.

port = VirtualBox::ForwardedPort.new
port.name = "apache" # This can be anything
port.guestport = 80
port.hostport = 8080
vm.forwarded_ports << port
port.save # Or vm.save

# Modifying an Existing Forwarded Port

This is assuming that ‘vm` is a local variable storing a VM object which has already been found.

vm.forwarded_ports.first.hostport = 1919
vm.save

# Deleting a Forwarded Port

To delete a forwarded port, you simply destroy it like any other model:

vm.forwarded_ports.first.destroy

# Attributes and Relationships

Properties of the model are exposed using standard ruby instance methods which are generated on the fly. Because of this, they are not listed below as available instance methods.

These attributes can be accessed and modified via standard ruby-style ‘instance.attribute` and `instance.attribute=` methods. The attributes are listed below.

Relationships are also accessed like attributes but can’t be set. Instead, they are typically references to other objects such as an AttachedDevice which in turn have their own attributes which can be modified.

## Attributes

This is copied directly from the class header, but lists all available attributes. If you don’t understand what this means, read AbstractModel::Attributable.

attribute :parent, :readonly => true
attribute :name
attribute :instance, :default => "0"
attribute :device, :default => "pcnet"
attribute :protocol, :default => "TCP"
attribute :guestport
attribute :hostport

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from AbstractModel

#errors, errors_for_relationship, #existing_record!, #inspect, #lazy_attribute?, #lazy_relationship?, #new_record!, #new_record?, #parent_machine, #populate_attributes, #populate_relationship, #populate_relationships, reload!, #reload!, reload?, reloaded!, #save!, #save_attribute, #save_changed_interface_attributes, #save_interface_attribute, #set_relationship, #write_attribute

Methods included from AbstractModel::Validatable

#__validates_extract_options, #add_error, #clear_errors, #errors, #errors_on, #full_error_messages, #valid?, #validates_format_of, #validates_inclusion_of, #validates_numericality_of, #validates_presence_of

Methods included from AbstractModel::Relatable

#destroy_relationship, #destroy_relationships, #has_relationship?, included, #lazy_relationship?, #loaded_relationship?, #populate_relationship, #populate_relationships, #read_relationship, #relationship_class, #relationship_data, #save_relationship, #save_relationships, #set_relationship

Methods included from AbstractModel::VersionMatcher

#assert_version_match, #split_version, #version_match?

Methods included from AbstractModel::Dirty

#changed?, #changes, #clear_dirty!, #ignore_dirty, #method_missing, #set_dirty!

Methods included from AbstractModel::InterfaceAttributes

#load_interface_attribute, #load_interface_attributes, #save_interface_attribute, #save_interface_attributes, #spec_to_proc

Methods included from AbstractModel::Attributable

#attributes, #has_attribute?, included, #lazy_attribute?, #loaded_attribute?, #populate_attributes, #read_attribute, #readonly_attribute?, #write_attribute

Methods included from Logger

included, #logger, #logger_output=

Constructor Details

#initialize(data = {}) ⇒ ForwardedPort

Returns a new instance of ForwardedPort.

Parameters:

  • data (Hash) (defaults to: {})

    The initial attributes to populate.



122
123
124
125
# File 'lib/virtualbox/forwarded_port.rb', line 122

def initialize(data={})
  super()
  populate_attributes(data) if !data.empty?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class VirtualBox::AbstractModel::Dirty

Class Method Details

.populate_relationship(caller, data) ⇒ Array<ForwardedPort>

Populates a relationship with another model.

**This method typically won’t be used except internally.**

Returns:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/virtualbox/forwarded_port.rb', line 85

def populate_relationship(caller, data)
  relation = Proxies::Collection.new(caller)

  caller.extra_data.each do |key, value|
    next unless key =~ /^(VBoxInternal\/Devices\/(.+?)\/(.+?)\/LUN#0\/Config\/(.+?)\/)Protocol$/i

    port = new({
      :parent => caller,
      :parent_collection => relation,
      :name => $4.to_s,
      :instance => $3.to_s,
      :device => $2.to_s,
      :protocol => value,
      :guestport => caller.extra_data["#{$1.to_s}GuestPort"],
      :hostport => caller.extra_data["#{$1.to_s}HostPort"]
    })

    port.existing_record!

    relation.push(port)
  end

  relation
end

.save_relationship(caller, data) ⇒ Object

Saves the relationship. This simply calls #save on every member of the relationship.

**This method typically won’t be used except internally.**



114
115
116
117
118
# File 'lib/virtualbox/forwarded_port.rb', line 114

def save_relationship(caller, data)
  data.dup.each do |fp|
    fp.save
  end
end

Instance Method Details

#added_to_relationship(proxy) ⇒ Object

Relationship callback when added to a collection. This is automatically called by any relationship collection when this object is added.



206
207
208
209
# File 'lib/virtualbox/forwarded_port.rb', line 206

def added_to_relationship(proxy)
  write_attribute(:parent, proxy.parent)
  write_attribute(:parent_collection, proxy)
end

#destroyBoolean

Destroys the port forwarding mapping.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/virtualbox/forwarded_port.rb', line 187

def destroy
  results = []

  if !new_record?
    results << parent.extra_data.delete("#{key_prefix(true)}Protocol")
    results << parent.extra_data.delete("#{key_prefix(true)}GuestPort")
    results << parent.extra_data.delete("#{key_prefix(true)}HostPort")

    # Remove it from any collection
    parent_collection.delete(self, true) if parent_collection

    new_record!
  end

  results.empty? || results.all? { |o| o == true }
end

#deviceString

Retrieves the device for the forwarded port. This tries to “do the right thing” depending on the first NIC of the VM parent by either setting the forwarded port type to “pcnet” or “e1000.” If the device was already set manually, this method will simply return that value instead.

Returns:

  • (String)

    Device type for the forwarded port



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/virtualbox/forwarded_port.rb', line 144

def device
  # Return the current or default value if it is:
  #  * an existing record, since it was already mucked with, no need to
  #    modify it again
  #  * device setting changed, since we should return what the user set
  #    it to
  #  * If the parent is nil, since we can't infer the type without a parent
  return read_attribute(:device) if !new_record? || device_changed? || parent.nil?

  device_map = {
    :Am79C970A => "pcnet",
    :Am79C973 => "pcnet",
    :I82540EM => "e1000",
    :I82543GC => "e1000",
    :I82545EM => "e1000"
  }

  return device_map[parent.network_adapters[0].adapter_type]
end

#key_prefix(old_name = false) ⇒ String

Returns the prefix to be used for the extra data key. Forwarded ports are created by simply setting ExtraData on a VM. This class hides most of the inner workings of it, but it requires a common prefix. This method generates that.

Returns:

  • (String)


217
218
219
220
# File 'lib/virtualbox/forwarded_port.rb', line 217

def key_prefix(old_name=false)
  name_value = old_name && name_changed? ? name_was : name
  "VBoxInternal\/Devices\/#{device}\/#{instance}\/LUN#0\/Config\/#{name_value}\/"
end

#saveBoolean

Saves the forwarded port.

Returns:

  • (Boolean)

    True if command was successful, false otherwise.

Raises:



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/virtualbox/forwarded_port.rb', line 167

def save
  return true if !new_record? && !changed?
  raise Exceptions::ValidationFailedException.new(errors) if !valid?

  destroy if !new_record? && name_changed?

  parent.extra_data["#{key_prefix}Protocol"] = protocol
  parent.extra_data["#{key_prefix}GuestPort"] = guestport
  parent.extra_data["#{key_prefix}HostPort"] = hostport
  result = parent.extra_data.save

  clear_dirty!
  existing_record!

  result
end

#validateObject

Validates a forwarded port.



128
129
130
131
132
133
134
135
# File 'lib/virtualbox/forwarded_port.rb', line 128

def validate
  super

  validates_presence_of :parent
  validates_presence_of :name
  validates_presence_of :guestport
  validates_presence_of :hostport
end