Skip to content

Commit

Permalink
Make vagrant play nice with OpenStack.
Browse files Browse the repository at this point in the history
Right now, this is only tested against the stable/folsom branch of OpenStack. Unfortunately, the latest version of fog has trouble authenticating against stable/folsom, so we use fog-1.6.0. That means that a few small things needed to be backported.

We also catch and ignore "Network unreachable" errors after creating a VM, since this error seems to appear consistently and just for a short time.
  • Loading branch information
Thomas Kadauke committed Mar 15, 2013
1 parent 16d8ad4 commit 6116f4a
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 90 deletions.
1 change: 1 addition & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (c) 2013 Mitchell Hashimoto
Copyright (c) 2013 cloudbau GmbH

MIT License

Expand Down
62 changes: 33 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
# Vagrant RackSpace Cloud Provider
# Vagrant OpenStack Cloud Provider

This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that adds a
[RackSpace Cloud](http://www.rackspace.com/cloud) provider to Vagrant,
allowing Vagrant to control and provision machines within RackSpace
[OpenStack Cloud](http://www.openstack.org) provider to Vagrant,
allowing Vagrant to control and provision machines within an OpenStack
cloud.

This plugin started as a fork of the Vagrant RackSpace provider.

**Note:** This plugin requires Vagrant 1.1+.

## Features

* Boot Rackspace Cloud instances.
* Boot OpenStack Cloud instances.
* SSH into the instances.
* Provision the instances with any built-in Vagrant provisioner.
* Minimal synced folder support via `rsync`.

## Usage

Install using standard Vagrant 1.1+ plugin installation methods. After
installing, `vagrant up` and specify the `rackspace` provider. An example is
installing, `vagrant up` and specify the `openstack` provider. An example is
shown below.

```
$ vagrant plugin install vagrant-rackspace
$ vagrant plugin install vagrant-openstack
...
$ vagrant up --provider=rackspace
$ vagrant up --provider=openstack
...
```

Of course prior to doing this, you'll need to obtain an Rackspace-compatible
Of course prior to doing this, you'll need to obtain an OpenStack-compatible
box file for Vagrant.

## Quick Start

After installing the plugin (instructions above), the quickest way to get
started is to actually use a dummy Rackspace box and specify all the details
started is to actually use a dummy OpenStack box and specify all the details
manually within a `config.vm.provider` block. So first, add the dummy
box using any name you want:

```
$ vagrant box add dummy https://github.com/mitchellh/vagrant-rackspace/raw/master/dummy.box
$ vagrant box add dummy https://github.com/cloudbau/vagrant-openstack/raw/master/dummy.box
...
```

Expand All @@ -49,19 +51,22 @@ your information where necessary.
Vagrant.configure("2") do |config|
config.vm.box = "dummy"
config.vm.provider :rackspace do |rs|
config.vm.provider :openstack do |rs|
rs.username = "YOUR USERNAME"
rs.api_key = "YOUR API KEY"
rs.flavor = /512MB/
rs.flavor = /m1.tiny/
rs.image = /Ubuntu/
rs.endpoint = "KEYSTONE AUTH URL"
rs.keypair_name = "YOUR KEYPAIR NAME"
rs.ssh_username = "SSH USERNAME"
end
end
```

And then run `vagrant up --provider=rackspace`.
And then run `vagrant up --provider=openstack`.

This will start an Ubuntu 12.04 instance in the DFW datacenter region within
your account. And assuming your SSH information was filled in properly
This will start a tiny Ubuntu instance in your OpenStack installation within
your tenant. And assuming your SSH information was filled in properly
within your Vagrantfile, SSH and provisioning will work as well.

Note that normally a lot of this boilerplate is encoded within the box
Expand All @@ -71,8 +76,8 @@ no preconfigured defaults.
## Box Format

Every provider in Vagrant must introduce a custom box format. This
provider introduces `rackspace` boxes. You can view an example box in
the [example_box/ directory](https://github.com/mitchellh/vagrant-rackspace/tree/master/example_box).
provider introduces `openstack` boxes. You can view an example box in
the [example_box/ directory](https://github.com/cloudbau/vagrant-openstack/tree/master/example_box).
That directory also contains instructions on how to build a box.

The box format is basically just the required `metadata.json` file
Expand All @@ -83,29 +88,28 @@ provider-specific configuration for this provider.

This provider exposes quite a few provider-specific configuration options:

* `api_key` - The API key for accessing Rackspace.
* `api_key` - The API key for accessing OpenStack.
* `flavor` - The server flavor to boot. This can be a string matching
the exact ID or name of the server, or this can be a regular expression
to partially match some server flavor.
* `image` - The server image to boot. This can be a string matching the
exact ID or name of the image, or this can be a regular expression to
partially match some image.
* `endpoint` - The endpoint to hit. By default this is DFW.
* `public_key_path` - The path to a public key to initialize with the remote
server. This should be the matching pair for the private key configured
with `config.ssh.private_key_path` on Vagrant.
* `server_name` - The name of the server within RackSpace Cloud. This
* `endpoint` - The keystone authentication URL of your OpenStack installation.
* `server_name` - The name of the server within the OpenStack Cloud. This
defaults to the name of the Vagrant machine (via `config.vm.define`), but
can be overridden with this.
* `username` - The username with which to access Rackspace.
* `username` - The username with which to access OpenStack.
* `keypair_name` - The name of the keypair to access the machine.
* `ssh_username` - The username to access the machine.

These can be set like typical provider-specific configuration:

```ruby
Vagrant.configure("2") do |config|
# ... other stuff

config.vm.provider :rackspace do |rs|
config.vm.provider :openstack do |rs|
rs.username = "mitchellh"
rs.api_key = "foobarbaz"
end
Expand All @@ -115,9 +119,9 @@ end
## Networks

Networking features in the form of `config.vm.network` are not
supported with `vagrant-rackspace`, currently. If any of these are
supported with `vagrant-openstack`, currently. If any of these are
specified, Vagrant will emit a warning, but will otherwise boot
the Rackspace server.
the OpenStack server.

## Synced Folders

Expand All @@ -131,7 +135,7 @@ chef, and puppet) to work!

## Development

To work on the `vagrant-rackspace` plugin, clone this repository out, and use
To work on the `vagrant-openstack` plugin, clone this repository out, and use
[Bundler](http://gembundler.com) to get the dependencies:

```
Expand All @@ -150,5 +154,5 @@ creating a `Vagrantfile` in the top level of this directory (it is gitignored)
that uses it, and uses bundler to execute Vagrant:

```
$ bundle exec vagrant up --provider=rackspace
$ bundle exec vagrant up --provider=openstack
```
Binary file modified dummy.box
Binary file not shown.
5 changes: 2 additions & 3 deletions lib/vagrant-openstack/action/connect_openstack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ def call(env)
@logger.info("Connecting to OpenStack...")
env[:openstack_compute] = Fog::Compute.new({
:provider => :openstack,
:version => :v2,
:openstack_username => username,
:openstack_api_key => api_key,
:openstack_auth_uri => endpoint,
:openstack_username => username
:openstack_auth_url => endpoint
})

@app.call(env)
Expand Down
32 changes: 11 additions & 21 deletions lib/vagrant-openstack/action/create_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,12 @@ def call(env)

# Find the image
env[:ui].info(I18n.t("vagrant_openstack.finding_image"))
image = find_matching(env[:openstack_compute].images.all, config.image)
image = find_matching(env[:openstack_compute].images, config.image)
raise Errors::NoMatchingImage if !image

# Figure out the name for the server
server_name = config.server_name || env[:machine].name

# If we're using the default keypair, then show a warning
default_key_path = Vagrant.source_root.join("keys/vagrant.pub").to_s
public_key_path = File.expand_path(config.public_key_path, env[:root_path])

if default_key_path == public_key_path
env[:ui].warn(I18n.t("vagrant_openstack.warn_insecure_ssh"))
end

# Output the settings we're going to use to the user
env[:ui].info(I18n.t("vagrant_openstack.launching_server"))
env[:ui].info(" -- Flavor: #{flavor.name}")
Expand All @@ -48,15 +40,10 @@ def call(env)

# Build the options for launching...
options = {
:flavor_id => flavor.id,
:image_id => image.id,
:flavor_ref => flavor.id,
:image_ref => image.id,
:name => server_name,
:personality => [
{
:path => "/root/.ssh/authorized_keys",
:contents => Base64.encode64(File.read(public_key_path))
}
]
:key_name => config.keypair_name
}

# Create the server
Expand All @@ -67,7 +54,7 @@ def call(env)

# Wait for the server to finish building
env[:ui].info(I18n.t("vagrant_openstack.waiting_for_build"))
retryable(:on => Fog::Errors::TimeoutError, :tries => 200) do
retryable(:on => Timeout::Error, :tries => 200) do
# If we're interrupted don't worry about waiting
next if env[:interrupted]

Expand All @@ -93,9 +80,12 @@ def call(env)
# Wait for SSH to become available
env[:ui].info(I18n.t("vagrant_openstack.waiting_for_ssh"))
while true
# If we're interrupted then just back out
break if env[:interrupted]
break if env[:machine].communicate.ready?
begin
# If we're interrupted then just back out
break if env[:interrupted]
break if env[:machine].communicate.ready?
rescue Errno::ENETUNREACH
end
sleep 2
end

Expand Down
7 changes: 5 additions & 2 deletions lib/vagrant-openstack/action/read_ssh_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ def read_ssh_info(openstack, machine)
return nil
end

config = machine.provider_config

# Read the DNS info
return {
:host => server.ipv4_address,
# Usually there should only be one public IP
:host => server.addresses['public'].last['addr'],
:port => 22,
:username => "root"
:username => config.ssh_username
}
end
end
Expand Down
40 changes: 22 additions & 18 deletions lib/vagrant-openstack/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ class Config < Vagrant.plugin("2", :config)
# expression to partially match a name.
attr_accessor :image

# The path to the public key to set up on the remote server for SSH.
# This should match the private key configured with `config.ssh.private_key_path`.
#
# @return [String]
attr_accessor :public_key_path

# The name of the server. This defaults to the name of the machine
# defined by Vagrant (via `config.vm.define`), but can be overriden
# here.
Expand All @@ -37,41 +31,51 @@ class Config < Vagrant.plugin("2", :config)
#
# @return [String]
attr_accessor :username

# The name of the keypair to use.
#
# @return [String]
attr_accessor :keypair_name

# The SSH username to use with this OpenStack instance. This overrides
# the `config.ssh.username` variable.
#
# @return [String]
attr_accessor :ssh_username

def initialize
@api_key = UNSET_VALUE
@endpoint = UNSET_VALUE
@flavor = UNSET_VALUE
@image = UNSET_VALUE
@public_key_path = UNSET_VALUE
@server_name = UNSET_VALUE
@username = UNSET_VALUE
@keypair_name = UNSET_VALUE
@ssh_username = UNSET_VALUE
end

def finalize!
@api_key = nil if @api_key == UNSET_VALUE
@endpoint = nil if @endpoint == UNSET_VALUE
@flavor = /512MB/ if @flavor == UNSET_VALUE
@image = /Ubuntu/ if @image == UNSET_VALUE
@flavor = /m1.tiny/ if @flavor == UNSET_VALUE
@image = /cirros/ if @image == UNSET_VALUE
@server_name = nil if @server_name == UNSET_VALUE
@username = nil if @username == UNSET_VALUE

if @public_key_path == UNSET_VALUE
@public_key_path = Vagrant.source_root.join("keys/vagrant.pub")
end
# Keypair defaults to nil
@keypair_name = nil if @keypair_name == UNSET_VALUE

# The SSH values by default are nil, and the top-level config
# `config.ssh` values are used.
@ssh_username = nil if @ssh_username == UNSET_VALUE
end

def validate(machine)
errors = []

errors << I18n.t("vagrant_openstack.config.api_key_required") if !@api_key
errors << I18n.t("vagrant_openstack.config.username_required") if !@username

public_key_path = File.expand_path(@public_key_path, machine.env.root_path)
if !File.file?(public_key_path)
errors << I18n.t("vagrant_openstack.config.public_key_not_found")
end


{ "OpenStack Provider" => errors }
end
end
Expand Down
13 changes: 2 additions & 11 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ en:
Waiting for the server to be built...
waiting_for_ssh: |-
Waiting for SSH to become available...
warn_insecure_ssh: |-
Warning! By not specifying a custom public/private keypair,
Vagrant is defaulting to use the insecure keypair that ships with
Vagrant. While this isn't much of a big deal for local development,
this is quite insecure for remote servers. Please specify a custom
public/private keypair.
warn_networks: |-
Warning! The OpenStack provider doesn't support any of the Vagrant
high-level network configurations (`config.vm.network`). They
Expand All @@ -34,9 +28,6 @@ en:
config:
api_key_required: |-
An API key is required.
public_key_not_found: |-
The public key file could not be found. Please make sure
you specified a valid path.
username_required: |-
A username is required.
Expand Down Expand Up @@ -74,8 +65,8 @@ en:
short_error: |-
error
long_error: |-
The server is in an erroneous state. Contact OpenStack support
or destroy the machine with `vagrant destroy`.
The server is in an erroneous state. Destroy the machine with
`vagrant destroy`.
short_not_created: |-
not created
long_not_created: |-
Expand Down
Loading

0 comments on commit 6116f4a

Please sign in to comment.