vagrant-openstack-provider 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Appraisals +13 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE.txt +22 -0
  7. data/RELEASE.md +15 -0
  8. data/Rakefile +21 -0
  9. data/Vagrantfile +37 -0
  10. data/dummy.box +0 -0
  11. data/example_box/README.md +13 -0
  12. data/example_box/metadata.json +3 -0
  13. data/features/provision.feature +35 -0
  14. data/features/steps/sdk_steps.rb +13 -0
  15. data/features/steps/server_steps.rb +25 -0
  16. data/features/support/env.rb +37 -0
  17. data/features/support/fog_mock.rb +19 -0
  18. data/features/vagrant-openstack.feature +70 -0
  19. data/gemfiles/latest_stable.gemfile +13 -0
  20. data/gemfiles/oldest_current.gemfile +13 -0
  21. data/gemfiles/previous_release.gemfile +13 -0
  22. data/lib/vagrant-openstack.rb +53 -0
  23. data/lib/vagrant-openstack/action.rb +123 -0
  24. data/lib/vagrant-openstack/action/connect_openstack.rb +30 -0
  25. data/lib/vagrant-openstack/action/create_server.rb +134 -0
  26. data/lib/vagrant-openstack/action/delete_server.rb +26 -0
  27. data/lib/vagrant-openstack/action/is_created.rb +16 -0
  28. data/lib/vagrant-openstack/action/message_already_created.rb +16 -0
  29. data/lib/vagrant-openstack/action/message_not_created.rb +16 -0
  30. data/lib/vagrant-openstack/action/read_ssh_info.rb +52 -0
  31. data/lib/vagrant-openstack/action/read_state.rb +40 -0
  32. data/lib/vagrant-openstack/action/sync_folders.rb +125 -0
  33. data/lib/vagrant-openstack/config.rb +229 -0
  34. data/lib/vagrant-openstack/errors.rb +36 -0
  35. data/lib/vagrant-openstack/openstack_client.rb +91 -0
  36. data/lib/vagrant-openstack/plugin.rb +37 -0
  37. data/lib/vagrant-openstack/provider.rb +50 -0
  38. data/lib/vagrant-openstack/version.rb +5 -0
  39. data/locales/en.yml +85 -0
  40. data/spec/vagrant-openstack/config_spec.rb +184 -0
  41. data/stackrc +32 -0
  42. data/vagrant-openstack.gemspec +23 -0
  43. 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 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,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