vagrant-openstack-provider 0.5.2 → 0.6.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/lib/vagrant-openstack-provider.rb +2 -31
  4. data/lib/vagrant-openstack-provider/action.rb +21 -7
  5. data/lib/vagrant-openstack-provider/action/abstract_action.rb +22 -0
  6. data/lib/vagrant-openstack-provider/action/connect_openstack.rb +19 -40
  7. data/lib/vagrant-openstack-provider/action/create_server.rb +10 -6
  8. data/lib/vagrant-openstack-provider/action/create_stack.rb +67 -0
  9. data/lib/vagrant-openstack-provider/action/delete_server.rb +28 -3
  10. data/lib/vagrant-openstack-provider/action/delete_stack.rb +72 -0
  11. data/lib/vagrant-openstack-provider/action/message.rb +4 -2
  12. data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +4 -2
  13. data/lib/vagrant-openstack-provider/action/read_state.rb +9 -4
  14. data/lib/vagrant-openstack-provider/action/resume.rb +4 -2
  15. data/lib/vagrant-openstack-provider/action/start_server.rb +4 -2
  16. data/lib/vagrant-openstack-provider/action/stop_server.rb +4 -2
  17. data/lib/vagrant-openstack-provider/action/suspend.rb +4 -2
  18. data/lib/vagrant-openstack-provider/action/sync_folders.rb +17 -13
  19. data/lib/vagrant-openstack-provider/action/wait_accessible.rb +5 -2
  20. data/lib/vagrant-openstack-provider/action/wait_active.rb +5 -3
  21. data/lib/vagrant-openstack-provider/action/wait_stop.rb +5 -3
  22. data/lib/vagrant-openstack-provider/catalog/openstack_catalog.rb +66 -0
  23. data/lib/vagrant-openstack-provider/client/domain.rb +41 -1
  24. data/lib/vagrant-openstack-provider/client/glance.rb +63 -0
  25. data/lib/vagrant-openstack-provider/client/heat.rb +50 -0
  26. data/lib/vagrant-openstack-provider/client/http_utils.rb +18 -0
  27. data/lib/vagrant-openstack-provider/client/neutron.rb +9 -15
  28. data/lib/vagrant-openstack-provider/client/nova.rb +3 -3
  29. data/lib/vagrant-openstack-provider/client/openstack.rb +10 -0
  30. data/lib/vagrant-openstack-provider/command/abstract_command.rb +7 -0
  31. data/lib/vagrant-openstack-provider/command/image_list.rb +12 -2
  32. data/lib/vagrant-openstack-provider/command/main.rb +1 -0
  33. data/lib/vagrant-openstack-provider/command/network_list.rb +3 -3
  34. data/lib/vagrant-openstack-provider/command/subnet_list.rb +25 -0
  35. data/lib/vagrant-openstack-provider/config.rb +78 -7
  36. data/lib/vagrant-openstack-provider/config_resolver.rb +36 -5
  37. data/lib/vagrant-openstack-provider/errors.rb +30 -2
  38. data/lib/vagrant-openstack-provider/logging.rb +39 -0
  39. data/lib/vagrant-openstack-provider/version.rb +1 -1
  40. data/locales/en.yml +107 -4
  41. data/spec/vagrant-openstack-provider/action/connect_openstack_spec.rb +255 -8
  42. data/spec/vagrant-openstack-provider/action/create_server_spec.rb +6 -1
  43. data/spec/vagrant-openstack-provider/action/create_stack_spec.rb +97 -0
  44. data/spec/vagrant-openstack-provider/action/delete_server_spec.rb +34 -6
  45. data/spec/vagrant-openstack-provider/action/delete_stack_spec.rb +64 -0
  46. data/spec/vagrant-openstack-provider/action/read_state_spec.rb +13 -1
  47. data/spec/vagrant-openstack-provider/action/sync_folders_spec.rb +1 -0
  48. data/spec/vagrant-openstack-provider/action/wait_active_spec.rb +1 -1
  49. data/spec/vagrant-openstack-provider/action/wait_stop_spec.rb +1 -1
  50. data/spec/vagrant-openstack-provider/client/glance_spec.rb +128 -0
  51. data/spec/vagrant-openstack-provider/client/heat_spec.rb +124 -0
  52. data/spec/vagrant-openstack-provider/client/neutron_spec.rb +33 -1
  53. data/spec/vagrant-openstack-provider/client/nova_spec.rb +2 -2
  54. data/spec/vagrant-openstack-provider/command/image_list_spec.rb +75 -23
  55. data/spec/vagrant-openstack-provider/command/subnet_list_spec.rb +46 -0
  56. data/spec/vagrant-openstack-provider/config_resolver_spec.rb +85 -19
  57. data/spec/vagrant-openstack-provider/config_spec.rb +177 -1
  58. data/spec/vagrant-openstack-provider/spec_helper.rb +3 -0
  59. metadata +20 -2
