vagrant-openstack-provider 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Appraisals +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +22 -0
- data/RELEASE.md +15 -0
- data/Rakefile +21 -0
- data/Vagrantfile +37 -0
- data/dummy.box +0 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/features/provision.feature +35 -0
- data/features/steps/sdk_steps.rb +13 -0
- data/features/steps/server_steps.rb +25 -0
- data/features/support/env.rb +37 -0
- data/features/support/fog_mock.rb +19 -0
- data/features/vagrant-openstack.feature +70 -0
- data/gemfiles/latest_stable.gemfile +13 -0
- data/gemfiles/oldest_current.gemfile +13 -0
- data/gemfiles/previous_release.gemfile +13 -0
- data/lib/vagrant-openstack.rb +53 -0
- data/lib/vagrant-openstack/action.rb +123 -0
- data/lib/vagrant-openstack/action/connect_openstack.rb +30 -0
- data/lib/vagrant-openstack/action/create_server.rb +134 -0
- data/lib/vagrant-openstack/action/delete_server.rb +26 -0
- data/lib/vagrant-openstack/action/is_created.rb +16 -0
- data/lib/vagrant-openstack/action/message_already_created.rb +16 -0
- data/lib/vagrant-openstack/action/message_not_created.rb +16 -0
- data/lib/vagrant-openstack/action/read_ssh_info.rb +52 -0
- data/lib/vagrant-openstack/action/read_state.rb +40 -0
- data/lib/vagrant-openstack/action/sync_folders.rb +125 -0
- data/lib/vagrant-openstack/config.rb +229 -0
- data/lib/vagrant-openstack/errors.rb +36 -0
- data/lib/vagrant-openstack/openstack_client.rb +91 -0
- data/lib/vagrant-openstack/plugin.rb +37 -0
- data/lib/vagrant-openstack/provider.rb +50 -0
- data/lib/vagrant-openstack/version.rb +5 -0
- data/locales/en.yml +85 -0
- data/spec/vagrant-openstack/config_spec.rb +184 -0
- data/stackrc +32 -0
- data/vagrant-openstack.gemspec +23 -0
- metadata +135 -0
@@ -0,0 +1,123 @@
|
|
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 when `vagrant provision` is called.
|
28
|
+
def self.action_provision
|
29
|
+
Vagrant::Action::Builder.new.tap do |b|
|
30
|
+
b.use ConfigValidate
|
31
|
+
b.use Call, IsCreated do |env, b2|
|
32
|
+
if !env[:result]
|
33
|
+
b2.use MessageNotCreated
|
34
|
+
next
|
35
|
+
end
|
36
|
+
|
37
|
+
b2.use Provision
|
38
|
+
b2.use SyncFolders
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# This action is called to read the SSH info of the machine. The
|
44
|
+
# resulting state is expected to be put into the `:machine_ssh_info`
|
45
|
+
# key.
|
46
|
+
def self.action_read_ssh_info
|
47
|
+
Vagrant::Action::Builder.new.tap do |b|
|
48
|
+
b.use ConfigValidate
|
49
|
+
b.use ConnectOpenstack
|
50
|
+
b.use ReadSSHInfo
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# This action is called to read the state of the machine. The
|
55
|
+
# resulting state is expected to be put into the `:machine_state_id`
|
56
|
+
# key.
|
57
|
+
def self.action_read_state
|
58
|
+
Vagrant::Action::Builder.new.tap do |b|
|
59
|
+
b.use ConfigValidate
|
60
|
+
b.use ConnectOpenstack
|
61
|
+
b.use ReadState
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.action_ssh
|
66
|
+
Vagrant::Action::Builder.new.tap do |b|
|
67
|
+
b.use ConfigValidate
|
68
|
+
b.use Call, IsCreated do |env, b2|
|
69
|
+
if !env[:result]
|
70
|
+
b2.use MessageNotCreated
|
71
|
+
next
|
72
|
+
end
|
73
|
+
|
74
|
+
b2.use SSHExec
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.action_ssh_run
|
80
|
+
Vagrant::Action::Builder.new.tap do |b|
|
81
|
+
b.use ConfigValidate
|
82
|
+
b.use Call, IsCreated do |env, b2|
|
83
|
+
if !env[:result]
|
84
|
+
b2.use MessageNotCreated
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
b2.use SSHRun
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.action_up
|
94
|
+
Vagrant::Action::Builder.new.tap do |b|
|
95
|
+
b.use ConfigValidate
|
96
|
+
b.use Call, IsCreated do |env, b2|
|
97
|
+
if env[:result]
|
98
|
+
b2.use MessageAlreadyCreated
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
b2.use ConnectOpenstack
|
103
|
+
b2.use Provision
|
104
|
+
b2.use SyncFolders
|
105
|
+
b2.use CreateServer
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# The autoload farm
|
111
|
+
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
112
|
+
autoload :ConnectOpenstack, action_root.join("connect_openstack")
|
113
|
+
autoload :CreateServer, action_root.join("create_server")
|
114
|
+
autoload :DeleteServer, action_root.join("delete_server")
|
115
|
+
autoload :IsCreated, action_root.join("is_created")
|
116
|
+
autoload :MessageAlreadyCreated, action_root.join("message_already_created")
|
117
|
+
autoload :MessageNotCreated, action_root.join("message_not_created")
|
118
|
+
autoload :ReadSSHInfo, action_root.join("read_ssh_info")
|
119
|
+
autoload :ReadState, action_root.join("read_state")
|
120
|
+
autoload :SyncFolders, action_root.join("sync_folders")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "fog"
|
2
|
+
require "log4r"
|
3
|
+
require "restclient"
|
4
|
+
require "json"
|
5
|
+
require_relative "../openstack_client"
|
6
|
+
|
7
|
+
module VagrantPlugins
|
8
|
+
module Openstack
|
9
|
+
module Action
|
10
|
+
# This action connects to Openstack, verifies credentials work, and
|
11
|
+
# puts the Openstack connection object into the `:openstack_compute` key
|
12
|
+
# in the environment.
|
13
|
+
class ConnectOpenstack
|
14
|
+
def initialize(app, env)
|
15
|
+
@app = app
|
16
|
+
@logger = Log4r::Logger.new("vagrant_openstack::action::connect_openstack")
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
# Get the configs
|
21
|
+
config = env[:machine].provider_config
|
22
|
+
client = OpenstackClient::new()
|
23
|
+
env[:openstack_client] = client
|
24
|
+
client.authenticate(env)
|
25
|
+
@app.call(env)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "fog"
|
2
|
+
require "log4r"
|
3
|
+
require 'socket'
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
require 'vagrant/util/retryable'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module Openstack
|
10
|
+
module Action
|
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 call(env)
|
20
|
+
config = env[:machine].provider_config
|
21
|
+
client = env[:openstack_client]
|
22
|
+
|
23
|
+
# Find the flavor
|
24
|
+
env[:ui].info(I18n.t("vagrant_openstack.finding_flavor"))
|
25
|
+
flavors = client.get_all_flavors(env)
|
26
|
+
flavor = find_matching(flavors, config.flavor)
|
27
|
+
raise Errors::NoMatchingFlavor if !flavor
|
28
|
+
|
29
|
+
# Find the image
|
30
|
+
env[:ui].info(I18n.t("vagrant_openstack.finding_image"))
|
31
|
+
images = client.get_all_images(env)
|
32
|
+
image = find_matching(images, config.image)
|
33
|
+
raise Errors::NoMatchingImage if !image
|
34
|
+
|
35
|
+
# Figure out the name for the server
|
36
|
+
server_name = config.server_name || env[:machine].name
|
37
|
+
|
38
|
+
# Output the settings we're going to use to the user
|
39
|
+
env[:ui].info(I18n.t("vagrant_openstack.launching_server"))
|
40
|
+
env[:ui].info(" -- Flavor : #{flavor.name}")
|
41
|
+
env[:ui].info(" -- FlavorRef : #{flavor.id}")
|
42
|
+
env[:ui].info(" -- Image : #{image.name}")
|
43
|
+
env[:ui].info(" -- KeyPair : #{config.keypair_name}")
|
44
|
+
env[:ui].info(" -- ImageRef : #{image.id}")
|
45
|
+
env[:ui].info(" -- Disk Config : #{config.disk_config}") if config.disk_config
|
46
|
+
env[:ui].info(" -- Network : #{config.network}") if config.network
|
47
|
+
env[:ui].info(" -- Tenant : #{config.tenant_name}")
|
48
|
+
env[:ui].info(" -- Name : #{server_name}")
|
49
|
+
|
50
|
+
#TODO(julienvey) add metadata
|
51
|
+
#TODO(julienvey) add availability_zone
|
52
|
+
#TODO(julienvey) add disk_config
|
53
|
+
|
54
|
+
server_id = client.create_server(env, server_name, image.id, flavor.id, config.keypair_name)
|
55
|
+
|
56
|
+
#TODO(julienvey) Find a network if provided
|
57
|
+
#if config.network
|
58
|
+
# network = find_matching(env[:openstack_network].networks, config.network)
|
59
|
+
# options[:nics] = [{"net_id" => network.id}] if network
|
60
|
+
#end
|
61
|
+
|
62
|
+
# Store the ID right away so we can track it
|
63
|
+
env[:machine].id = server_id
|
64
|
+
|
65
|
+
# Wait for the server to finish building
|
66
|
+
env[:ui].info(I18n.t("vagrant_openstack.waiting_for_build"))
|
67
|
+
timeout(200) do
|
68
|
+
while client.get_server_details(env, server_id)['status'] != 'ACTIVE' do
|
69
|
+
sleep 3
|
70
|
+
@logger.debug("Waiting for server to be ACTIVE")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if config.floating_ip
|
75
|
+
env[:ui].info("Using floating IP #{config.floating_ip}")
|
76
|
+
client.add_floating_ip(env, server_id, config.floating_ip)
|
77
|
+
end
|
78
|
+
|
79
|
+
if !env[:interrupted]
|
80
|
+
# Clear the line one more time so the progress is removed
|
81
|
+
env[:ui].clear_line
|
82
|
+
|
83
|
+
# Wait for SSH to become available
|
84
|
+
host = env[:machine].provider_config.floating_ip
|
85
|
+
ssh_timeout = env[:machine].provider_config.ssh_timeout
|
86
|
+
sleep 240
|
87
|
+
if !port_open?(env, host, 22, ssh_timeout)
|
88
|
+
env[:ui].error(I18n.t("vagrant_openstack.timeout"))
|
89
|
+
raise Errors::SshUnavailable,
|
90
|
+
:host => host,
|
91
|
+
:timeout => ssh_timeout
|
92
|
+
end
|
93
|
+
|
94
|
+
env[:ui].info(I18n.t("vagrant_openstack.ready"))
|
95
|
+
end
|
96
|
+
|
97
|
+
@app.call(env)
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
def port_open?(env, ip, port, timeout)
|
103
|
+
start_time = Time.now
|
104
|
+
current_time = start_time
|
105
|
+
while (current_time - start_time) <= timeout
|
106
|
+
begin
|
107
|
+
env[:ui].info(I18n.t("vagrant_openstack.waiting_for_ssh"))
|
108
|
+
TCPSocket.new(ip, port)
|
109
|
+
return true
|
110
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
111
|
+
sleep 1
|
112
|
+
end
|
113
|
+
current_time = Time.now
|
114
|
+
end
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
|
118
|
+
# This method finds a matching _thing_ in a collection of
|
119
|
+
# _things_. This works matching if the ID or NAME equals to
|
120
|
+
# `name`. Or, if `name` is a regexp, a partial match is chosen
|
121
|
+
# as well.
|
122
|
+
def find_matching(collection, name)
|
123
|
+
collection.each do |single|
|
124
|
+
return single if single.id == name
|
125
|
+
return single if single.name == name
|
126
|
+
return single if name.is_a?(Regexp) && name =~ single.name
|
127
|
+
end
|
128
|
+
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
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,52 @@
|
|
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)
|
16
|
+
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_ssh_info(env)
|
21
|
+
client = env[:openstack_client]
|
22
|
+
machine = env[:machine]
|
23
|
+
config = env[:machine].provider_config
|
24
|
+
@logger.debug(config)
|
25
|
+
return nil if machine.id.nil?
|
26
|
+
begin
|
27
|
+
details = client.get_server_details(env, machine.id)
|
28
|
+
rescue Exception => e
|
29
|
+
# The machine can't be found
|
30
|
+
@logger.error(e)
|
31
|
+
@logger.info("Machine couldn't be found, assuming it got destroyed.")
|
32
|
+
machine.id = nil
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
|
36
|
+
for addr in details['addresses']['private']
|
37
|
+
if addr['OS-EXT-IPS:type'] == 'floating'
|
38
|
+
host = addr['addr']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
return {
|
43
|
+
# Usually there should only be one public IP
|
44
|
+
:host => host,
|
45
|
+
:port => 22,
|
46
|
+
:username => config.ssh_username
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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)
|
16
|
+
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_state(env)
|
21
|
+
machine = env[:machine]
|
22
|
+
client = env[:openstack_client]
|
23
|
+
return :not_created if machine.id.nil?
|
24
|
+
|
25
|
+
# Find the machine
|
26
|
+
server = client.get_server_details(env, machine.id)
|
27
|
+
if server.nil? || server['status'] == "DELETED"
|
28
|
+
# The machine can't be found
|
29
|
+
@logger.info("Machine not found or deleted, assuming it got destroyed.")
|
30
|
+
machine.id = nil
|
31
|
+
return :not_created
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the state
|
35
|
+
return server['status'].downcase.to_sym
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|