Class: Vagrant::Machine

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant/machine.rb

Overview

This represents a machine that Vagrant manages. This provides a singular API for querying the state and making state changes to the machine, which is backed by any sort of provider (VirtualBox, VMWare, etc.).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, provider_name, provider_cls, provider_config, config, data_dir, box, env, base = false) ⇒ Machine

Initialize a new machine.

Parameters:

  • name (String)

    Name of the virtual machine.

  • provider (Class)

    The provider backing this machine. This is currently expected to be a V1 ‘provider` plugin.

  • provider_config (Object)

    The provider-specific configuration for this machine.

  • config (Object)

    The configuration for this machine.

  • data_dir (Pathname)

    The directory where machine-specific data can be stored. This directory is ensured to exist.

  • box (Box)

    The box that is backing this virtual machine.

  • env (Environment)

    The environment that this machine is a part of.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/vagrant/machine.rb', line 68

def initialize(name, provider_name, provider_cls, provider_config, config, data_dir, box, env, base=false)
  @logger = Log4r::Logger.new("vagrant::machine")
  @logger.info("Initializing machine: #{name}")
  @logger.info("  - Provider: #{provider_cls}")
  @logger.info("  - Box: #{box}")
  @logger.info("  - Data dir: #{data_dir}")

  @box             = box
  @config          = config
  @data_dir        = data_dir
  @env             = env
  @name            = name
  @provider_config = provider_config
  @provider_name   = provider_name

  # Read the ID, which is usually in local storage
  @id = nil

  # XXX: This is temporary. This will be removed very soon.
  if base
    @id = name
  else
    # Read the id file from the data directory if it exists as the
    # ID for the pre-existing physical representation of this machine.
    id_file = @data_dir.join("id")
    @id = id_file.read if id_file.file?
  end

  # Initializes the provider last so that it has access to all the
  # state we setup on this machine.
  @provider = provider_cls.new(self)
end

Instance Attribute Details

#boxBox (readonly)

The box that is backing this machine.

Returns:



11
12
13
# File 'lib/vagrant/machine.rb', line 11

def box
  @box
end

#configObject (readonly)

Configuration for the machine.

Returns:

  • (Object)


16
17
18
# File 'lib/vagrant/machine.rb', line 16

def config
  @config
end

#data_dirPathname (readonly)

Directory where machine-specific data can be stored.

Returns:

  • (Pathname)


21
22
23
# File 'lib/vagrant/machine.rb', line 21

def data_dir
  @data_dir
end

#envEnvironment (readonly)

The environment that this machine is a part of.

Returns:



26
27
28
# File 'lib/vagrant/machine.rb', line 26

def env
  @env
end

#idString

ID of the machine. This ID comes from the provider and is not guaranteed to be of any particular format except that it is a string.

Returns:

  • (String)


33
34
35
# File 'lib/vagrant/machine.rb', line 33

def id
  @id
end

#nameString (readonly)

Name of the machine. This is assigned by the Vagrantfile.

Returns:

  • (String)


38
39
40
# File 'lib/vagrant/machine.rb', line 38

def name
  @name
end

#providerObject (readonly)

The provider backing this machine.

Returns:

  • (Object)


43
44
45
# File 'lib/vagrant/machine.rb', line 43

def provider
  @provider
end

#provider_configObject (readonly)

The provider-specific configuration for this machine.

Returns:

  • (Object)


48
49
50
# File 'lib/vagrant/machine.rb', line 48

def provider_config
  @provider_config
end

#provider_nameSymbol (readonly)

The name of the provider.

Returns:

  • (Symbol)


53
54
55
# File 'lib/vagrant/machine.rb', line 53

def provider_name
  @provider_name
end

Instance Method Details

#action(name, extra_env = nil) ⇒ Object

This calls an action on the provider. The provider may or may not actually implement the action.

Parameters:

  • name (Symbol)

    Name of the action to run.

  • extra_env (Hash) (defaults to: nil)

    This data will be passed into the action runner as extra data set on the environment hash for the middleware runner.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/vagrant/machine.rb', line 108

def action(name, extra_env=nil)
  @logger.info("Calling action: #{name} on provider #{@provider}")

  # Get the callable from the provider.
  callable = @provider.action(name)

  # If this action doesn't exist on the provider, then an exception
  # must be raised.
  if callable.nil?
    raise Errors::UnimplementedProviderAction,
      :action => name,
      :provider => @provider.to_s
  end

  # Run the action with the action runner on the environment
  env = {
    :action_name    => "machine_action_#{name}".to_sym,
    :machine        => self,
    :machine_action => name,
    :ui             => @env.ui_class.new(@name)
  }.merge(extra_env || {})
  @env.action_runner.run(callable, env)
end

#communicateObject

Returns a communication object for executing commands on the remote machine. Note that the exact semantics of this are up to the communication provider itself. Despite this, the semantics are expected to be consistent across operating systems. For example, all linux-based systems should have similar communication (usually a shell). All Windows systems should have similar communication as well. Therefore, prior to communicating with the machine, users of this method are expected to check the guest OS to determine their behavior.

This method will always return some valid communication object. The ‘ready?` API can be used on the object to check if communication is actually ready.