@@ -21,9 +21,30 @@ module VagrantPlugins
21
21
  end
22
22
  end
23
23
 
24
+ class Image < Item
25
+ attr_accessor :visibility
26
+ attr_accessor :size
27
+ attr_accessor :min_ram
28
+ attr_accessor :min_disk
29
+
30
+ def initialize(id, name, visibility = nil, size = nil, min_ram = nil, min_disk = nil)
31
+ @visibility = visibility
32
+ @size = size
33
+ @min_ram = min_ram
34
+ @min_disk = min_disk
35
+ super(id, name)
36
+ end
37
+
38
+ protected
39
+
40
+ def state
41
+ [@id, @name, @visibility, @size, @min_ram, @min_disk]
42
+ end
43
+ end
44
+
24
45
  class Flavor < Item
25
46
  #
26
- # THe number of vCPU
47
+ # The number of vCPU
27
48
  #
28
49
  attr_accessor :vcpus
29
50
 
@@ -115,6 +136,25 @@ module VagrantPlugins
115
136
  [@id, @name, @size, @status, @bootable, @instance_id, @device]
116
137
  end
117
138
  end
139
+
140
+ class Subnet < Item
141
+ attr_accessor :cidr
142
+ attr_accessor :enable_dhcp
143
+ attr_accessor :network_id
144
+
145
+ def initialize(id, name, cidr, enable_dhcp, network_id)
146
+ @cidr = cidr
147
+ @enable_dhcp = enable_dhcp
148
+ @network_id = network_id
149
+ super(id, name)
150
+ end
151
+
152
+ protected
153
+
154
+ def state
155
+ [@id, @name, @cidr, @enable_dhcp, @network_id]
156
+ end
157
+ end
118
158
  end
119
159
  end
120
160
  end
