vagrant-openstack-plugin-tom 0.12.0

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +11 -0
  4. data/Authors.txt +11 -0
  5. data/CHANGELOG.md +185 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.txt +23 -0
  8. data/README.md +278 -0
  9. data/Rakefile +21 -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/example_vagrant_file +24 -0
  14. data/lib/vagrant-openstack-plugin.rb +53 -0
  15. data/lib/vagrant-openstack-plugin/action.rb +268 -0
  16. data/lib/vagrant-openstack-plugin/action/connect_openstack.rb +90 -0
  17. data/lib/vagrant-openstack-plugin/action/create_network_interfaces.rb +52 -0
  18. data/lib/vagrant-openstack-plugin/action/create_orchestration_stack.rb +97 -0
  19. data/lib/vagrant-openstack-plugin/action/create_server.rb +263 -0
  20. data/lib/vagrant-openstack-plugin/action/delete_orchestration_stack.rb +78 -0
  21. data/lib/vagrant-openstack-plugin/action/delete_server.rb +84 -0
  22. data/lib/vagrant-openstack-plugin/action/hard_reboot_server.rb +27 -0
  23. data/lib/vagrant-openstack-plugin/action/is_created.rb +16 -0
  24. data/lib/vagrant-openstack-plugin/action/is_paused.rb +16 -0
  25. data/lib/vagrant-openstack-plugin/action/is_snapshoting.rb +24 -0
  26. data/lib/vagrant-openstack-plugin/action/is_suspended.rb +16 -0
  27. data/lib/vagrant-openstack-plugin/action/message_already_created.rb +16 -0
  28. data/lib/vagrant-openstack-plugin/action/message_already_paused.rb +16 -0
  29. data/lib/vagrant-openstack-plugin/action/message_already_suspended.rb +16 -0
  30. data/lib/vagrant-openstack-plugin/action/message_not_created.rb +16 -0
  31. data/lib/vagrant-openstack-plugin/action/message_server_running.rb +16 -0
  32. data/lib/vagrant-openstack-plugin/action/message_snapshot_done.rb +16 -0
  33. data/lib/vagrant-openstack-plugin/action/message_snapshot_in_progress.rb +16 -0
  34. data/lib/vagrant-openstack-plugin/action/message_will_not_destroy.rb +16 -0
  35. data/lib/vagrant-openstack-plugin/action/pause_server.rb +27 -0
  36. data/lib/vagrant-openstack-plugin/action/read_ssh_info.rb +103 -0
  37. data/lib/vagrant-openstack-plugin/action/read_state.rb +39 -0
  38. data/lib/vagrant-openstack-plugin/action/reboot_server.rb +27 -0
  39. data/lib/vagrant-openstack-plugin/action/resume_server.rb +31 -0
  40. data/lib/vagrant-openstack-plugin/action/suspend_server.rb +27 -0
  41. data/lib/vagrant-openstack-plugin/action/sync_folders.rb +104 -0
  42. data/lib/vagrant-openstack-plugin/action/take_snapshot.rb +26 -0
  43. data/lib/vagrant-openstack-plugin/action/wait_for_state.rb +39 -0
  44. data/lib/vagrant-openstack-plugin/action/wait_for_task.rb +44 -0
  45. data/lib/vagrant-openstack-plugin/action/warn_networks.rb +19 -0
  46. data/lib/vagrant-openstack-plugin/command.rb +70 -0
  47. data/lib/vagrant-openstack-plugin/command/command_snapshot.rb +43 -0
  48. data/lib/vagrant-openstack-plugin/config.rb +246 -0
  49. data/lib/vagrant-openstack-plugin/errors.rb +71 -0
  50. data/lib/vagrant-openstack-plugin/plugin.rb +45 -0
  51. data/lib/vagrant-openstack-plugin/provider.rb +50 -0
  52. data/lib/vagrant-openstack-plugin/version.rb +5 -0
  53. data/locales/en.yml +154 -0
  54. data/spec/vagrant-openstack-plugin/config_spec.rb +152 -0
  55. data/vagrant-openstack-plugin.gemspec +24 -0
  56. metadata +142 -0
