vagrant-openstack-provider 0.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.
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