@@ -0,0 +1,63 @@
1
+ require 'log4r'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ require 'vagrant-openstack-provider/client/http_utils'
6
+ require 'vagrant-openstack-provider/client/domain'
7
+
8
+ module VagrantPlugins
9
+ module Openstack
10
+ class GlanceClient
11
+ include Singleton
12
+ include VagrantPlugins::Openstack::HttpUtils
13
+ include VagrantPlugins::Openstack::Domain
14
+
15
+ def initialize
16
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
17
+ @session = VagrantPlugins::Openstack.session
18
+ end
19
+
20
+ def get_api_version_list(_env)
21
+ json = RestClient.get(@session.endpoints[:image], 'X-Auth-Token' => @session.token, :accept => :json) do |response|
22
+ log_response(response)
23
+ case response.code
24
+ when 200, 300
25
+ response
26
+ when 401
27
+ fail Errors::AuthenticationFailed
28
+ else
29
+ fail Errors::VagrantOpenstackError, message: response.to_s
30
+ end
31
+ end
32
+ JSON.parse(json)['versions']
33
+ end
34
+
35
+ # Endpoint /images exists on both v1 and v2 API
36
+ # The attribute 'visibility' is used to detect
37
+ # if the call has been made on v1 or v2
38
+ #
39
+ # In case of v2 we have all the needed information,
40
+ # but in case of v1 we don't and we have to call
41
+ # /images/detail to get full details
42
+ #
43
+ def get_all_images(env)
44
+ images_json = get(env, "#{@session.endpoints[:image]}/images")
45
+ images = JSON.parse(images_json)['images']
46
+
47
+ return images if images.empty?
48
+
49
+ is_v1 = false
50
+ unless images[0].key? 'visibility'
51
+ is_v1 = true
52
+ images_json = get(env, "#{@session.endpoints[:image]}/images/detail")
53
+ images = JSON.parse(images_json)['images']
54
+ end
55
+
56
+ images.map do |i|
57
+ i['visibility'] = i['is_public'] ? 'public' : 'private' if is_v1
58
+ Image.new(i['id'], i['name'], i['visibility'], i['size'], i['min_ram'], i['min_disk'])
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,50 @@
1
+ require 'log4r'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ require 'vagrant-openstack-provider/client/http_utils'
6
+ require 'vagrant-openstack-provider/client/domain'
7
+
8
+ module VagrantPlugins
9
+ module Openstack
10
+ class HeatClient
11
+ include Singleton
12
+ include VagrantPlugins::Openstack::HttpUtils
13
+ include VagrantPlugins::Openstack::Domain
14
+
15
+ def initialize
16
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
17
+ @session = VagrantPlugins::Openstack.session
18
+ end
19
+
20
+ def create_stack(env, options)
21
+ stack = {}.tap do |s|
22
+ s['stack_name'] = options[:name] if options[:name]
23
+ s['template'] = options[:template]
24
+ end
25
+ stack_res = post(env, "#{@session.endpoints[:orchestration]}/stacks", stack.to_json)
26
+ JSON.parse(stack_res)['stack']['id']
27
+ end
28
+
29
+ def get_stack_details(env, stack_name, stack_id)
30
+ stack_exists do
31
+ server_details = get(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}")
32
+ JSON.parse(server_details)['stack']
33
+ end
34
+ end
35
+
36
+ def delete_stack(env, stack_name, stack_id)
37
+ stack_exists do
38
+ delete(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}")
39
+ end
40
+ end
41
+
42
+ def stack_exists
43
+ return yield
44
+ rescue Errors::VagrantOpenstackError => e
45
+ raise Errors::StackNotFound if e.extra_data[:code] == 404
46
+ raise e
47
+ end
48
+ end
49
+ end
50
+ end
@@ -55,6 +55,24 @@ module VagrantPlugins
55
55
  end
56
56
  end
57
57
 
58
+ def get_api_version_list(service_type)
59
+ url = @session.endpoints[service_type]
60
+ headers = { 'X-Auth-Token' => @session.token, :accept => :json }
61
+ log_request(:GET, url, headers)
62
+ json = RestClient.get(url, headers) do |response|
63
+ log_response(response)
64
+ case response.code
65
+ when 200, 300
66
+ response
67
+ when 401
68
+ fail Errors::AuthenticationFailed
69
+ else
70
+ fail Errors::VagrantOpenstackError, message: response.to_s
71
+ end
72
+ end
73
+ JSON.parse(json)['versions']
74
+ end
75
+
58
76
  private
59
77
 
60
78
  ERRORS =
@@ -17,21 +17,6 @@ module VagrantPlugins
17
17
  @session = VagrantPlugins::Openstack.session
18
18
  end
19
19
 
20
- def get_api_version_list(_env)
21
- json = RestClient.get(@session.endpoints[:network], 'X-Auth-Token' => @session.token, :accept => :json) do |response|
22
- log_response(response)
23
- case response.code
24
- when 200, 300
25
- response
26
- when 401
27
- fail Errors::AuthenticationFailed
28
- else
29
- fail Errors::VagrantOpenstackError, message: response.to_s
30
- end
31
- end
32
- JSON.parse(json)['versions']
33
- end
34
-
35
20
  def get_private_networks(env)
36
21
  get_networks(env, false)
37
22
  end
@@ -40,6 +25,15 @@ module VagrantPlugins
40
25
  get_networks(env, true)
41
26
  end
42
27
 
28
+ def get_subnets(env)
29
+ subnets_json = get(env, "#{@session.endpoints[:network]}/subnets")
30
+ subnets = []
31
+ JSON.parse(subnets_json)['subnets'].each do |n|
32
+ subnets << Subnet.new(n['id'], n['name'], n['cidr'], n['enable_dhcp'], n['network_id'])
33
+ end
34
+ subnets
35
+ end
36
+
43
37
  private
