vagrant-openstack-cloud-provider 1.1.4
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.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/.travis.yml +11 -0
- data/AUTHORS +5 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +23 -0
- data/README.md +169 -0
- data/Rakefile +21 -0
- data/dummy.box +0 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/lib/vagrant-openstack-cloud-provider.rb +53 -0
- data/lib/vagrant-openstack-cloud-provider/action.rb +129 -0
- data/lib/vagrant-openstack-cloud-provider/action/connect_openstack.rb +34 -0
- data/lib/vagrant-openstack-cloud-provider/action/create_server.rb +185 -0
- data/lib/vagrant-openstack-cloud-provider/action/delete_server.rb +26 -0
- data/lib/vagrant-openstack-cloud-provider/action/is_created.rb +16 -0
- data/lib/vagrant-openstack-cloud-provider/action/message_already_created.rb +16 -0
- data/lib/vagrant-openstack-cloud-provider/action/message_not_created.rb +16 -0
- data/lib/vagrant-openstack-cloud-provider/action/read_ssh_info_from_api.rb +46 -0
- data/lib/vagrant-openstack-cloud-provider/action/read_ssh_info_from_cache.rb +53 -0
- data/lib/vagrant-openstack-cloud-provider/action/read_state.rb +38 -0
- data/lib/vagrant-openstack-cloud-provider/action/sync_folders.rb +58 -0
- data/lib/vagrant-openstack-cloud-provider/config.rb +126 -0
- data/lib/vagrant-openstack-cloud-provider/errors.rb +35 -0
- data/lib/vagrant-openstack-cloud-provider/plugin.rb +37 -0
- data/lib/vagrant-openstack-cloud-provider/provider.rb +50 -0
- data/lib/vagrant-openstack-cloud-provider/version.rb +5 -0
- data/locales/en.yml +81 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/vagrant-openstack-cloud-provider/action/create_server_spec.rb +89 -0
- data/spec/vagrant-openstack-cloud-provider/action/read_ssh_info_spec.rb +122 -0
- data/spec/vagrant-openstack-cloud-provider/config_spec.rb +81 -0
- data/vagrant-openstack-cloud-provider.gemspec +25 -0
- metadata +123 -0
@@ -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 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
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
class ::Hash
|
5
|
+
def method_missing(name)
|
6
|
+
return self[name] if key? name
|
7
|
+
self.each { |k,v| return v if k.to_s.to_sym == name }
|
8
|
+
super.method_missing name
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module VagrantPlugins
|
13
|
+
module OpenStack
|
14
|
+
module Action
|
15
|
+
# This action reads the SSH info for the machine and puts it into the
|
16
|
+
# `:machine_ssh_info` key in the environment.
|
17
|
+
class ReadSSHInfoFromCache
|
18
|
+
def initialize(app, env)
|
19
|
+
@app = app
|
20
|
+
@logger = Log4r::Logger.new("vagrant_openstack::action::read_ssh_info")
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
ssh_info = read_ssh_info(env[:machine])
|
25
|
+
|
26
|
+
if ssh_info and ssh_info[:host] != nil
|
27
|
+
env[:machine_ssh_info] = ssh_info
|
28
|
+
end
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_ssh_info(machine)
|
33
|
+
return nil if machine.id.nil?
|
34
|
+
|
35
|
+
cached_metadata_file = machine.data_dir.join("cached_metadata")
|
36
|
+
|
37
|
+
@logger.info("Loading cached metadata from #{cached_metadata_file}")
|
38
|
+
server = JSON.load(cached_metadata_file.read) rescue nil
|
39
|
+
|
40
|
+
config = machine.provider_config
|
41
|
+
|
42
|
+
host = server.addresses[config.public_network_name].last['addr'] rescue nil
|
43
|
+
|
44
|
+
return {
|
45
|
+
:host => host,
|
46
|
+
:port => 22,
|
47
|
+
:username => config.ssh_username
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
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,58 @@
|
|
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
|
+
next if data[:disabled]
|
25
|
+
|
26
|
+
# Make sure there is a trailing slash on the host path to
|
27
|
+
# avoid creating an additional directory with rsync
|
28
|
+
hostpath = "#{hostpath}/" if hostpath !~ /\/$/
|
29
|
+
|
30
|
+
env[:ui].info(I18n.t("vagrant_openstack.rsync_folder",
|
31
|
+
:hostpath => hostpath,
|
32
|
+
:guestpath => guestpath))
|
33
|
+
|
34
|
+
# Create the guest path
|
35
|
+
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
|
36
|
+
env[:machine].communicate.sudo(
|
37
|
+
"chown #{ssh_info[:username]} '#{guestpath}'")
|
38
|
+
|
39
|
+
# Rsync over to the guest path using the SSH info
|
40
|
+
command = [
|
41
|
+
"rsync", "--verbose", "--archive", "--compress", "--delete",
|
42
|
+
"-e", "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path][0]}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
|
43
|
+
hostpath,
|
44
|
+
"#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
|
45
|
+
|
46
|
+
r = Vagrant::Util::Subprocess.execute(*command)
|
47
|
+
if r.exit_code != 0
|
48
|
+
raise Errors::RsyncError,
|
49
|
+
:guestpath => guestpath,
|
50
|
+
:hostpath => hostpath,
|
51
|
+
:stderr => r.stderr
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|