openstack-vagrant 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjUzZWU1ZjcyOTgwZTg2Yjg1NjMwN2IwNzE0NGM1ZjRmOTY5NThhOA==
5
+ data.tar.gz: !binary |-
6
+ ZTJjMjk1MzcxOWZlOGViODVlNjJmMjA4NDBlOTNmNTIzNjlmYmZhNQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzM2MWU5NDUxODEzYjhkZDk2YjRhYmJhZTk1YzkxMTIxZTBkZjlhNmE3M2E1
10
+ ZDgwMzgxNzMzYmNkYTQ4MGZhZmI3MzViZjQzYWU1ZWRlZTg0ZjMyODUxY2Zm
11
+ OTQxNTBlYzkxZDVlODhjMTAzN2U0ZTBhMjlhZmU0ZmQyNzkxY2M=
12
+ data.tar.gz: !binary |-
13
+ NTU5ZmNlODNmNjkzMmFkMmZlNGViYWNiZDNlMmZiZjQ1MWVjMDM2YThhNTY4
14
+ Y2JiMTU1YWIwZmZlOThhZDMzMDQwMzk1NGEwNDNjMmYwYjBjNTA2YWUzZTQ0
15
+ MzU1NDc2MDYxMzMxNDYxZGYyMjhkMjMwYzM3YjVhYzhhY2U0YzA=
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ .rspec
2
+ .idea
3
+ *.iml
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .vagrant
9
+ .yardoc
10
+ Gemfile.lock
11
+ InstalledFiles
12
+ _yardoc
13
+ coverage
14
+ doc/
15
+ lib/bundler/man
16
+ pkg
17
+ rdoc
18
+ spec/reports
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
22
+ Vagrantfile
23
+ vendor/
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ sudo: false
2
+ rvm:
3
+ - 2.0.0
4
+ deploy:
5
+ provider: rubygems
6
+ api_key:
7
+ secure: D4lNAf8yFYcAJH0A6pmDjSMKxNgYZI2etr1dWeK/IWUOY21Q+G9+QkgeDFvIIzWxyKRCg8ONAmDw5Sav74CJsY+8uDNqxTvfnaJ+l3fRGHJiOpLWvBY/uRh8jGdgtuPf+cVqwYKYZxE2siHh7NI8pOBdAqLVou9CSO1Ab8ZVJdc=
8
+ gem: openstack-vagrant
9
+ on:
10
+ tags: true
11
+ repo: mat128/openstack-vagrant
data/Gemfile ADDED
@@ -0,0 +1,11 @@
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", :tag => 'v1.8.1'
10
+ gem 'simplecov', :require => false
11
+ 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,169 @@
1
+ # Vagrant OpenStack Cloud Provider
2
+ [![Build Status](https://travis-ci.org/mat128/openstack-vagrant.png?branch=master)](https://travis-ci.org/mat128/openstack-vagrant)
3
+
4
+ This is a [Vagrant](http://www.vagrantup.com) 1.2+ plugin that adds an
5
+ [OpenStack Cloud](http://www.openstack.org) provider to Vagrant,
6
+ allowing Vagrant to control and provision machines within an OpenStack
7
+ cloud.
8
+
9
+ This plugin started as a fork of the Vagrant Rackspace provider.
10
+
11
+ **Note:** This plugin requires Vagrant 1.2+. The last version of this plugin supporting Vagrant 1.1 is 0.3.0.
12
+
13
+ ## Features
14
+
15
+ * Boot OpenStack Cloud instances.
16
+ * SSH into the instances.
17
+ * Provision the instances with any built-in Vagrant provisioner.
18
+ * Minimal synced folder support via `rsync`.
19
+ * Create instances with a specific list of networks
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ $ git clone https://github.com/mat128/openstack-vagrant.git
25
+ $ cd openstack-vagrant
26
+ $ gem build openstack-vagrant.gemspec
27
+ $ vagrant plugin install openstack-vagrant-*.gem
28
+ ...
29
+ $ vagrant up --provider=openstack
30
+ ...
31
+ ```
32
+
33
+ Of course prior to doing this, you'll need to obtain an OpenStack-compatible
34
+ box file for Vagrant.
35
+
36
+ ## Quick Start
37
+
38
+ After installing the plugin (instructions above), the quickest way to get
39
+ started is to actually use a dummy OpenStack box and specify all the details
40
+ manually within a `config.vm.provider` block. So first, add the dummy
41
+ box using any name you want:
42
+
43
+ ```
44
+ $ vagrant box add dummy https://github.com/mat128/openstack-vagrant/raw/master/dummy.box
45
+ ...
46
+ ```
47
+
48
+ And then make a Vagrantfile that looks like the following, filling in
49
+ your information where necessary.
50
+
51
+ ```
52
+ require 'openstack-vagrant'
53
+
54
+ Vagrant.configure("2") do |config|
55
+ config.vm.box = "dummy"
56
+
57
+ config.vm.provider :openstack do |os| # e.g.
58
+ os.username = "YOUR USERNAME" # "#{ENV['OS_USERNAME']}"
59
+ os.api_key = "YOUR API KEY" # "#{ENV['OS_PASSWORD']}"
60
+ os.flavor = /m1.tiny/
61
+ os.image = /Ubuntu/
62
+ os.endpoint = "KEYSTONE AUTH URL" # "#{ENV['OS_AUTH_URL']}/tokens"
63
+ os.keypair_name = "YOUR KEYPAIR NAME"
64
+ os.ssh_username = "SSH USERNAME"
65
+ os.public_network_name = "NAME OF THE PUBLIC NETWORK"
66
+ os.networks = %w(net1 net2 net3)
67
+ os.tenant = "NAME OF THE TENANT"
68
+ os.region = "REGION_NAME"
69
+ end
70
+ end
71
+ ```
72
+
73
+ And then run `vagrant up --provider=openstack`.
74
+
75
+ This will start a tiny Ubuntu instance in your OpenStack installation within
76
+ your tenant. And assuming your SSH information was filled in properly
77
+ within your Vagrantfile, SSH and provisioning will work as well.
78
+
79
+ Note that normally a lot of this boilerplate is encoded within the box
80
+ file, but the box file used for the quick start, the "dummy" box, has
81
+ no preconfigured defaults.
82
+
83
+ ## Box Format
84
+
85
+ Every provider in Vagrant must introduce a custom box format. This
86
+ provider introduces `openstack` boxes. You can view an example box in
87
+ the [example_box/ directory](https://github.com/mat128/openstack-vagrant/tree/master/example_box).
88
+ That directory also contains instructions on how to build a box.
89
+
90
+ The box format is basically just the required `metadata.json` file
91
+ along with a `Vagrantfile` that does default settings for the
92
+ provider-specific configuration for this provider.
93
+
94
+ ## Configuration
95
+
96
+ This provider exposes quite a few provider-specific configuration options:
97
+
98
+ * `api_key` - The API key for accessing OpenStack.
99
+ * `flavor` - The server flavor to boot. This can be a string matching
100
+ the exact ID or name of the server, or this can be a regular expression
101
+ to partially match some server flavor.
102
+ * `image` - The server image to boot. This can be a string matching the
103
+ exact ID or name of the image, or this can be a regular expression to
104
+ partially match some image.
105
+ * `endpoint` - The keystone authentication URL of your OpenStack installation.
106
+ * `server_name` - The name of the server within the OpenStack Cloud. This
107
+ defaults to the name of the Vagrant machine (via `config.vm.define`), but
108
+ can be overridden with this.
109
+ * `username` - The username with which to access OpenStack.
110
+ * `keypair_name` - The name of the keypair to access the machine.
111
+ * `ssh_username` - The username to access the machine.
112
+ * `public_network_name` - The name of the public network within your Openstack cluster
113
+ * `networks` - A list -- use %w(net1 net2) -- of networks to configure
114
+ on your instance.
115
+ * `tenant` - The name of the tenant on which the virtual machine should spawn.
116
+
117
+ These can be set like typical provider-specific configuration:
118
+
119
+ ```ruby
120
+ Vagrant.configure("2") do |config|
121
+ # ... other stuff
122
+
123
+ config.vm.provider :openstack do |os|
124
+ os.username = "mitchellh"
125
+ os.api_key = "foobarbaz"
126
+ end
127
+ end
128
+ ```
129
+
130
+ ## Networks
131
+
132
+ Networking features in the form of `config.vm.network` are not
133
+ supported with `vagrant-openstack`, currently. If any of these are
134
+ specified, Vagrant will emit a warning, but will otherwise boot
135
+ the OpenStack server.
136
+
137
+ ## Synced Folders
138
+
139
+ There is minimal support for synced folders. Upon `vagrant up`,
140
+ `vagrant reload`, and `vagrant provision`, the OpenStack provider will use
141
+ `rsync` (if available) to uni-directionally sync the folder to
142
+ the remote machine over SSH.
143
+
144
+ This is good enough for all built-in Vagrant provisioners (shell,
145
+ chef, and puppet) to work!
146
+
147
+ ## Development
148
+
149
+ To work on the `vagrant-openstack` plugin, clone this repository out, and use
150
+ [Bundler](http://gembundler.com) to get the dependencies:
151
+
152
+ ```
153
+ $ bundle
154
+ ```
155
+
156
+ Once you have the dependencies, verify the unit tests pass with `rake`:
157
+
158
+ ```
159
+ $ bundle exec rake
160
+ ```
161
+
162
+ If those pass, you're ready to start developing the plugin. You can test
163
+ the plugin without installing it into your Vagrant environment by just
164
+ creating a `Vagrantfile` in the top level of this directory (it is gitignored)
165
+ that uses it, and uses bundler to execute Vagrant:
166
+
167
+ ```
168
+ $ bundle exec vagrant up --provider=openstack
169
+ ```
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,34 @@
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
+ @logger.info("Connecting to OpenStack Compute...")
20
+ env[:openstack_compute] = Fog::Compute.new({
21
+ :provider => :openstack,
22
+ :openstack_region => config.region,
23
+ :openstack_username => config.username,
24
+ :openstack_api_key => config.api_key,
25
+ :openstack_auth_url => config.endpoint,
26
+ :openstack_tenant => config.tenant
27
+ })
28
+
29
+ @app.call(env)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,185 @@
1
+ require "fog"
2
+ require "log4r"
3
+ require "json"
4
+
5
+ require 'vagrant/util/retryable'
6
+
7
+ module VagrantPlugins
8
+ module OpenStack
9
+ module Action
10
+ # This creates the OpenStack server.
11
+ class CreateServer
12
+ include Vagrant::Util::Retryable
13
+
14
+ def initialize(app, env)
15
+ @app = app
16
+ @logger = Log4r::Logger.new("vagrant_openstack::action::create_server")
17
+ end
18
+
19
+ def server_to_be_available?(server)
20
+ raise if server.state == 'ERROR'
21
+ server.state == 'ACTIVE'
22
+ end
23
+
24
+ def call(env)
25
+ # Get the configs
26
+ config = env[:machine].provider_config
27
+
28
+ if !config.networks.nil? and config.networks.any?
29
+ @logger.info("Connecting to OpenStack Network...")
30
+ env[:openstack_network] = Fog::Network.new({
31
+ :provider => :openstack,
32
+ :openstack_region => config.region,
33
+ :openstack_username => config.username,
34
+ :openstack_api_key => config.api_key,
35
+ :openstack_auth_url => config.endpoint,
36
+ :openstack_tenant => config.tenant
37
+ })
38
+ end
39
+
40
+ # Find the flavor
41
+ env[:ui].info(I18n.t("vagrant_openstack.finding_flavor"))
42
+ flavor = find_matching(env[:openstack_compute].flavors.all, config.flavor)
43
+ raise Errors::NoMatchingFlavor if !flavor
44
+
45
+ # Find the image
46
+ env[:ui].info(I18n.t("vagrant_openstack.finding_image"))
47
+ image = find_matching(env[:openstack_compute].images, config.image)
48
+ raise Errors::NoMatchingImage if !image
49
+
50
+ # Find the networks
51
+ effective_networks = []
52
+ if !config.networks.nil? and config.networks.any?
53
+ env[:ui].info(I18n.t("vagrant_openstack.finding_network"))
54
+ available_networks = env[:openstack_network].list_networks[:body]["networks"]
55
+
56
+ for network_name in config.networks
57
+ match = find_matching(available_networks, network_name)
58
+ unless match
59
+ raise Errors::NoMatchingNetwork,
60
+ :network_name => network_name
61
+ end
62
+ effective_networks << match
63
+ end
64
+ end
65
+
66
+ # Figure out the name for the server
67
+ server_name = config.server_name || env[:machine].name
68
+
69
+ # Output the settings we're going to use to the user
70
+ env[:ui].info(I18n.t("vagrant_openstack.launching_server"))
71
+ env[:ui].info(" -- Flavor: #{flavor.name}")
72
+ env[:ui].info(" -- Image: #{image.name}")
73
+ if effective_networks.any?
74
+ env[:ui].info(' -- Network(s): ')
75
+ for net in effective_networks
76
+ env[:ui].info(" - #{net['name']}")
77
+ end
78
+ end
79
+ env[:ui].info(" -- Name: #{server_name}")
80
+
81
+ openstack_nics = []
82
+
83
+ for net in effective_networks
84
+ openstack_nics << {'net_id' => net['id']}
85
+ end
86
+
87
+ # Build the options for launching...
88
+ options = {
89
+ :flavor_ref => flavor.id,
90
+ :image_ref => image.id,
91
+ :name => server_name,
92
+ :key_name => config.keypair_name,
93
+ :user_data_encoded => Base64.encode64(config.user_data),
94
+ :metadata => config.metadata,
95
+ :os_scheduler_hints => config.scheduler_hints
96
+ }
97
+
98
+ if openstack_nics.any?
99
+ options[:nics] = openstack_nics
100
+ end
101
+
102
+ # Create the server
103
+ server = env[:openstack_compute].servers.create(options)
104
+
105
+ # Store the ID right away so we can track it
106
+ env[:machine].id = server.id
107
+
108
+ # Wait for the server to finish building
109
+ env[:ui].info("Instance UUID: #{env[:machine].id}")
110
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_build"))
111
+ retryable(:on => Timeout::Error, :tries => 200) do
112
+ # If we're interrupted don't worry about waiting
113
+ next if env[:interrupted]
114
+
115
+ # Wait for the server to be ready
116
+ begin
117
+ (1..120).each do |n|
118
+ env[:ui].clear_line
119
+ env[:ui].report_progress(n, 120, true)
120
+ server = env[:openstack_compute].servers.get(env[:machine].id)
121
+ break if self.server_to_be_available?(server)
122
+ sleep 1
123
+ end
124
+ server = env[:openstack_compute].servers.get(env[:machine].id)
125
+ raise unless self.server_to_be_available?(server)
126
+ rescue
127
+ raise Errors::CreateBadState, :state => server.state
128
+ end
129
+ end
130
+
131
+ env[:machine].data_dir.join("cached_metadata").open("w+") do |f|
132
+ f.write(server.to_json)
133
+ end
134
+
135
+ unless env[:interrupted]
136
+ # Clear the line one more time so the progress is removed
137
+ env[:ui].clear_line
138
+ ssh_is_responding?(env)
139
+ env[:ui].info(I18n.t("vagrant_openstack.ready"))
140
+ end
141
+
142
+ @app.call(env)
143
+ end
144
+ protected
145
+
146
+ def ssh_responding?(env)
147
+ begin
148
+ # Wait for SSH to become available
149
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_ssh"))
150
+ (1..60).each do |n|
151
+ begin
152
+ # If we're interrupted then just back out
153
+ break if env[:interrupted]
154
+ break if env[:machine].communicate.ready?
155
+ rescue Errno::ENETUNREACH
156
+ end
157
+ sleep 2
158
+ end
159
+ raise unless env[:machine].communicate.ready?
160
+ rescue
161
+ raise Errors::SshUnavailable
162
+ end
163
+ end
164
+
165
+ # This method finds a matching _thing_ in a collection of
166
+ # _things_. This works matching if the ID or NAME equals to
167
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
168
+ # as well.
169
+ def find_matching(collection, name)
170
+ collection.each do |single|
171
+ if single.is_a?(Hash)
172
+ return single if single['name'] == name
173
+ else
174
+ return single if single.id == name
175
+ return single if single.name == name
176
+ return single if name.is_a?(Regexp) && name =~ single.name
177
+ end
178
+ end
179
+
180
+ nil
181
+ end
182
+ end
183
+ end
184
+ end
185
+ 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 ReadSSHInfoFromAPI
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[config.public_network_name].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