44
38
 
45
39
  def get_networks(env, all)
@@ -45,7 +45,7 @@ module VagrantPlugins
45
45
 
46
46
  def get_all_images(env)
47
47
  images_json = get(env, "#{@session.endpoints[:compute]}/images")
48
- JSON.parse(images_json)['images'].map { |fl| Item.new(fl['id'], fl['name']) }
48
+ JSON.parse(images_json)['images'].map { |fl| Image.new(fl['id'], fl['name'], 'unknown') }
49
49
  end
50
50
 
51
51
  def create_server(env, options)
@@ -193,9 +193,9 @@ module VagrantPlugins
193
193
  JSON.parse(ip_details)['floating_ips'].each do |ip|
194
194
  next unless ip['ip'] == floating_ip
195
195
  return if ip['instance_id'].nil?
196
- fail "Floating IP #{floating_ip} already assigned to another server"
196
+ fail Errors::FloatingIPAlreadyAssigned, floating_ip: floating_ip
197
197
  end
198
- fail "Floating IP #{floating_ip} not available for this tenant"
198
+ fail Errors::FloatingIPNotAvailable, floating_ip: floating_ip
199
199
  end
200
200
  end
201
201
  end
@@ -2,10 +2,12 @@ require 'log4r'
2
2
  require 'restclient'
3
3
  require 'json'
4
4
 
5
+ require 'vagrant-openstack-provider/client/heat'
5
6
  require 'vagrant-openstack-provider/client/keystone'
6
7
  require 'vagrant-openstack-provider/client/nova'
7
8
  require 'vagrant-openstack-provider/client/neutron'
8
9
  require 'vagrant-openstack-provider/client/cinder'
10
+ require 'vagrant-openstack-provider/client/glance'
9
11
 
10
12
  module VagrantPlugins
11
13
  module Openstack
@@ -39,6 +41,10 @@ module VagrantPlugins
39
41
  Openstack::NovaClient.instance
40
42
  end
41
43
 
44
+ def self.heat
45
+ Openstack::HeatClient.instance
46
+ end
47
+
42
48
  def self.neutron
43
49
  Openstack::NeutronClient.instance
44
50
  end
@@ -46,5 +52,9 @@ module VagrantPlugins
46
52
  def self.cinder
47
53
  Openstack::CinderClient.instance
48
54
  end
55
+
56
+ def self.glance
57
+ Openstack::GlanceClient.instance
58
+ end
49
59
  end
50
60
  end
@@ -20,7 +20,14 @@ module VagrantPlugins
20
20
 
21
21
  cmd(name, @argv, env)
22
22
  @env.ui.info('')
23
+ # rubocop:disable Lint/RescueException
24
+ rescue Errors::VagrantOpenstackError, SystemExit, Interrupt => e
25
+ raise e
26
+ rescue Exception => e
27
+ puts I18n.t('vagrant_openstack.global_error').red unless e.message && e.message.start_with?('Catched Error:')
28
+ raise e
23
29
  end
30
+ # rubocop:enable Lint/RescueException
24
31
 
25
32
  #
26
33
  # Before Vagrant 1.5, args list ends with an extra arg '--'. It removes it if present.
@@ -12,8 +12,18 @@ module VagrantPlugins
12
12
  end
13
13
  def cmd(name, argv, env)
14
14
  fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0
15
- images = env[:openstack_client].nova.get_all_images(env)
16
- display_item_list(env, images)
15
+ rows = []
16
+ headers = %w(Id Name)
17
+ if env[:openstack_client].session.endpoints.key? :image
18
+ images = env[:openstack_client].glance.get_all_images(env)
19
+ images.each { |i| rows << [i.id, i.name, i.visibility, i.size.to_i / 1024 / 1024, i.min_ram, i.min_disk] }
20
+ headers << ['Visibility', 'Size (Mo)', 'Min RAM (Go)', 'Min Disk (Go)']
21
+ headers = headers.flatten
22
+ else
23
+ images = env[:openstack_client].nova.get_all_images(env)
24
+ images.each { |image| rows << [image.id, image.name] }
25
+ end
26
+ display_table(env, headers, rows)
17
27
  end
