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.
Files changed (35) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +23 -0
  3. data/.travis.yml +11 -0
  4. data/AUTHORS +5 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE.txt +23 -0
  7. data/README.md +169 -0
  8. data/Rakefile +21 -0
  9. data/dummy.box +0 -0
  10. data/example_box/README.md +13 -0
  11. data/example_box/metadata.json +3 -0
  12. data/lib/vagrant-openstack-cloud-provider.rb +53 -0
  13. data/lib/vagrant-openstack-cloud-provider/action.rb +129 -0
  14. data/lib/vagrant-openstack-cloud-provider/action/connect_openstack.rb +34 -0
  15. data/lib/vagrant-openstack-cloud-provider/action/create_server.rb +185 -0
  16. data/lib/vagrant-openstack-cloud-provider/action/delete_server.rb +26 -0
  17. data/lib/vagrant-openstack-cloud-provider/action/is_created.rb +16 -0
  18. data/lib/vagrant-openstack-cloud-provider/action/message_already_created.rb +16 -0
  19. data/lib/vagrant-openstack-cloud-provider/action/message_not_created.rb +16 -0
  20. data/lib/vagrant-openstack-cloud-provider/action/read_ssh_info_from_api.rb +46 -0
  21. data/lib/vagrant-openstack-cloud-provider/action/read_ssh_info_from_cache.rb +53 -0
  22. data/lib/vagrant-openstack-cloud-provider/action/read_state.rb +38 -0
  23. data/lib/vagrant-openstack-cloud-provider/action/sync_folders.rb +58 -0
  24. data/lib/vagrant-openstack-cloud-provider/config.rb +126 -0
  25. data/lib/vagrant-openstack-cloud-provider/errors.rb +35 -0
  26. data/lib/vagrant-openstack-cloud-provider/plugin.rb +37 -0
  27. data/lib/vagrant-openstack-cloud-provider/provider.rb +50 -0
  28. data/lib/vagrant-openstack-cloud-provider/version.rb +5 -0
  29. data/locales/en.yml +81 -0
  30. data/spec/spec_helper.rb +21 -0
  31. data/spec/vagrant-openstack-cloud-provider/action/create_server_spec.rb +89 -0
  32. data/spec/vagrant-openstack-cloud-provider/action/read_ssh_info_spec.rb +122 -0
  33. data/spec/vagrant-openstack-cloud-provider/config_spec.rb +81 -0
  34. data/vagrant-openstack-cloud-provider.gemspec +25 -0
  35. 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 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
@@ -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