vagrant-openstack-plugin-tom 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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