vagrant-openstack-plugin 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .vagrant
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ Vagrantfile
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0 (March 14, 2013)
2
+
3
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ # We depend on Vagrant for development, but we don't add it as a
7
+ # gem dependency because we expect to be installed within the
8
+ # Vagrant environment itself using `vagrant plugin`.
9
+ #gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 Mitchell Hashimoto
2
+ Copyright (c) 2013 cloudbau GmbH
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # Vagrant OpenStack Cloud Provider
2
+
3
+ This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that adds a
4
+ [OpenStack Cloud](http://www.openstack.org) provider to Vagrant,
5
+ allowing Vagrant to control and provision machines within an OpenStack
6
+ cloud.
7
+
8
+ This plugin started as a fork of the Vagrant RackSpace provider.
9
+
10
+ **Note:** This plugin requires Vagrant 1.1+.
11
+
12
+ ## Features
13
+
14
+ * Boot OpenStack Cloud instances.
15
+ * SSH into the instances.
16
+ * Provision the instances with any built-in Vagrant provisioner.
17
+ * Minimal synced folder support via `rsync`.
18
+
19
+ ## Usage
20
+
21
+ Install using standard Vagrant 1.1+ plugin installation methods. After
22
+ installing, `vagrant up` and specify the `openstack` provider. An example is
23
+ shown below.
24
+
25
+ ```
26
+ $ vagrant plugin install vagrant-openstack
27
+ ...
28
+ $ vagrant up --provider=openstack
29
+ ...
30
+ ```
31
+
32
+ Of course prior to doing this, you'll need to obtain an OpenStack-compatible
33
+ box file for Vagrant.
34
+
35
+ ## Quick Start
36
+
37
+ After installing the plugin (instructions above), the quickest way to get
38
+ started is to actually use a dummy OpenStack box and specify all the details
39
+ manually within a `config.vm.provider` block. So first, add the dummy
40
+ box using any name you want:
41
+
42
+ ```
43
+ $ vagrant box add dummy https://github.com/cloudbau/vagrant-openstack/raw/master/dummy.box
44
+ ...
45
+ ```
46
+
47
+ And then make a Vagrantfile that looks like the following, filling in
48
+ your information where necessary.
49
+
50
+ ```
51
+ require 'vagrant-openstack'
52
+
53
+ Vagrant.configure("2") do |config|
54
+ config.vm.box = "dummy"
55
+
56
+ config.vm.provider :openstack do |os| # e.g.
57
+ os.username = "YOUR USERNAME" # "#{ENV['OS_USERNAME']}"
58
+ os.api_key = "YOUR API KEY" # "#{ENV['OS_PASSWORD']}"
59
+ os.flavor = /m1.tiny/
60
+ os.image = /Ubuntu/
61
+ os.endpoint = "KEYSTONE AUTH URL" # "#{ENV['OS_AUTH_URL']}/tokens"
62
+ os.keypair_name = "YOUR KEYPAIR NAME"
63
+ os.ssh_username = "SSH USERNAME"
64
+ end
65
+ end
66
+ ```
67
+
68
+ And then run `vagrant up --provider=openstack`.
69
+
70
+ This will start a tiny Ubuntu instance in your OpenStack installation within
71
+ your tenant. And assuming your SSH information was filled in properly
72
+ within your Vagrantfile, SSH and provisioning will work as well.
73
+
74
+ Note that normally a lot of this boilerplate is encoded within the box
75
+ file, but the box file used for the quick start, the "dummy" box, has
76
+ no preconfigured defaults.
77
+
78
+ ## Box Format
79
+
80
+ Every provider in Vagrant must introduce a custom box format. This
81
+ provider introduces `openstack` boxes. You can view an example box in
82
+ the [example_box/ directory](https://github.com/cloudbau/vagrant-openstack/tree/master/example_box).
83
+ That directory also contains instructions on how to build a box.
84
+
85
+ The box format is basically just the required `metadata.json` file
86
+ along with a `Vagrantfile` that does default settings for the
87
+ provider-specific configuration for this provider.
88
+
89
+ ## Configuration
90
+
91
+ This provider exposes quite a few provider-specific configuration options:
92
+
93
+ * `api_key` - The API key for accessing OpenStack.
94
+ * `flavor` - The server flavor to boot. This can be a string matching
95
+ the exact ID or name of the server, or this can be a regular expression
96
+ to partially match some server flavor.
97
+ * `image` - The server image to boot. This can be a string matching the
98
+ exact ID or name of the image, or this can be a regular expression to
99
+ partially match some image.
100
+ * `endpoint` - The keystone authentication URL of your OpenStack installation.
101
+ * `server_name` - The name of the server within the OpenStack Cloud. This
102
+ defaults to the name of the Vagrant machine (via `config.vm.define`), but
103
+ can be overridden with this.
104
+ * `username` - The username with which to access OpenStack.
105
+ * `keypair_name` - The name of the keypair to access the machine.
106
+ * `ssh_username` - The username to access the machine.
107
+
108
+ These can be set like typical provider-specific configuration:
109
+
110
+ ```ruby
111
+ Vagrant.configure("2") do |config|
112
+ # ... other stuff
113
+
114
+ config.vm.provider :openstack do |rs|
115
+ rs.username = "mitchellh"
116
+ rs.api_key = "foobarbaz"
117
+ end
118
+ end
119
+ ```
120
+
121
+ ## Networks
122
+
123
+ Networking features in the form of `config.vm.network` are not
124
+ supported with `vagrant-openstack`, currently. If any of these are
125
+ specified, Vagrant will emit a warning, but will otherwise boot
126
+ the OpenStack server.
127
+
128
+ ## Synced Folders
129
+
130
+ There is minimal support for synced folders. Upon `vagrant up`,
131
+ `vagrant reload`, and `vagrant provision`, the OpenStack provider will use
132
+ `rsync` (if available) to uni-directionally sync the folder to
133
+ the remote machine over SSH.
134
+
135
+ This is good enough for all built-in Vagrant provisioners (shell,
136
+ chef, and puppet) to work!
137
+
138
+ ## Development
139
+
140
+ To work on the `vagrant-openstack` plugin, clone this repository out, and use
141
+ [Bundler](http://gembundler.com) to get the dependencies:
142
+
143
+ ```
144
+ $ bundle
145
+ ```
146
+
147
+ Once you have the dependencies, verify the unit tests pass with `rake`:
148
+
149
+ ```
150
+ $ bundle exec rake
151
+ ```
152
+
153
+ If those pass, you're ready to start developing the plugin. You can test
154
+ the plugin without installing it into your Vagrant environment by just
155
+ creating a `Vagrantfile` in the top level of this directory (it is gitignored)
156
+ that uses it, and uses bundler to execute Vagrant:
157
+
158
+ ```
159
+ $ bundle exec vagrant up --provider=openstack
160
+ ```
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rspec/core/rake_task'
4
+
5
+ # Immediately sync all stdout so that tools like buildbot can
6
+ # immediately load in the output.
7
+ $stdout.sync = true
8
+ $stderr.sync = true
9
+
10
+ # Change to the directory of this file.
11
+ Dir.chdir(File.expand_path("../", __FILE__))
12
+
13
+ # This installs the tasks that help with gem creation and
14
+ # publishing.
15
+ Bundler::GemHelper.install_tasks
16
+
17
+ # Install the `spec` task so that we can run tests.
18
+ RSpec::Core::RakeTask.new
19
+
20
+ # Default task is to run the unit tests
21
+ task :default => "spec"
data/dummy.box ADDED
Binary file
@@ -0,0 +1,13 @@
1
+ # Vagrant OpenStack Cloud Example Box
2
+
3
+ Vagrant providers each require a custom provider-specific box format.
4
+ This folder shows the example contents of a box for the `openstack` provider.
5
+ To turn this into a box:
6
+
7
+ ```
8
+ $ tar cvzf openstack.box ./metadata.json ./Vagrantfile
9
+ ```
10
+
11
+ This box works by using Vagrant's built-in Vagrantfile merging to setup
12
+ defaults for OpenStack. These defaults can easily be overwritten by higher-level
13
+ Vagrantfiles (such as project root Vagrantfiles).
@@ -0,0 +1,3 @@
1
+ {
2
+ "provider": "openstack"
3
+ }
@@ -0,0 +1,53 @@
1
+ require "pathname"
2
+
3
+ require "vagrant-openstack/plugin"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ lib_path = Pathname.new(File.expand_path("../vagrant-openstack", __FILE__))
8
+ autoload :Errors, lib_path.join("errors")
9
+
10
+ # This initializes the i18n load path so that the plugin-specific
11
+ # translations work.
12
+ def self.init_i18n
13
+ I18n.load_path << File.expand_path("locales/en.yml", source_root)
14
+ I18n.reload!
15
+ end
16
+
17
+ # This initializes the logging so that our logs are outputted at
18
+ # the same level as Vagrant core logs.
19
+ def self.init_logging
20
+ # Initialize logging
21
+ level = nil
22
+ begin
23
+ level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
24
+ rescue NameError
25
+ # This means that the logging constant wasn't found,
26
+ # which is fine. We just keep `level` as `nil`. But
27
+ # we tell the user.
28
+ level = nil
29
+ end
30
+
31
+ # Some constants, such as "true" resolve to booleans, so the
32
+ # above error checking doesn't catch it. This will check to make
33
+ # sure that the log level is an integer, as Log4r requires.
34
+ level = nil if !level.is_a?(Integer)
35
+
36
+ # Set the logging level on all "vagrant" namespaced
37
+ # logs as long as we have a valid level.
38
+ if level
39
+ logger = Log4r::Logger.new("vagrant_openstack")
40
+ logger.outputters = Log4r::Outputter.stderr
41
+ logger.level = level
42
+ logger = nil
43
+ end
44
+ end
45
+
46
+ # This returns the path to the source of this plugin.
47
+ #
48
+ # @return [Pathname]
49
+ def self.source_root
50
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,110 @@
1
+ require "pathname"
2
+
3
+ require "vagrant/action/builder"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # Include the built-in modules so we can use them as top-level things.
9
+ include Vagrant::Action::Builtin
10
+
11
+ # This action is called to destroy the remote machine.
12
+ def self.action_destroy
13
+ Vagrant::Action::Builder.new.tap do |b|
14
+ b.use ConfigValidate
15
+ b.use Call, IsCreated do |env, b2|
16
+ if !env[:result]
17
+ b2.use MessageNotCreated
18
+ next
19
+ end
20
+
21
+ b2.use ConnectOpenStack
22
+ b2.use DeleteServer
23
+ end
24
+ end
25
+ end
26
+
27
+ # This action is called to read the SSH info of the machine. The
28
+ # resulting state is expected to be put into the `:machine_ssh_info`
29
+ # key.
30
+ def self.action_read_ssh_info
31
+ Vagrant::Action::Builder.new.tap do |b|
32
+ b.use ConfigValidate
33
+ b.use ConnectOpenStack
34
+ b.use ReadSSHInfo
35
+ end
36
+ end
37
+
38
+ # This action is called to read the state of the machine. The
39
+ # resulting state is expected to be put into the `:machine_state_id`
40
+ # key.
41
+ def self.action_read_state
42
+ Vagrant::Action::Builder.new.tap do |b|
43
+ b.use ConfigValidate
44
+ b.use ConnectOpenStack
45
+ b.use ReadState
46
+ end
47
+ end
48
+
49
+ def self.action_ssh
50
+ Vagrant::Action::Builder.new.tap do |b|
51
+ b.use ConfigValidate
52
+ b.use Call, IsCreated do |env, b2|
53
+ if !env[:result]
54
+ b2.use MessageNotCreated
55
+ next
56
+ end
57
+
58
+ b2.use SSHExec
59
+ end
60
+ end
61
+ end
62
+
63
+ def self.action_up
64
+ Vagrant::Action::Builder.new.tap do |b|
65
+ b.use ConfigValidate
66
+ b.use Call, IsCreated do |env, b2|
67
+ if env[:result]
68
+ b2.use MessageAlreadyCreated
69
+ next
70
+ end
71
+
72
+ b2.use ConnectOpenStack
73
+ b2.use Provision
74
+ b2.use SyncFolders
75
+ b2.use WarnNetworks
76
+ b2.use CreateServer
77
+ end
78
+ end
79
+ end
80
+
81
+ def self.action_provision
82
+ Vagrant::Action::Builder.new.tap do |b|
83
+ b.use ConfigValidate
84
+ b.use Call, IsCreated do |env, b2|
85
+ if !env[:result]
86
+ b2.use MessageNotCreated
87
+ next
88
+ end
89
+
90
+ b2.use Provision
91
+ b2.use SyncFolders
92
+ end
93
+ end
94
+ end
95
+
96
+ # The autoload farm
97
+ action_root = Pathname.new(File.expand_path("../action", __FILE__))
98
+ autoload :ConnectOpenStack, action_root.join("connect_openstack")
99
+ autoload :CreateServer, action_root.join("create_server")
100
+ autoload :DeleteServer, action_root.join("delete_server")
101
+ autoload :IsCreated, action_root.join("is_created")
102
+ autoload :MessageAlreadyCreated, action_root.join("message_already_created")
103
+ autoload :MessageNotCreated, action_root.join("message_not_created")
104
+ autoload :ReadSSHInfo, action_root.join("read_ssh_info")
105
+ autoload :ReadState, action_root.join("read_state")
106
+ autoload :SyncFolders, action_root.join("sync_folders")
107
+ autoload :WarnNetworks, action_root.join("warn_networks")
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module OpenStack
6
+ module Action
7
+ # This action connects to OpenStack, verifies credentials work, and
8
+ # puts the OpenStack connection object into the `:openstack_compute` key
9
+ # in the environment.
10
+ class ConnectOpenStack
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_openstack::action::connect_openstack")
14
+ end
15
+
16
+ def call(env)
17
+ # Get the configs
18
+ config = env[:machine].provider_config
19
+ api_key = config.api_key
20
+ endpoint = config.endpoint
21
+ username = config.username
22
+
23
+ @logger.info("Connecting to OpenStack...")
24
+ env[:openstack_compute] = Fog::Compute.new({
25
+ :provider => :openstack,
26
+ :openstack_username => username,
27
+ :openstack_api_key => api_key,
28
+ :openstack_auth_url => endpoint
29
+ })
30
+
31
+ @app.call(env)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,117 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module OpenStack
8
+ module Action
9
+ # This creates the OpenStack server.
10
+ class CreateServer
11
+ include Vagrant::Util::Retryable
12
+
13
+ def initialize(app, env)
14
+ @app = app
15
+ @logger = Log4r::Logger.new("vagrant_openstack::action::create_server")
16
+ end
17
+
18
+ def call(env)
19
+ # Get the configs
20
+ config = env[:machine].provider_config
21
+
22
+ # Find the flavor
23
+ env[:ui].info(I18n.t("vagrant_openstack.finding_flavor"))
24
+ flavor = find_matching(env[:openstack_compute].flavors.all, config.flavor)
25
+ raise Errors::NoMatchingFlavor if !flavor
26
+
27
+ # Find the image
28
+ env[:ui].info(I18n.t("vagrant_openstack.finding_image"))
29
+ image = find_matching(env[:openstack_compute].images, config.image)
30
+ raise Errors::NoMatchingImage if !image
31
+
32
+ # Figure out the name for the server
33
+ server_name = config.server_name || env[:machine].name
34
+
35
+ # Output the settings we're going to use to the user
36
+ env[:ui].info(I18n.t("vagrant_openstack.launching_server"))
37
+ env[:ui].info(" -- Flavor: #{flavor.name}")
38
+ env[:ui].info(" -- Image: #{image.name}")
39
+ env[:ui].info(" -- Name: #{server_name}")
40
+
41
+ # Build the options for launching...
42
+ options = {
43
+ :flavor_ref => flavor.id,
44
+ :image_ref => image.id,
45
+ :name => server_name,
46
+ :key_name => config.keypair_name,
47
+ :user_data_encoded => Base64.encode64(config.user_data)
48
+ }
49
+
50
+ # Create the server
51
+ server = env[:openstack_compute].servers.create(options)
52
+
53
+ # Store the ID right away so we can track it
54
+ env[:machine].id = server.id
55
+
56
+ # Wait for the server to finish building
57
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_build"))
58
+ retryable(:on => Timeout::Error, :tries => 200) do
59
+ # If we're interrupted don't worry about waiting
60
+ next if env[:interrupted]
61
+
62
+ # Set the progress
63
+ env[:ui].clear_line
64
+ env[:ui].report_progress(server.progress, 100, false)
65
+
66
+ # Wait for the server to be ready
67
+ begin
68
+ server.wait_for(5) { ready? }
69
+ rescue RuntimeError => e
70
+ # If we don't have an error about a state transition, then
71
+ # we just move on.
72
+ raise if e.message !~ /should have transitioned/
73
+ raise Errors::CreateBadState, :state => server.state
74
+ end
75
+ end
76
+
77
+ if !env[:interrupted]
78
+ # Clear the line one more time so the progress is removed
79
+ env[:ui].clear_line
80
+
81
+ # Wait for SSH to become available
82
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_ssh"))
83
+ while true
84
+ begin
85
+ # If we're interrupted then just back out
86
+ break if env[:interrupted]
87
+ break if env[:machine].communicate.ready?
88
+ rescue Errno::ENETUNREACH
89
+ end
90
+ sleep 2
91
+ end
92
+
93
+ env[:ui].info(I18n.t("vagrant_openstack.ready"))
94
+ end
95
+
96
+ @app.call(env)
97
+ end
98
+
99
+ protected
100
+
101
+ # This method finds a matching _thing_ in a collection of
102
+ # _things_. This works matching if the ID or NAME equals to
103
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
104
+ # as well.
105
+ def find_matching(collection, name)
106
+ collection.each do |single|
107
+ return single if single.id == name
108
+ return single if single.name == name
109
+ return single if name.is_a?(Regexp) && name =~ single.name
110
+ end
111
+
112
+ nil
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,26 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Action
6
+ # This deletes the running server, if there is one.
7
+ class DeleteServer
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_openstack::action::delete_server")
11
+ end
12
+
13
+ def call(env)
14
+ if env[:machine].id
15
+ env[:ui].info(I18n.t("vagrant_openstack.deleting_server"))
16
+ server = env[:openstack_compute].servers.get(env[:machine].id)
17
+ server.destroy
18
+ env[:machine].id = nil
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ module Action
4
+ class IsCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:result] = env[:machine].state.id != :not_created
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ module Action
4
+ class MessageAlreadyCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_openstack.already_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ module Action
4
+ class MessageNotCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_openstack.not_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Action
6
+ # This action reads the SSH info for the machine and puts it into the
7
+ # `:machine_ssh_info` key in the environment.
8
+ class ReadSSHInfo
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_openstack::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:openstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(openstack, machine)
21
+ return nil if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = openstack.servers.get(machine.id)
25
+ if server.nil?
26
+ # The machine can't be found
27
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return nil
30
+ end
31
+
32
+ config = machine.provider_config
33
+
34
+ host = server.addresses['public'].last['addr'] rescue nil
35
+ # Read the DNS info
36
+ return {
37
+ # Usually there should only be one public IP
38
+ :host => host,
39
+ :port => 22,
40
+ :username => config.ssh_username
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_openstack::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:openstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(openstack, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = openstack.servers.get(machine.id)
25
+ if server.nil? || server.state == "DELETED"
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or deleted, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.downcase.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # This middleware uses `rsync` to sync the folders over to the
9
+ # remote instance.
10
+ class SyncFolders
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_openstack::action::sync_folders")
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+
19
+ ssh_info = env[:machine].ssh_info
20
+
21
+ env[:machine].config.vm.synced_folders.each do |id, data|
22
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
23
+ guestpath = data[:guestpath]
24
+
25
+ # Make sure there is a trailing slash on the host path to
26
+ # avoid creating an additional directory with rsync
27
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
28
+
29
+ env[:ui].info(I18n.t("vagrant_openstack.rsync_folder",
30
+ :hostpath => hostpath,
31
+ :guestpath => guestpath))
32
+
33
+ # Create the guest path
34
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
35
+ env[:machine].communicate.sudo(
36
+ "chown #{ssh_info[:username]} '#{guestpath}'")
37
+
38
+ # Rsync over to the guest path using the SSH info
39
+ command = [
40
+ "rsync", "--verbose", "--archive", "-z",
41
+ "-e", "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path]}'",
42
+ hostpath,
43
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
44
+
45
+ r = Vagrant::Util::Subprocess.execute(*command)
46
+ if r.exit_code != 0
47
+ raise Errors::RsyncError,
48
+ :guestpath => guestpath,
49
+ :hostpath => hostpath,
50
+ :stderr => r.stderr
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ module Action
4
+ class WarnNetworks
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].config.vm.networks.length > 0
11
+ env[:ui].warn(I18n.t("vagrant_openstack.warn_networks"))
12
+ end
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,92 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The API key to access OpenStack.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :api_key
10
+
11
+ # The endpoint to access OpenStack. If nil, it will default
12
+ # to DFW.
13
+ #
14
+ # @return [String]
15
+ attr_accessor :endpoint
16
+
17
+ # The flavor of server to launch, either the ID or name. This
18
+ # can also be a regular expression to partially match a name.
19
+ attr_accessor :flavor
20
+
21
+ # The name or ID of the image to use. This can also be a regular
22
+ # expression to partially match a name.
23
+ attr_accessor :image
24
+
25
+ # The name of the server. This defaults to the name of the machine
26
+ # defined by Vagrant (via `config.vm.define`), but can be overriden
27
+ # here.
28
+ attr_accessor :server_name
29
+
30
+ # The username to access OpenStack.
31
+ #
32
+ # @return [String]
33
+ attr_accessor :username
34
+
35
+ # The name of the keypair to use.
36
+ #
37
+ # @return [String]
38
+ attr_accessor :keypair_name
39
+
40
+ # The SSH username to use with this OpenStack instance. This overrides
41
+ # the `config.ssh.username` variable.
42
+ #
43
+ # @return [String]
44
+ attr_accessor :ssh_username
45
+
46
+ # User data to be sent to the newly created OpenStack instance. Use this
47
+ # e.g. to inject a script at boot time.
48
+ #
49
+ # @return [String]
50
+ attr_accessor :user_data
51
+
52
+ def initialize
53
+ @api_key = UNSET_VALUE
54
+ @endpoint = UNSET_VALUE
55
+ @flavor = UNSET_VALUE
56
+ @image = UNSET_VALUE
57
+ @server_name = UNSET_VALUE
58
+ @username = UNSET_VALUE
59
+ @keypair_name = UNSET_VALUE
60
+ @ssh_username = UNSET_VALUE
61
+ @user_data = UNSET_VALUE
62
+ end
63
+
64
+ def finalize!
65
+ @api_key = nil if @api_key == UNSET_VALUE
66
+ @endpoint = nil if @endpoint == UNSET_VALUE
67
+ @flavor = /m1.tiny/ if @flavor == UNSET_VALUE
68
+ @image = /cirros/ if @image == UNSET_VALUE
69
+ @server_name = nil if @server_name == UNSET_VALUE
70
+ @username = nil if @username == UNSET_VALUE
71
+
72
+ # Keypair defaults to nil
73
+ @keypair_name = nil if @keypair_name == UNSET_VALUE
74
+
75
+ # The SSH values by default are nil, and the top-level config
76
+ # `config.ssh` values are used.
77
+ @ssh_username = nil if @ssh_username == UNSET_VALUE
78
+
79
+ @user_data = "" if @user_data == UNSET_VALUE
80
+ end
81
+
82
+ def validate(machine)
83
+ errors = []
84
+
85
+ errors << I18n.t("vagrant_openstack.config.api_key_required") if !@api_key
86
+ errors << I18n.t("vagrant_openstack.config.username_required") if !@username
87
+
88
+ { "OpenStack Provider" => errors }
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Errors
6
+ class VagrantOpenStackError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_openstack.errors")
8
+ end
9
+
10
+ class CreateBadState < VagrantOpenStackError
11
+ error_key(:create_bad_state)
12
+ end
13
+
14
+ class NoMatchingFlavor < VagrantOpenStackError
15
+ error_key(:no_matching_flavor)
16
+ end
17
+
18
+ class NoMatchingImage < VagrantOpenStackError
19
+ error_key(:no_matching_image)
20
+ end
21
+
22
+ class RsyncError < VagrantOpenStackError
23
+ error_key(:rsync_error)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The OpenStack Cloud provider must be run within Vagrant."
5
+ end
6
+
7
+ # This is a sanity check to make sure no one is attempting to install
8
+ # this into an early Vagrant version.
9
+ if Vagrant::VERSION < "1.1.0"
10
+ raise "OpenStack Cloud provider is only compatible with Vagrant 1.1+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module OpenStack
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "OpenStack Cloud"
17
+ description <<-DESC
18
+ This plugin enables Vagrant to manage machines in OpenStack Cloud.
19
+ DESC
20
+
21
+ config(:openstack, :provider) do
22
+ require_relative "config"
23
+ Config
24
+ end
25
+
26
+ provider(:openstack) do
27
+ # Setup some things
28
+ OpenStack.init_i18n
29
+ OpenStack.init_logging
30
+
31
+ # Load the actual provider
32
+ require_relative "provider"
33
+ Provider
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require "vagrant"
2
+
3
+ require "vagrant-openstack/action"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ class Provider < Vagrant.plugin("2", :provider)
8
+ def initialize(machine)
9
+ @machine = machine
10
+ end
11
+
12
+ def action(name)
13
+ # Attempt to get the action method from the Action class if it
14
+ # exists, otherwise return nil to show that we don't support the
15
+ # given action.
16
+ action_method = "action_#{name}"
17
+ return Action.send(action_method) if Action.respond_to?(action_method)
18
+ nil
19
+ end
20
+
21
+ def ssh_info
22
+ # Run a custom action called "read_ssh_info" which does what it
23
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
24
+ # key in the environment.
25
+ env = @machine.action("read_ssh_info")
26
+ env[:machine_ssh_info]
27
+ end
28
+
29
+ def state
30
+ # Run a custom action we define called "read_state" which does
31
+ # what it says. It puts the state in the `:machine_state_id`
32
+ # key in the environment.
33
+ env = @machine.action("read_state")
34
+
35
+ state_id = env[:machine_state_id]
36
+
37
+ # Get the short and long description
38
+ short = I18n.t("vagrant_openstack.states.short_#{state_id}")
39
+ long = I18n.t("vagrant_openstack.states.long_#{state_id}")
40
+
41
+ # Return the MachineState object
42
+ Vagrant::MachineState.new(state_id, short, long)
43
+ end
44
+
45
+ def to_s
46
+ "OpenStack Cloud"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,73 @@
1
+ en:
2
+ vagrant_openstack:
3
+ already_created: |-
4
+ The server is already created.
5
+ deleting_server: |-
6
+ Deleting server...
7
+ finding_flavor: |-
8
+ Finding flavor for server...
9
+ finding_image: |-
10
+ Finding image for server...
11
+ launching_server: |-
12
+ Launching a server with the following settings...
13
+ not_created: |-
14
+ The server hasn't been created yet. Run `vagrant up` first.
15
+ ready: |-
16
+ The server is ready!
17
+ rsync_folder: |-
18
+ Rsyncing folder: %{hostpath} => %{guestpath}
19
+ waiting_for_build: |-
20
+ Waiting for the server to be built...
21
+ waiting_for_ssh: |-
22
+ Waiting for SSH to become available...
23
+ warn_networks: |-
24
+ Warning! The OpenStack provider doesn't support any of the Vagrant
25
+ high-level network configurations (`config.vm.network`). They
26
+ will be silently ignored.
27
+
28
+ config:
29
+ api_key_required: |-
30
+ An API key is required.
31
+ username_required: |-
32
+ A username is required.
33
+
34
+ errors:
35
+ create_bad_state: |-
36
+ While creating the server, it transitioned to an unexpected
37
+ state: '%{state}', instead of properly booting. Run `vagrant status`
38
+ to find out what can be done about this state, or `vagrant destroy`
39
+ if you want to start over.
40
+ no_matching_flavor: |-
41
+ No matching flavor was found! Please check your flavor setting
42
+ to make sure you have a valid flavor chosen.
43
+ no_matching_image: |-
44
+ No matching image was found! Please check your image setting to
45
+ make sure you have a valid image chosen.
46
+ rsync_error: |-
47
+ There was an error when attemping to rsync a share folder.
48
+ Please inspect the error message below for more info.
49
+
50
+ Host path: %{hostpath}
51
+ Guest path: %{guestpath}
52
+ Error: %{stderr}
53
+
54
+ states:
55
+ short_active: |-
56
+ active
57
+ long_active: |-
58
+ The server is up and running. Run `vagrant ssh` to access it.
59
+ short_build: |-
60
+ building
61
+ long_build: |-
62
+ The server is currently being built. You must wait for this to
63
+ complete before you can access it. You can delete the server, however,
64
+ by running `vagrant destroy`.
65
+ short_error: |-
66
+ error
67
+ long_error: |-
68
+ The server is in an erroneous state. Destroy the machine with
69
+ `vagrant destroy`.
70
+ short_not_created: |-
71
+ not created
72
+ long_not_created: |-
73
+ The server is not created. Run `vagrant up` to create it.
@@ -0,0 +1,67 @@
1
+ require "vagrant-openstack/config"
2
+
3
+ describe VagrantPlugins::OpenStack::Config do
4
+ describe "defaults" do
5
+ let(:vagrant_public_key) { Vagrant.source_root.join("keys/vagrant.pub") }
6
+
7
+ subject do
8
+ super().tap do |o|
9
+ o.finalize!
10
+ end
11
+ end
12
+
13
+ its(:api_key) { should be_nil }
14
+ its(:endpoint) { should be_nil }
15
+ its(:flavor) { should eq(/m1.tiny/) }
16
+ its(:image) { should eq(/cirros/) }
17
+ its(:server_name) { should be_nil }
18
+ its(:username) { should be_nil }
19
+ its(:keypair_name) { should be_nil }
20
+ its(:ssh_username) { should be_nil }
21
+ end
22
+
23
+ describe "overriding defaults" do
24
+ [:api_key,
25
+ :endpoint,
26
+ :flavor,
27
+ :image,
28
+ :server_name,
29
+ :username,
30
+ :keypair_name,
31
+ :ssh_username].each do |attribute|
32
+ it "should not default #{attribute} if overridden" do
33
+ subject.send("#{attribute}=".to_sym, "foo")
34
+ subject.finalize!
35
+ subject.send(attribute).should == "foo"
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "validation" do
41
+ let(:machine) { double("machine") }
42
+
43
+ subject do
44
+ super().tap do |o|
45
+ o.finalize!
46
+ end
47
+ end
48
+
49
+ context "with good values" do
50
+ it "should validate"
51
+ end
52
+
53
+ context "the API key" do
54
+ it "should error if not given"
55
+ end
56
+
57
+ context "the public key path" do
58
+ it "should have errors if the key doesn't exist"
59
+ it "should not have errors if the key exists with an absolute path"
60
+ it "should not have errors if the key exists with a relative path"
61
+ end
62
+
63
+ context "the username" do
64
+ it "should error if not given"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vagrant-openstack/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "vagrant-openstack-plugin"
8
+ gem.version = VagrantPlugins::OpenStack::VERSION
9
+ gem.authors = ["Mitchell Hashimoto"]
10
+ gem.email = ["mitchell@hashicorp.com"]
11
+ gem.description = "Enables Vagrant to manage machines in OpenStack Cloud."
12
+ gem.summary = "Enables Vagrant to manage machines in OpenStack Cloud."
13
+ gem.homepage = "http://www.vagrantup.com"
14
+
15
+ gem.add_runtime_dependency "fog", "~> 1.6.0"
16
+
17
+ gem.add_development_dependency "rake"
18
+ gem.add_development_dependency "rspec", "~> 2.13.0"
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-openstack-plugin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mitchell Hashimoto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fog
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.6.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.13.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ description: Enables Vagrant to manage machines in OpenStack Cloud.
63
+ email:
64
+ - mitchell@hashicorp.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - CHANGELOG.md
71
+ - Gemfile
72
+ - LICENSE.txt
73
+ - README.md
74
+ - Rakefile
75
+ - dummy.box
76
+ - example_box/README.md
77
+ - example_box/metadata.json
78
+ - lib/vagrant-openstack.rb
79
+ - lib/vagrant-openstack/action.rb
80
+ - lib/vagrant-openstack/action/connect_openstack.rb
81
+ - lib/vagrant-openstack/action/create_server.rb
82
+ - lib/vagrant-openstack/action/delete_server.rb
83
+ - lib/vagrant-openstack/action/is_created.rb
84
+ - lib/vagrant-openstack/action/message_already_created.rb
85
+ - lib/vagrant-openstack/action/message_not_created.rb
86
+ - lib/vagrant-openstack/action/read_ssh_info.rb
87
+ - lib/vagrant-openstack/action/read_state.rb
88
+ - lib/vagrant-openstack/action/sync_folders.rb
89
+ - lib/vagrant-openstack/action/warn_networks.rb
90
+ - lib/vagrant-openstack/config.rb
91
+ - lib/vagrant-openstack/errors.rb
92
+ - lib/vagrant-openstack/plugin.rb
93
+ - lib/vagrant-openstack/provider.rb
94
+ - lib/vagrant-openstack/version.rb
95
+ - locales/en.yml
96
+ - spec/vagrant-openstack/config_spec.rb
97
+ - vagrant-openstack.gemspec
98
+ homepage: http://www.vagrantup.com
99
+ licenses: []
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: -650629708573648818
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ segments:
120
+ - 0
121
+ hash: -650629708573648818
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 1.8.24
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Enables Vagrant to manage machines in OpenStack Cloud.
128
+ test_files:
129
+ - spec/vagrant-openstack/config_spec.rb