18
28
  end
19
29
  end
@@ -5,6 +5,7 @@ module VagrantPlugins
5
5
  { name: :'image-list', file: 'image_list' , clazz: 'ImageList' },
6
6
  { name: :'flavor-list', file: 'flavor_list', clazz: 'FlavorList' },
7
7
  { name: :'network-list', file: 'network_list', clazz: 'NetworkList' },
8
+ { name: :'subnet-list', file: 'subnet_list', clazz: 'SubnetList' },
8
9
  { name: :'floatingip-list', file: 'floatingip_list', clazz: 'FloatingIpList' },
9
10
  { name: :'volume-list', file: 'volume_list', clazz: 'VolumeList' },
10
11
  { name: :'reset', file: 'reset', clazz: 'Reset' }
@@ -13,13 +13,13 @@ module VagrantPlugins
13
13
  def cmd(name, argv, env)
14
14
  fail Errors::UnrecognizedArgForCommand, cmd: name, arg: argv[1] if argv.size > 1
15
15
  if argv.size == 0
16
- flavors = env[:openstack_client].neutron.get_private_networks(env)
16
+ networks = env[:openstack_client].neutron.get_private_networks(env)
17
17
  elsif argv[0] == 'all'
18
- flavors = env[:openstack_client].neutron.get_all_networks(env)
18
+ networks = env[:openstack_client].neutron.get_all_networks(env)
19
19
  else
20
20
  fail Errors::UnrecognizedArgForCommand, cmd: name, arg: argv[0]
21
21
  end
22
- display_item_list(env, flavors)
22
+ display_item_list(env, networks)
23
23
  end
24
24
  end
25
25
  end
@@ -0,0 +1,25 @@
1
+ require 'vagrant-openstack-provider/command/utils'
2
+ require 'vagrant-openstack-provider/command/abstract_command'
3
+
4
+ module VagrantPlugins
5
+ module Openstack
6
+ module Command
7
+ class SubnetList < AbstractCommand
8
+ include VagrantPlugins::Openstack::Command::Utils
9
+
10
+ def self.synopsis
11
+ I18n.t('vagrant_openstack.command.subnet_list_synopsis')
12
+ end
13
+
14
+ def cmd(name, argv, env)
15
+ fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0
16
+ rows = []
17
+ env[:openstack_client].neutron.get_subnets(env).each do |subnet|
18
+ rows << [subnet.id, subnet.name, subnet.cidr, subnet.enable_dhcp, subnet.network_id]
19
+ end
20
+ display_table(env, ['Id', 'Name', 'CIDR', 'DHCP', 'Network Id'], rows)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -8,24 +8,32 @@ module VagrantPlugins
8
8
  #
9
9
  attr_accessor :password
10
10
 
11
- # The compute service url to access Openstack. If nil, it will read from
12
- # hypermedia catalog form REST API
11
+ # The compute service url to access Openstack. If nil, it will read from hypermedia catalog form REST API
13
12
  #
14
13
  attr_accessor :openstack_compute_url
15
14
 
16
- # The network service url to access Openstack. If nil, it will read from
17
- # hypermedia catalog form REST API
15
+ # The network service url to access Openstack. If nil, it will read from hypermedia catalog form REST API
18
16
  #
19
17
  attr_accessor :openstack_network_url
20
18
 
21
- # The block storage service url to access Openstack. If nil, it will read from
22
- # hypermedia catalog form REST API
19
+ # The block storage service url to access Openstack. If nil, it will read from hypermedia catalog form REST API
23
20
  #
24
21
  attr_accessor :openstack_volume_url
25
22
 
23
+ # The orchestration service url to access Openstack. If nil, it will read from hypermedia catalog form REST API
24
+ #
25
+ attr_accessor :openstack_orchestration_url
26
+
27
+ # The image service url to access Openstack. If nil, it will read from hypermedia catalog form REST API
28
+ #
29
+ attr_accessor :openstack_image_url
30
+
26
31
  # The authentication endpoint. This defaults to Openstack's global authentication endpoint.
