» Plugin Development: Providers

This page documents how to add support for new providersto Vagrant, allowing Vagrant to run and manage machines powered by asystem other than VirtualBox. Prior to reading this, you should be familiarwith the plugin development basics.

Prior to developing a provider you should also be familiar with howproviders work froma user standpoint.

Warning: Advanced Topic! Developing plugins is anadvanced topic that only experienced Vagrant users who are reasonablycomfortable with Ruby should approach.

» Example Provider: AWS

The best way to learn how to write a provider is to see how one iswritten in practice. To augment this documentation, please heavilystudy the vagrant-aws plugin,which implements an AWS provider. The plugin is a good example of how tostructure, test, and implement your plugin.

» Definition Component

Within the context of a plugin definition, new providers are definedlike so:

  1. provider "my_cloud" do
  2. require_relative "provider"
  3. Provider
  4. end

Providers are defined with the provider method, which takes a singleargument specifying the name of the provider. This is the name that isused with vagrant up to specify the provider. So in the case above,our provider would be used by calling vagrant up —provider=my_cloud.

The block argument then lazily loads and returns a class thatimplements the Vagrant.plugin(2, :provider) interface, which is coverednext.

» Provider Class

The provider class should subclass and implementVagrant.plugin(2, :provider) which is an upgrade-safe way to let Vagrantreturn the proper parent class.

This class and the methods that need to be implemented arevery well documented. The documentation done on the class in the comments should beenough to understand what needs to be done.

Viewing the AWS provider class as well as theoverall structure of the plugin is recommended as a strong getting started point.

Instead of going in depth over each method that needs to be implemented,the documentation will cover high-level but important points to help youcreate your provider.

» Box Format

Each provider is responsible for having its own box format. This isactually an extremely simple step due to how generic boxes are. Beforeexplaining you should get familiar with the generalbox file format.

The only requirement for your box format is that the metadata.jsonfile have a provider key which matches the name of your provider youchose above.

In addition to this, you may put any data in the metadata as wellas any files in the archive. Since Vagrant core itself does not care,it is up to your provider to handle the data of the box. Vagrant corejust handles unpacking and verifying the box is for the properprovider.

As an example of a couple box formats that are actually in use:

  • The virtualbox box format is just a flat directory of the contentsof a VBoxManage export command.

  • The vmware_fusion box format is just a flat directory of thecontents of a vmwarevm folder, but only including the bare essentialfiles for VMware to function.

  • The aws box format is just a Vagrantfile defaulting some configuration.You can see an example aws box unpacked here.

Before anything with your provider is even written, you can verifyyour box format works by doing vagrant box add with it. When you doa vagrant box list you can see what boxes for what providers are installed.

You do not need the provider plugin installed to add a box for thatprovider.

» Actions

Probably the most important concept to understand when building aprovider is the provider "action" interface. It is the secret sauce thatmakes providers do the magic they do.

Actions are built on top of the concept ofmiddleware, whichallow providers to execute multiple distinct steps, have error recoverymechanics, as well as before/after behaviors, and much more.

Vagrant core requests specific actions from your provider through theaction method on your provider class. The full list of actions requestedis listed in the comments of that method on the superclass. If yourprovider does not implement a certain action, then Vagrant core will showa friendly error, so do not worry if you miss any, things will not explodeor crash spectacularly.

Take a look at how the VirtualBox provideruses actions to build up complicated multi-step processes. The AWS provider uses a similar process.

» Built-in Middleware

To assist with common tasks, Vagrant ships with a set ofbuilt-in middleware. Each of the middleware is well commented on the behavior and optionsfor each, and using these built-in middleware is critical to buildinga well-behaved provider.

These built-in middleware can be thought of as a standard library foryour actions on your provider. The core VirtualBox provider uses thesebuilt-in middleware heavily.

» Persisting State

In the process of creating and managing a machine, providers generally needto store some sort of state somewhere. Vagrant provides each machine witha directory to store this state.

As a use-case example for this, the VirtualBox provider stores the UUIDof the VirtualBox virtual machine created. This allows the provider to trackwhether the machine is created, running, suspended, etc.

The VMware provider actually copies the entire virtual machine into thisstate directory, complete with virtual disk drives and everything.

The directory is available from the data_dir attribute of the Machineinstance given to initialize your provider. Within middleware actions, themachine is always available via the :machine key on the environment. Thedata_dir attribute is a Ruby Pathname object.

It is important for providers to carefully manage all the contents ofthis directory. Vagrant core itself does little to clean up this directory.Therefore, when a machine is destroyed, be sure to clean up all the statefrom this directory.

» Configuration

Vagrant supports provider-specific configuration,allowing for users to finely tune and control specific providers fromVagrantfiles. It is easy for your custom provider to expose custom configurationas well.

Provider-specific configuration is a special case of a normalconfiguration plugin. When defining theconfiguration component, name the configuration the same as the provider,and as a second parameter, specify :provider, like so:

  1. config("my_cloud", :provider) do
  2. require_relative "config"
  3. Config
  4. end

As long as the name matches your provider, and the second :providerparameter is given, Vagrant will automatically expose this as provider-specificconfiguration for your provider. Users can now do the following in theirVagrantfiles:

  1. config.vm.provider :my_cloud do |config|
  2. # Your specific configuration!
  3. end

The configuration class returned from the config component in the pluginis the same as any other configuration plugin,so read that page for more information. Vagrant automatically handlesconfiguration validation and such just like any other configuration piece.

The provider-specific configuration is available on the machine objectvia the provider_config attribute. So within actions or your provider class,you can access the config via machine.provider_config.

Best practice: Your provider should _not require_provider-specific configuration to function, if possible. Vagrantpractices a strong convention over configurationphilosophy. When a user installs your provider, they should ideallybe able to vagrant up —provider=your_provider andhave it just work.

» Parallelization

Vagrant supports parallelizing some actions, such as vagrant up, if theprovider explicitly supports it. By default, Vagrant will not parallelize aprovider.

When parallelization is enabled, multiple actions may be runin parallel. Therefore, providers must be certain that their action stacksare thread-safe. The core of Vagrant itself (such as box collections, SSH,etc.) is thread-safe.

Providers can explicitly enable parallelization by setting the paralleloption on the provider component:

  1. provider("my_cloud", parallel: true) do
  2. require_relative "provider"
  3. Provider
  4. end

That is the only change that is needed to enable parallelization.