@@ -0,0 +1,52 @@
1
+ require "log4r"
2
+
3
+ require 'vagrant/util/scoped_hash_override'
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ class CreateNetworkInterfaces
9
+ include Vagrant::Util::ScopedHashOverride
10
+
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_openstack::action::create_network_interfaces")
14
+ end
15
+
16
+ def call(env)
17
+ networks_to_configure = []
18
+ env[:machine].config.vm.networks.each_with_index do |network, slot_number|
19
+ type = network[0]
20
+ original_options = network[1]
21
+ next if type != :private_network
22
+ next if original_options[:auto_config] === false
23
+ next if slot_number == 0
24
+
25
+ options = scoped_hash_override(original_options, :openstack)
26
+
27
+ @logger.info "Configuring interface slot_number #{slot_number} options #{options}"
28
+
29
+ network_to_configure = {
30
+ :interface => slot_number,
31
+ }
32
+
33
+ if options[:ip]
34
+ network_to_configure = {
35
+ :type => :static,
36
+ :ip => options[:ip],
37
+ :netmask => "255.255.255.0",
38
+ }.merge(network_to_configure)
39
+ else
40
+ network_to_configure[:type] = :dhcp
41
+ end
42
+
43
+ networks_to_configure.push(network_to_configure)
44
+ end
45
+ env[:ui].info I18n.t('vagrant.actions.vm.network.configuring')
46
+ env[:machine].guest.capability(:configure_networks, networks_to_configure)
47
+ @app.call(env)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,97 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module OpenStack
6
+ module Action
7
+ class CreateOrchestrationStack
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new(
12
+ "vagrant_openstack::action::create_orchestration_stack")
13
+ end
14
+
15
+ def call(env)
16
+ # Get the config.
17
+ config = env[:machine].provider_config
18
+
19
+ # Are we going to handle orchestration stacks?
20
+ if not config.orchestration_stack_name
21
+ return @app.call(env)
22
+ end
23
+
24
+ # Create new fog orchestration service.
25
+ env[:openstack_orchestration] = Fog::Orchestration.new(
26
+ env[:fog_openstack_params])
27
+
28
+ # Check if stack is already created.
29
+ env[:openstack_orchestration].list_stacks.body['stacks'].each do |stack|
30
+ if config.orchestration_stack_name == stack['stack_name']
31
+ return @app.call(env)
32
+ end
33
+ end
34
+
35
+ # To avoid confusion, only one source for orchestration template
36
+ # should be set.
37
+ if [config.orchestration_cfn_template,
38
+ config.orchestration_cfn_template_file,
39
+ config.orchestration_cfn_template_url].count(nil) != 2
40
+ raise Errors::OrchestrationTemplateError,
41
+ :err => 'One source for orchestration template should be specified.'
42
+ end
43
+
44
+ # Prepare parameters for new orchestration stack.
45
+ # TODO: configurable parameters
46
+ stack_params = {
47
+ :disable_rollback => false,
48
+ :timeout_in_minutes => 5,
49
+ }
50
+
51
+ # Set template source.
52
+ if config.orchestration_cfn_template
53
+ stack_params[:template] = config.orchestration_cfn_template
54
+ elsif config.orchestration_cfn_template_file
55
+ if not File.exist?(config.orchestration_cfn_template_file)
56
+ raise Errors::OrchestrationNoTemplateFileError,
57
+ :fname => config.orchestration_cfn_template_file
58
+ end
59
+
60
+ # Load template file content. Newlines can cause parse error of
61
+ # input JSON string.
62
+ stack_params[:template] = ''
63
+ File.open(config.orchestration_cfn_template_file) { |file|
64
+ file.each_line do |line|
65
+ stack_params[:template] << line
66
+ end
67
+ }
68
+ else
69
+ stack_params[:template_url] = config.orchestration_cfn_template_url
70
+ end
71
+
72
+ # Set template parameters.
73
+ stack_params[:parameters] = config.orchestration_cfn_template_parameters
74
+
75
+ # Create new stack.
76
+ env[:ui].info(I18n.t("vagrant_openstack.creating_orchestration_stack"))
77
+ stack = env[:openstack_orchestration].create_stack(
78
+ config.orchestration_stack_name, stack_params)
79
+
80
+ # Write UUID of newly created stack into file for later use (stack removal).
81
+ created_stacks_fname = env[:machine].data_dir + 'orchestration_stacks'
82
+ message = 'Saving information about created orchestration stack '
83
+ message << "#{config.orchestration_stack_name}, "
84
+ message << "UUID=#{stack.body['stack']['id']} "
85
+ message << "to file #{created_stacks_fname}."
86
+ @logger.info(message)
87
+
88
+ File.open(created_stacks_fname, 'a') do |file|
89
+ file.puts stack.body['stack']['id']
90
+ end
91
+
92
+ @app.call(env)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,263 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module OpenStack
8
+ module Action
9
+ # This creates the OpenStack server.
10
+ class CreateServer
11
+ include Vagrant::Util::Retryable
12
+
13
+ def initialize(app, env)
14
+ @app = app
15
+ @logger = Log4r::Logger.new("vagrant_openstack::action::create_server")
16
+ end
17
+
18
+ def call(env)
19
+ # Get the configs
20
+ config = env[:machine].provider_config
21
+
22
+ # Find the flavor
23
+ env[:ui].info(I18n.t("vagrant_openstack.finding_flavor"))
24
+ flavor = find_matching(env[:openstack_compute].flavors.all, config.flavor)
25
+ raise Errors::NoMatchingFlavor if !flavor
26
+
27
+ # Find the image
28
+ env[:ui].info(I18n.t("vagrant_openstack.finding_image"))
29
+ image = find_matching(env[:openstack_compute].images, config.image)
30
+ raise Errors::NoMatchingImage if !image
31
+
32
+ # Figure out the name for the server
33
+ server_name = config.server_name || env[:machine].name
34
+
35
+ # Build the options for launching...
36
+ options = {
37
+ :flavor_ref => flavor.id,
38
+ :image_ref => image.id,
39
+ :name => server_name,
40
+ :key_name => config.keypair_name,
41
+ :metadata => config.metadata,
42
+ :user_data => config.user_data,
43
+ :security_groups => config.security_groups,
44
+ :os_scheduler_hints => config.scheduler_hints,
45
+ :availability_zone => config.availability_zone
46
+ }
47
+
48
+ # Fallback to only one network, otherwise `config.networks` overrides
49
+ unless config.networks
50
+ if config.network
51
+ config.networks = [ config.network ]
52
+ else
53
+ config.networks = []
54
+ end
55
+ end
56
+
57
+ # Find networks if provided
58
+ unless config.networks.empty?
59
+ env[:ui].info(I18n.t("vagrant_openstack.finding_network"))
60
+ options[:nics] = Array.new
61
+ config.networks.each_with_index do |os_network_name, i|
62
+
63
+ # Use the configured OpenStack network, if it exists.
64
+ os_network = find_matching(env[:openstack_network].networks, os_network_name)
65
+ if os_network
66
+ current = { :net_id => os_network.id }
67
+
68
+ # Match the OpenStack network to a corresponding
69
+ # config.vm.network option. If there is one, use that for its
70
+ # IP address.
71
+ config_network = env[:machine].config.vm.networks[i]
72
+ if config_network
73
+ ip_address = config_network[1][:ip]
74
+ current[:v4_fixed_ip] = ip_address if ip_address
75
+ end
76
+
77
+ options[:nics] << current
78
+ end
79
+ end
80
+ env[:ui].info("options[:nics]: #{options[:nics]}")
81
+ end
82
+
83
+ # Output the settings we're going to use to the user
84
+ env[:ui].info(I18n.t("vagrant_openstack.launching_server"))
85
+ env[:ui].info(" -- Flavor: #{flavor.name}")
86
+ env[:ui].info(" -- Image: #{image.name}")
87
+ env[:ui].info(" -- Name: #{server_name}")
88
+ config.networks.each do |n|
89
+ env[:ui].info(" -- Network: #{n}")
90
+ end
91
+ if config.security_groups
92
+ env[:ui].info(" -- Security Groups: #{config.security_groups}")
93
+ end
94
+
95
+ # Create the server
96
+ server = env[:openstack_compute].servers.create(options)
97
+
98
+ # Store the ID right away so we can track it
99
+ env[:machine].id = server.id
100
+
101
+ # Wait for the server to finish building
102
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_build"))
103
+ retryable(:on => Fog::Errors::TimeoutError, :tries => 200) do
104
+ # If we're interrupted don't worry about waiting
105
+ next if env[:interrupted]
106
+
107
+ # Set the progress
108
+ env[:ui].clear_line
109
+ env[:ui].report_progress(server.progress, 100, false)
110
+
111
+ # Wait for the server to be ready
112
+ begin
113
+ server.wait_for(5) { ready? }
114
+ # Once the server is up and running assign a floating IP if we have one
115
+ floating_ip = config.floating_ip
116
+ # try to automatically allocate and associate a floating IP
117
+ if floating_ip && floating_ip.to_sym == :auto
118
+ if config.floating_ip_pool
119
+ env[:ui].info("Allocating floating IP address from pool: #{config.floating_ip_pool}")
120
+ address = env[:openstack_compute].allocate_address(config.floating_ip_pool).body["floating_ip"]
121
+ if address["ip"].nil?
122
+ raise Errors::FloatingIPNotAllocated
123
+ else
124
+ floating_ip = address["ip"]
125
+ end
126
+ else
127
+ addresses = env[:openstack_compute].addresses
128
+ free_floating = addresses.find_index {|a| a.fixed_ip.nil?}
129
+ if free_floating.nil?
130
+ raise Errors::FloatingIPNotFound
131
+ else
132
+ floating_ip = addresses[free_floating].ip
133
+ end
134
+ end
135
+ end
136
+
137
+ # try to automatically associate the next available unassigned ip in the given pool
138
+ if floating_ip && floating_ip.to_sym == :associate_unassigned
139
+ if config.floating_ip_pool
140
+ env[:ui].info("Associating floating IP address from pool: #{config.floating_ip_pool}")
141
+ addresses = env[:openstack_compute].addresses
142
+
143
+ # grab the next available IP in this pool which is not currently allocated:
144
+ address = env[:openstack_compute].addresses.find { |thisone|
145
+ (thisone.attributes[:pool].eql? config.floating_ip_pool and thisone.attributes[:instance_id].nil?)
146
+ }
147
+
148
+ if address.nil?
149
+ raise Errors::FloatingUnassignedIPNotFound
150
+ else
151
+ floating_ip = address.attributes[:ip]
152
+ end
153
+ result = env[:openstack_compute].associate_address(server.id,floating_ip)
154
+ if result[:status] != 202
155
+ raise Errors::FloatingIPFailedAssociate
156
+ else
157
+ env[:ui].info("Found and Associated floating IP address #{address.attributes[:ip]} from pool #{config.floating_ip_pool}")
158
+ end
159
+ else
160
+ raise Errors::FloatingUnassignedRequiresPool
161
+ end
162
+ end
163
+
164
+ if floating_ip
165
+ floater = env[:openstack_compute].addresses.find { |thisone| thisone.ip.eql? floating_ip }
166
+ floater.server = server
167
+ end
168
+
169
+ # Process disks if provided
170
+ volumes = Array.new
171
+ if config.disks && !config.disks.empty?
172
+ env[:ui].info(I18n.t("vagrant_openstack.creating_disks"))
173
+ config.disks.each do |disk|
174
+ volume = env[:openstack_compute].volumes.all.find{|v| v.name ==
175
+ disk["name"] and
176
+ v.description ==
177
+ disk["description"] and
178
+ v.size ==
179
+ disk["size"] and
180
+ v.ready? }
181
+ if volume
182
+ env[:ui].info("re-using volume: #{disk["name"]}")
183
+ disk["volume_id"] = volume.id
184
+ else
185
+ env[:ui].info("creating volume: #{disk["name"]}")
186
+ disk["volume_id"] = env[:openstack_compute].create_volume(
187
+ disk["name"], disk["description"], disk["size"]).\
188
+ data[:body]["volume"]["id"]
189
+ volumes << { :id => disk["volume_id"] }
190
+ end
191
+
192
+ # mount points are not expected to be meaningful
193
+ # add useful support if your cloud respects them
194
+ begin
195
+ # Assume instance has only one disk
196
+ nova_offset = 1
197
+ # Increase counter in case we have swap or ephemeral disks.
198
+ if flavor.swap != 0
199
+ nova_offset += 1
200
+ end
201
+ if flavor.ephemeral != 0
202
+ nova_offset += 1
203
+ end
204
+ server.attach_volume(disk["volume_id"], "/dev/vd#{("c".."z").to_a[server.volume_attachments.length + nova_offset]}")
205
+ server.wait_for{ volume_attachments.any?{|vol| vol["id"]==disk["volume_id"]} }
206
+ rescue Excon::Errors::Error => e
207
+ raise Errors::VolumeBadState, :volume => disk["name"], :state => e.message
208
+ end
209
+ end
210
+ end
211
+
212
+ # store this so we can use it later
213
+ env[:floating_ip] = floating_ip
214
+
215
+ rescue RuntimeError => e
216
+ # If we don't have an error about a state transition, then
217
+ # we just move on.
218
+ raise if e.message !~ /should have transitioned/
219
+ raise Errors::CreateBadState, :state => server.state.downcase
220
+ end
221
+ end
222
+
223
+ if !env[:interrupted]
224
+ # Clear the line one more time so the progress is removed
225
+ env[:ui].clear_line
226
+
227
+ # Wait for SSH to become available
228
+ env[:ui].info(I18n.t("vagrant_openstack.waiting_for_ssh"))
229
+ while true
230
+ begin
231
+ # If we're interrupted then just back out
232
+ break if env[:interrupted]
233
+ break if env[:machine].communicate.ready?
234
+ rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH
235
+ end
236
+ sleep 2
237
+ end
238
+
239
+ env[:ui].info(I18n.t("vagrant_openstack.ready"))
240
+ end
241
+
242
+ @app.call(env)
243
+ end
244
+
245
+ protected
246
+
247
+ # This method finds a matching _thing_ in a collection of
248
+ # _things_. This works matching if the ID or NAME equals to
249
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
250
+ # as well.
251
+ def find_matching(collection, name)
252
+ collection.each do |single|
253
+ return single if single.id == name
254
+ return single if single.name == name
255
+ return single if name.is_a?(Regexp) && name =~ single.name
256
+ end
257
+
258
+ nil
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,78 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module OpenStack
6
+ module Action
7
+ class DeleteOrchestrationStack
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new(
12
+ "vagrant_openstack::action::delete_orchestration_stack")
13
+ end
14
+
15
+ def call(env)
16
+ # Get the config.
17
+ config = env[:machine].provider_config
18
+
19
+ # Load IDs for orchestration stacks created by vagrant and this
20
+ # project.
21
+ created_stacks_fname = env[:machine].data_dir + 'orchestration_stacks'
22
+
23
+ # Return if no action is needed.
24
+ if not config.orchestration_stack_destroy or not File.exist?(created_stacks_fname)
25
+ env[:machine].id = nil
26
+ return @app.call(env)
27
+ end
28
+
29
+ # Create new fog orchestration service.
30
+ env[:openstack_orchestration] = Fog::Orchestration.new(
31
+ env[:fog_openstack_params])
32
+
33
+ # Load IDs of stacks to be deleted.
34
+ available_stacks = env[:openstack_orchestration].list_stacks.body['stacks']
35
+ stacks_to_delete = []
36
+ File.open(created_stacks_fname) { |file|
37
+ file.each_line do |stack_id|
38
+ stack = find_stack(available_stacks, stack_id.chomp!)
39
+ next if not stack
40
+ stacks_to_delete << stack
41
+ end
42
+ }
43
+
44
+ # Delete stacks.
45
+ if stacks_to_delete.length > 0
46
+ env[:ui].info(I18n.t("vagrant_openstack.deleting_orchestration_stacks"))
47
+ end
48
+
49
+ stacks_to_delete.each do |stack|
50
+ @logger.info("Removing orchestration stack #{stack['stack_name']} (#{stack['id']}).")
51
+ env[:openstack_orchestration].delete_stack(
52
+ stack['stack_name'], stack['id'])
53
+
54
+ stacks_from_file.delete(stack)
55
+ end
56
+
57
+ # Delete file holding created stack IDs.
58
+ @logger.info("Deleting file #{created_stacks_fname}.")
59
+ File.delete(created_stacks_fname)
60
+
61
+ env[:machine].id = nil
62
+ @app.call(env)
63
+ end
64
+
65
+ private
66
+
67
+ def find_stack(available_stacks, stack_id)
68
+ available_stacks.each do |stack|
69
+ if stack['id'] == stack_id
70
+ return stack
71
+ end
72
+ end
73
+ false
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end