27
32
  attr_accessor :openstack_auth_url
28
33
 
34
+ # Openstack region
35
+ attr_accessor :region
36
+
29
37
  # The flavor of server to launch, either the ID or name. This
30
38
  # can also be a regular expression to partially match a name.
31
39
  attr_accessor :flavor
@@ -96,6 +104,12 @@ module VagrantPlugins
96
104
  # @return [String]
97
105
  attr_accessor :sync_method
98
106
 
107
+ # Sync folder ignore files. A list of files containing exclude patterns to ignore in the rsync operation
108
+ # performed by this provider
109
+ #
110
+ # @return [Array]
111
+ attr_accessor :rsync_ignore_files
112
+
99
113
  # Network list the VM will be connected to
100
114
  #
101
115
  # @return [Array]
@@ -106,6 +120,11 @@ module VagrantPlugins
106
120
  # @return [Array]
107
121
  attr_accessor :volumes
108
122
 
123
+ # Stack that will be created and associated to the instances
124
+ #
125
+ # @return [Array]
126
+ attr_accessor :stacks
127
+
109
128
  # Public key path to create OpenStack keypair
110
129
  #
111
130
  # @return [Array]
@@ -146,7 +165,10 @@ module VagrantPlugins
146
165
  @openstack_compute_url = UNSET_VALUE
147
166
  @openstack_network_url = UNSET_VALUE
148
167
  @openstack_volume_url = UNSET_VALUE
168
+ @openstack_orchestration_url = UNSET_VALUE
169
+ @openstack_image_url = UNSET_VALUE
149
170
  @openstack_auth_url = UNSET_VALUE
171
+ @region = UNSET_VALUE
150
172
  @flavor = UNSET_VALUE
151
173
  @image = UNSET_VALUE
152
174
  @volume_boot = UNSET_VALUE
@@ -154,15 +176,17 @@ module VagrantPlugins
154
176
  @server_name = UNSET_VALUE
155
177
  @username = UNSET_VALUE
156
178
  @rsync_includes = []
179
+ @rsync_ignore_files = []
157
180
  @keypair_name = UNSET_VALUE
158
181
  @ssh_username = UNSET_VALUE
159
182
  @ssh_timeout = UNSET_VALUE
160
183
  @floating_ip = UNSET_VALUE
161
- @floating_ip_pool = UNSET_VALUE
184
+ @floating_ip_pool = []
162
185
  @floating_ip_pool_always_allocate = UNSET_VALUE
163
186
  @sync_method = UNSET_VALUE
164
187
  @availability_zone = UNSET_VALUE
165
188
  @networks = []
189
+ @stacks = []
166
190
  @volumes = []
167
191
  @public_key_path = UNSET_VALUE
168
192
  @scheduler_hints = UNSET_VALUE
@@ -172,13 +196,49 @@ module VagrantPlugins
172
196
  @ssh_disabled = UNSET_VALUE
173
197
  end
174
198
 
199
+ def merge(other)
200
+ result = self.class.new
201
+
202
+ # Set all of our instance variables on the new class
203
+ [self, other].each do |obj|
204
+ obj.instance_variables.each do |key|
205
+ # Ignore keys that start with a double underscore. This allows
206
+ # configuration classes to still hold around internal state
207
+ # that isn't propagated.
208
+ next if key.to_s.start_with?('@__')
209
+
210
+ # Let user inputs a string or an array for floating ip pool attribute
211
+ obj.floating_ip_pool = [obj.floating_ip_pool].flatten if key.eql?(:@floating_ip_pool) && !obj.floating_ip_pool.nil?
212
+
213
+ # Don't set the value if it is the unset value, either.
214
+ value = obj.instance_variable_get(key)
215
+
216
+ if [:@networks, :@volumes, :@rsync_includes, :@ignore_files, :@floating_ip_pool, :@stacks].include? key
217
+ result.instance_variable_set(key, value) unless value.empty?
218
+ else
219
+ result.instance_variable_set(key, value) if value != UNSET_VALUE
220
+ end
221
+ end
222
+ end
223
+
224
+ # Persist through the set of invalid methods
225
+ this_invalid = @__invalid_methods || Set.new
226
+ other_invalid = other.instance_variable_get(:"@__invalid_methods") || Set.new
227
+ result.instance_variable_set(:"@__invalid_methods", this_invalid + other_invalid)
228
+
229
+ result
230
+ end
231
+
175
232
  # rubocop:disable Style/CyclomaticComplexity