Returns:

  • (Object)


146
147
148
149
150
151
152
153
154
155
# File 'lib/vagrant/machine.rb', line 146

def communicate
  if !@communicator
    # For now, we always return SSH. In the future, we'll abstract
    # this and allow plugins to define new methods of communication.
    klass = Vagrant.plugin("2").manager.communicators[:ssh]
    @communicator = klass.new(self)
  end

  @communicator
end

#guestObject

Returns a guest implementation for this machine. The guest implementation knows how to do guest-OS specific tasks, such as configuring networks, mounting folders, etc.

Returns:

  • (Object)

Raises:



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/vagrant/machine.rb', line 162

def guest
  raise Errors::MachineGuestNotReady if !communicate.ready?

  # Load the initial guest.
  last_guest = config.vm.guest
  guest      = load_guest(last_guest)

  # Loop and distro dispatch while there are distros.
  while true
    distro = guest.distro_dispatch
    break if !distro

    # This is just some really basic loop detection and avoiding for
    # guest classes. This is just here to help implementers a bit
    # avoid a situation that is fairly easy, since if you subclass
    # a parent which does `distro_dispatch`, you'll end up dispatching
    # forever.
    if distro == last_guest
      @logger.warn("Distro dispatch loop in '#{distro}'. Exiting loop.")
      break
    end

    last_guest = distro
    guest      = load_guest(distro)
  end

  # Return the result
  guest
end

#inspectString

This returns a clean inspect value so that printing the value via a pretty print (‘p`) results in a readable value.

Returns:

  • (String)


228
229
230
# File 'lib/vagrant/machine.rb', line 228

def inspect
  "#<#{self.class}: #{@name} (#{@provider.class})>"
end

#ssh_infoHash

This returns the SSH info for accessing this machine. This SSH info is queried from the underlying provider. This method returns ‘nil` if the machine is not ready for SSH communication.

The structure of the resulting hash is guaranteed to contain the following structure, although it may return other keys as well not documented here:

{
  :host => "1.2.3.4",
  :port => "22",
  :username => "mitchellh",
  :private_key_path => "/path/to/my/key"
}

Note that Vagrant makes no guarantee that this info works or is correct. This is simply the data that the provider gives us or that is configured via a Vagrantfile. It is still possible after this point when attempting to connect via SSH to get authentication errors.

Returns:

  • (Hash)

    SSH information.



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/vagrant/machine.rb', line 254

def ssh_info
  # First, ask the provider for their information. If the provider
  # returns nil, then the machine is simply not ready for SSH, and
  # we return nil as well.
  info = @provider.ssh_info
  return nil if info.nil?

  # Delete out the nil entries.
  info.dup.each do |key, value|
    info.delete(key) if value.nil?
  end

  # Next, we default some fields if they weren't given to us by
  # the provider.
  info[:host] ||= @config.ssh.host if @config.ssh.host
  info[:port] ||= @config.ssh.port if @config.ssh.port
  info[:username] ||= @config.ssh.username if @config.ssh.username

  # We also set some fields that are purely controlled by Varant
  info[:forward_agent] = @config.ssh.forward_agent
  info[:forward_x11]   = @config.ssh.forward_x11

  # Set the private key path. If a specific private key is given in
  # the Vagrantfile we set that. Otherwise, we use the default (insecure)
  # private key, but only if the provider didn't give us one.
  if !info[:private_key_path]
    if @config.ssh.private_key_path
      info[:private_key_path] = @config.ssh.private_key_path
    else
      info[:private_key_path] = @env.default_private_key_path
    end
  end

  # Expand the private key path relative to the root path
  info[:private_key_path] = File.expand_path(info[:private_key_path], @env.root_path)

  # Return the final compiled SSH info data
  info
end

#stateSymbol

Returns the state of this machine. The state is queried from the backing provider, so it can be any arbitrary symbol.

Returns:

  • (Symbol)

Raises:



298
299
300
301
302
# File 'lib/vagrant/machine.rb', line 298

def state
  result = @provider.state
  raise Errors::MachineStateInvalid if !result.is_a?(MachineState)
  result
end