176
233
  def finalize!
177
234
  @password = nil if @password == UNSET_VALUE
178
235
  @openstack_compute_url = nil if @openstack_compute_url == UNSET_VALUE
179
236
  @openstack_network_url = nil if @openstack_network_url == UNSET_VALUE
237
+ @openstack_orchestration_url = nil if @openstack_orchestration_url == UNSET_VALUE
180
238
  @openstack_volume_url = nil if @openstack_volume_url == UNSET_VALUE
239
+ @openstack_image_url = nil if @openstack_image_url == UNSET_VALUE
181
240
  @openstack_auth_url = nil if @openstack_auth_url == UNSET_VALUE
241
+ @region = nil if @region == UNSET_VALUE
182
242
  @flavor = nil if @flavor == UNSET_VALUE
183
243
  @image = nil if @image == UNSET_VALUE
184
244
  @volume_boot = nil if @volume_boot == UNSET_VALUE
@@ -186,6 +246,7 @@ module VagrantPlugins
186
246
  @server_name = nil if @server_name == UNSET_VALUE
187
247
  @username = nil if @username == UNSET_VALUE
188
248
  @rsync_includes = nil if @rsync_includes.empty?
249
+ @rsync_ignore_files = nil if @rsync_ignore_files.empty?
189
250
  @floating_ip = nil if @floating_ip == UNSET_VALUE
190
251
  @floating_ip_pool = nil if @floating_ip_pool == UNSET_VALUE
191
252
  @floating_ip_pool_always_allocate = false if floating_ip_pool_always_allocate == UNSET_VALUE
@@ -205,6 +266,7 @@ module VagrantPlugins
205
266
  @ssh_timeout = 180 if @ssh_timeout == UNSET_VALUE
206
267
  @networks = nil if @networks.empty?
207
268
  @volumes = nil if @volumes.empty?
269
+ @stacks = nil if @stacks.empty?
208
270
  end
209
271
  # rubocop:enable Style/CyclomaticComplexity
210
272
 
@@ -219,6 +281,7 @@ module VagrantPlugins
219
281
  errors << I18n.t('vagrant_openstack.config.username_required') unless @username
220
282
 
221
283
  validate_ssh_username(machine, errors)
284
+ validate_stack_config(errors)
222
285
  validate_ssh_timeout(errors)
223
286
 
224
287
  if machine.config.ssh.private_key_path
@@ -231,6 +294,8 @@ module VagrantPlugins
231
294
  openstack_compute_url: @openstack_compute_url,
232
295
  openstack_network_url: @openstack_network_url,
233
296
  openstack_volume_url: @openstack_volume_url,
297
+ openstack_orchestration_url: @openstack_orchestration_url,
298
+ openstack_image_url: @openstack_image_url,
234
299
  openstack_auth_url: @openstack_auth_url
235
300
  }.each_pair do |key, value|
236
301
  errors << I18n.t('vagrant_openstack.config.invalid_uri', key: key, uri: value) unless value.nil? || valid_uri?(value)
@@ -241,6 +306,12 @@ module VagrantPlugins
241
306
 
242
307
  private
243
308
 
309
+ def validate_stack_config(errors)
310
+ @stacks.each do |stack|
311
+ errors << I18n.t('vagrant_openstack.config.invalid_stack') unless stack[:name] && stack[:template]
312
+ end unless @stacks.nil?
313
+ end
314
+
244
315
  def validate_ssh_username(machine, errors)
245
316
  puts I18n.t('vagrant_openstack.config.ssh_username_deprecated').yellow if @ssh_username
246
317
  errors << I18n.t('vagrant_openstack.config.ssh_username_required') unless @ssh_username || machine.config.ssh.username