vagrant-softlayer 0.3.3 → 0.4.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.
- checksums.yaml +4 -4
- data/.gitignore +16 -16
- data/CHANGELOG.md +61 -49
- data/QUICKSTART.md +380 -376
- data/README.md +258 -256
- data/contrib/vagrant-softlayer-boxes +418 -416
- data/contrib/vagrant-softlayer-vlans +339 -329
- data/lib/vagrant-softlayer/action/create_instance.rb +74 -74
- data/lib/vagrant-softlayer/action/setup_softlayer.rb +39 -39
- data/lib/vagrant-softlayer/action/update_dns.rb +94 -94
- data/lib/vagrant-softlayer/action/wait_for_provision.rb +40 -40
- data/lib/vagrant-softlayer/action/wait_for_rebuild.rb +38 -36
- data/lib/vagrant-softlayer/config.rb +282 -268
- data/lib/vagrant-softlayer/util/load_balancer.rb +96 -103
- data/lib/vagrant-softlayer/util/network.rb +75 -75
- data/lib/vagrant-softlayer/util/warden.rb +38 -38
- data/lib/vagrant-softlayer/version.rb +5 -5
- data/spec/vagrant-softlayer/config_spec.rb +271 -267
- data/vagrant-softlayer.gemspec +55 -55
- metadata +4 -4
@@ -1,36 +1,38 @@
|
|
1
|
-
require "log4r"
|
2
|
-
|
3
|
-
module VagrantPlugins
|
4
|
-
module SoftLayer
|
5
|
-
module Action
|
6
|
-
# Waits until the new machine has been rebuilt.
|
7
|
-
class WaitForRebuild
|
8
|
-
include Util::Warden
|
9
|
-
|
10
|
-
def initialize(app, env)
|
11
|
-
@app = app
|
12
|
-
@logger = Log4r::Logger.new("vagrant_softlayer::action::wait_for_rebuild")
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(env)
|
16
|
-
env[:ui].info I18n.t("vagrant_softlayer.vm.wait_for_rebuild")
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module SoftLayer
|
5
|
+
module Action
|
6
|
+
# Waits until the new machine has been rebuilt.
|
7
|
+
class WaitForRebuild
|
8
|
+
include Util::Warden
|
9
|
+
|
10
|
+
def initialize(app, env)
|
11
|
+
@app = app
|
12
|
+
@logger = Log4r::Logger.new("vagrant_softlayer::action::wait_for_rebuild")
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env[:ui].info I18n.t("vagrant_softlayer.vm.wait_for_rebuild")
|
17
|
+
|
18
|
+
#Rechecks every 10 sec
|
19
|
+
virtual_server = ::SoftLayer::VirtualServer.server_with_id(env[:machine].id.to_i, :client => env[:sl_client])
|
20
|
+
|
21
|
+
ready = virtual_server.wait_until_ready((env[:machine].provider_config.rebuild_timeout.to_f/10).ceil, env[:machine].provider_config.transaction_wait, 10) do |server_ready|
|
22
|
+
unless server_ready
|
23
|
+
rebuild_status = env[:sl_machine].getActiveTransaction
|
24
|
+
rebuild_status = " Rebuild status: #{rebuild_status["transactionStatus"]["friendlyName"]} (#{rebuild_status["transactionStatus"]["name"]})." if rebuild_status && ! rebuild_status.empty?
|
25
|
+
@logger.info("#{env[:machine].provider_config.hostname} is still rebuilding. Retrying in 10 seconds.#{rebuild_status}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
raise Errors::SLRebuildTimeoutError unless ready
|
30
|
+
|
31
|
+
env[:ui].info I18n.t("vagrant_softlayer.vm.rebuilt")
|
32
|
+
|
33
|
+
@app.call(env)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,268 +1,282 @@
|
|
1
|
-
require "ostruct"
|
2
|
-
|
3
|
-
module VagrantPlugins
|
4
|
-
module SoftLayer
|
5
|
-
class Config < Vagrant.plugin("2", :config)
|
6
|
-
# The API key to access SoftLayer.
|
7
|
-
attr_accessor :api_key
|
8
|
-
|
9
|
-
#
|
10
|
-
attr_accessor :
|
11
|
-
|
12
|
-
# The
|
13
|
-
attr_accessor :
|
14
|
-
|
15
|
-
# The
|
16
|
-
attr_accessor :
|
17
|
-
|
18
|
-
#
|
19
|
-
attr_accessor :
|
20
|
-
|
21
|
-
#
|
22
|
-
attr_accessor :
|
23
|
-
|
24
|
-
# The
|
25
|
-
attr_accessor :
|
26
|
-
|
27
|
-
#
|
28
|
-
attr_accessor :
|
29
|
-
|
30
|
-
#
|
31
|
-
attr_accessor :
|
32
|
-
|
33
|
-
# The
|
34
|
-
attr_accessor :
|
35
|
-
|
36
|
-
# The
|
37
|
-
attr_accessor :
|
38
|
-
|
39
|
-
# The
|
40
|
-
attr_accessor :
|
41
|
-
|
42
|
-
# The
|
43
|
-
attr_accessor :
|
44
|
-
|
45
|
-
#
|
46
|
-
attr_accessor :
|
47
|
-
|
48
|
-
#
|
49
|
-
attr_accessor :
|
50
|
-
|
51
|
-
#
|
52
|
-
attr_accessor :
|
53
|
-
|
54
|
-
#
|
55
|
-
attr_accessor :
|
56
|
-
|
57
|
-
#
|
58
|
-
attr_accessor :
|
59
|
-
|
60
|
-
# The amount of time in seconds to wait for
|
61
|
-
attr_accessor :
|
62
|
-
|
63
|
-
# The
|
64
|
-
attr_accessor :
|
65
|
-
|
66
|
-
# The
|
67
|
-
attr_accessor :
|
68
|
-
|
69
|
-
#
|
70
|
-
attr_accessor :
|
71
|
-
|
72
|
-
#
|
73
|
-
attr_accessor :
|
74
|
-
|
75
|
-
#
|
76
|
-
attr_accessor :
|
77
|
-
|
78
|
-
# The
|
79
|
-
|
80
|
-
|
81
|
-
#
|
82
|
-
attr_accessor :
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
95
|
-
|
96
|
-
@
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
102
|
-
@
|
103
|
-
@
|
104
|
-
@
|
105
|
-
@
|
106
|
-
@
|
107
|
-
@
|
108
|
-
@
|
109
|
-
@
|
110
|
-
|
111
|
-
@
|
112
|
-
@
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
# :
|
128
|
-
# :
|
129
|
-
# :
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
opts[:method]
|
142
|
-
opts[:type]
|
143
|
-
opts[:service].health_check
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
#
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
@
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module SoftLayer
|
5
|
+
class Config < Vagrant.plugin("2", :config)
|
6
|
+
# The API key to access SoftLayer.
|
7
|
+
attr_accessor :api_key
|
8
|
+
|
9
|
+
# Th SoftLayer API request timeout value in seconds
|
10
|
+
attr_accessor :api_timeout
|
11
|
+
|
12
|
+
# The endpoint SoftLayer API url.
|
13
|
+
attr_accessor :endpoint_url
|
14
|
+
|
15
|
+
# The username to access SoftLayer.
|
16
|
+
attr_accessor :username
|
17
|
+
|
18
|
+
# The datacenter shortname.
|
19
|
+
attr_accessor :datacenter
|
20
|
+
|
21
|
+
# Whether to allocate a dedicated instance.
|
22
|
+
attr_accessor :dedicated
|
23
|
+
|
24
|
+
# The disk image capacity
|
25
|
+
attr_accessor :disk_capacity
|
26
|
+
|
27
|
+
# The domain of the instance.
|
28
|
+
attr_accessor :domain
|
29
|
+
|
30
|
+
# Force the use of the private IP for all communication even if a public IP is available
|
31
|
+
attr_accessor :force_private_ip
|
32
|
+
|
33
|
+
# The hostname of the instance.
|
34
|
+
attr_accessor :hostname
|
35
|
+
|
36
|
+
# The billing type of the instance (true for hourly, false for monthly).
|
37
|
+
attr_accessor :hourly_billing
|
38
|
+
|
39
|
+
# The global identifier of the compute or flex image to use.
|
40
|
+
attr_accessor :image_guid
|
41
|
+
|
42
|
+
# The disk type of the instance (true for local, false for SAN).
|
43
|
+
attr_accessor :local_disk
|
44
|
+
|
45
|
+
# The amount of RAM of the instance.
|
46
|
+
attr_accessor :max_memory
|
47
|
+
|
48
|
+
# Network port speed in Mbps.
|
49
|
+
attr_accessor :network_speed
|
50
|
+
|
51
|
+
# The instance operating system identifier.
|
52
|
+
attr_accessor :operating_system
|
53
|
+
|
54
|
+
# URI of post-install script to download.
|
55
|
+
attr_accessor :post_install
|
56
|
+
|
57
|
+
# Whether or not the instance only has access to the private network.
|
58
|
+
attr_accessor :private_only
|
59
|
+
|
60
|
+
# The amount of time in seconds to wait for provision to complete.
|
61
|
+
attr_accessor :provision_timeout
|
62
|
+
|
63
|
+
# The amount of time in seconds to wait for rebuild to complete.
|
64
|
+
attr_accessor :rebuild_timeout
|
65
|
+
|
66
|
+
# The id or name of the ssh key to be provisioned.
|
67
|
+
attr_accessor :ssh_key
|
68
|
+
|
69
|
+
# The number of processors of the instance.
|
70
|
+
attr_accessor :start_cpus
|
71
|
+
|
72
|
+
# Whether to wait for transaction completion on actions that place orders
|
73
|
+
attr_accessor :transaction_wait
|
74
|
+
|
75
|
+
# User defined metadata string.
|
76
|
+
attr_accessor :user_data
|
77
|
+
|
78
|
+
# The ID, name or qualified name of the private VLAN.
|
79
|
+
attr_accessor :vlan_private
|
80
|
+
|
81
|
+
# The ID, name or qualified name of the public VLAN.
|
82
|
+
attr_accessor :vlan_public
|
83
|
+
|
84
|
+
# The load balancers service groups to join.
|
85
|
+
attr_reader :load_balancers
|
86
|
+
|
87
|
+
# Automatically update DNS on create and destroy.
|
88
|
+
attr_accessor :manage_dns
|
89
|
+
|
90
|
+
def initialize
|
91
|
+
@api_key = UNSET_VALUE
|
92
|
+
@api_timeout = UNSET_VALUE
|
93
|
+
@endpoint_url = UNSET_VALUE
|
94
|
+
@username = UNSET_VALUE
|
95
|
+
|
96
|
+
@datacenter = UNSET_VALUE
|
97
|
+
@dedicated = UNSET_VALUE
|
98
|
+
@disk_capacity = UNSET_VALUE
|
99
|
+
@domain = UNSET_VALUE
|
100
|
+
@force_private_ip = UNSET_VALUE
|
101
|
+
@hostname = UNSET_VALUE
|
102
|
+
@image_guid = UNSET_VALUE
|
103
|
+
@hourly_billing = UNSET_VALUE
|
104
|
+
@local_disk = UNSET_VALUE
|
105
|
+
@max_memory = UNSET_VALUE
|
106
|
+
@network_speed = UNSET_VALUE
|
107
|
+
@operating_system = UNSET_VALUE
|
108
|
+
@post_install = UNSET_VALUE
|
109
|
+
@private_only = UNSET_VALUE
|
110
|
+
@provision_timeout = UNSET_VALUE
|
111
|
+
@rebuild_timeout = UNSET_VALUE
|
112
|
+
@ssh_key = UNSET_VALUE
|
113
|
+
@start_cpus = UNSET_VALUE
|
114
|
+
@transaction_wait = UNSET_VALUE
|
115
|
+
@user_data = UNSET_VALUE
|
116
|
+
@vlan_private = UNSET_VALUE
|
117
|
+
@vlan_public = UNSET_VALUE
|
118
|
+
|
119
|
+
@load_balancers = []
|
120
|
+
@manage_dns = UNSET_VALUE
|
121
|
+
end
|
122
|
+
|
123
|
+
# Set the load balancer service group to join.
|
124
|
+
#
|
125
|
+
# Available options:
|
126
|
+
#
|
127
|
+
# :method => Routing method. Default to round robin.
|
128
|
+
# :port => Load balancer virtual port.
|
129
|
+
# :type => Routing type. Default to TCP.
|
130
|
+
# :vip => Load balancer virtual IP address.
|
131
|
+
#
|
132
|
+
# An optional block will accept parameters for the
|
133
|
+
# balanced service. Available parameters:
|
134
|
+
#
|
135
|
+
# :destination_port => TCP port on the node.
|
136
|
+
# :health_check => Service health check. Default to ping.
|
137
|
+
# :weight => Service weight. Default to 1.
|
138
|
+
#
|
139
|
+
def join_load_balancer(opts = {}, &block)
|
140
|
+
# Defaults
|
141
|
+
opts[:method] ||= "ROUND ROBIN"
|
142
|
+
opts[:type] ||= "TCP"
|
143
|
+
opts[:service] = OpenStruct.new(:destination_port => nil, :health_check => "PING", :weight => 1)
|
144
|
+
|
145
|
+
yield opts[:service] if block_given?
|
146
|
+
|
147
|
+
# Convert all options that belongs to
|
148
|
+
# an enumeration in uppercase.
|
149
|
+
opts[:method].upcase!
|
150
|
+
opts[:type].upcase!
|
151
|
+
opts[:service].health_check.upcase!
|
152
|
+
|
153
|
+
@load_balancers << opts
|
154
|
+
end
|
155
|
+
|
156
|
+
def finalize!
|
157
|
+
# Try to get username and api key from environment variables.
|
158
|
+
# They will default to nil if the environment variables are not present.
|
159
|
+
@api_key = ENV["SL_API_KEY"] if @api_key == UNSET_VALUE
|
160
|
+
@username = ENV["SL_USERNAME"] if @username == UNSET_VALUE
|
161
|
+
|
162
|
+
# Th SoftLayer API request timeout value in seconds
|
163
|
+
@api_timeout = 60 if @api_timeout == UNSET_VALUE
|
164
|
+
|
165
|
+
# Endpoint url defaults to public SoftLayer API url.
|
166
|
+
@endpoint_url = API_PUBLIC_ENDPOINT if @endpoint_url == UNSET_VALUE
|
167
|
+
|
168
|
+
# No default datacenter.
|
169
|
+
@datacenter = nil if @datacenter == UNSET_VALUE
|
170
|
+
|
171
|
+
# Shared instance by default.
|
172
|
+
@dedicated = false if @dedicated == UNSET_VALUE
|
173
|
+
|
174
|
+
# 25GB disk capacity image by default.
|
175
|
+
@disk_capacity = nil if @disk_capacity == UNSET_VALUE
|
176
|
+
|
177
|
+
# Domain should be specified in Vagrantfile, so we set default to nil.
|
178
|
+
@domain = nil if @domain == UNSET_VALUE
|
179
|
+
|
180
|
+
# Disable the use of force private IP so the default selection can take effect
|
181
|
+
@force_private_ip = false if @force_private_ip == UNSET_VALUE
|
182
|
+
|
183
|
+
# Hostname should be specified in Vagrantfile, either using `config.vm.hostname`
|
184
|
+
# or the provider specific configuration entry.
|
185
|
+
@hostname = nil if @hostname == UNSET_VALUE
|
186
|
+
|
187
|
+
# Bill hourly by default.
|
188
|
+
@hourly_billing = true if @hourly_billing == UNSET_VALUE
|
189
|
+
|
190
|
+
# Disable the use of a specific block device image so we can leave the OS template as default
|
191
|
+
@image_guid = nil if @image_guid == UNSET_VALUE
|
192
|
+
|
193
|
+
# Use local disk by default.
|
194
|
+
@local_disk = true if @local_disk == UNSET_VALUE
|
195
|
+
|
196
|
+
# 1Gb of RAM by default.
|
197
|
+
@max_memory = 1024 if @max_memory == UNSET_VALUE
|
198
|
+
|
199
|
+
# 10Mbps by default.
|
200
|
+
@network_speed = 10 if @network_speed == UNSET_VALUE
|
201
|
+
|
202
|
+
# Provision with the latest Ubuntu by default.
|
203
|
+
@operating_system = "UBUNTU_LATEST" if @operating_system == UNSET_VALUE
|
204
|
+
|
205
|
+
# No post install script by default.
|
206
|
+
@post_install = nil if @post_install == UNSET_VALUE
|
207
|
+
|
208
|
+
# Private-network only is false by default.
|
209
|
+
@private_only = false if @private_only == UNSET_VALUE
|
210
|
+
|
211
|
+
# The amount of time in seconds to wait for provision to complete.
|
212
|
+
@provision_timeout = 1200 if @provision_timeout == UNSET_VALUE
|
213
|
+
|
214
|
+
# The amount of time in seconds to wait for rebuild to complete.
|
215
|
+
@rebuild_timeout = 1200 if @rebuild_timeout == UNSET_VALUE
|
216
|
+
|
217
|
+
# SSH key should be specified in Vagrantfile, so we set default to nil.
|
218
|
+
@ssh_key = nil if @ssh_key == UNSET_VALUE
|
219
|
+
|
220
|
+
# One processor by default.
|
221
|
+
@start_cpus = 1 if @start_cpus == UNSET_VALUE
|
222
|
+
|
223
|
+
# Whether to wait for transaction completion on actions that place orders
|
224
|
+
@transaction_wait = true if @start_cpus == UNSET_VALUE
|
225
|
+
|
226
|
+
# No user metadata by default.
|
227
|
+
@user_data = nil if @user_data == UNSET_VALUE
|
228
|
+
|
229
|
+
# No specific private VLAN by default.
|
230
|
+
@vlan_private = nil if @vlan_private == UNSET_VALUE
|
231
|
+
|
232
|
+
# No specific public VLAN by default.
|
233
|
+
@vlan_public = nil if @vlan_public == UNSET_VALUE
|
234
|
+
|
235
|
+
# DNS management off by default
|
236
|
+
@manage_dns = false if @manage_dns == UNSET_VALUE
|
237
|
+
end
|
238
|
+
|
239
|
+
# Aliases for ssh_key for beautiful semantic.
|
240
|
+
def ssh_keys=(value)
|
241
|
+
@ssh_key = value
|
242
|
+
end
|
243
|
+
|
244
|
+
alias_method :ssh_key_id=, :ssh_keys=
|
245
|
+
alias_method :ssh_key_ids=, :ssh_keys=
|
246
|
+
alias_method :ssh_key_name=, :ssh_keys=
|
247
|
+
alias_method :ssh_key_names=, :ssh_keys=
|
248
|
+
|
249
|
+
def validate(machine)
|
250
|
+
errors = []
|
251
|
+
|
252
|
+
errors << I18n.t("vagrant_softlayer.config.api_key_required") if !@api_key
|
253
|
+
errors << I18n.t("vagrant_softlayer.config.username_required") if !@username
|
254
|
+
|
255
|
+
errors << I18n.t("vagrant_softlayer.config.domain_required") if !@domain
|
256
|
+
errors << I18n.t("vagrant_softlayer.config.ssh_key_required") if !@ssh_key
|
257
|
+
|
258
|
+
errors << I18n.t("vagrant_softlayer.config.img_guid_os_code_mutually_exclusive") if @image_guid && @operating_system
|
259
|
+
errors << I18n.t("vagrant_softlayer.config.img_guid_capacity_mutually_exclusive") if @image_guid && @disk_capacity
|
260
|
+
|
261
|
+
# Fail if both `vm.hostname` and `provider.hostname` are nil.
|
262
|
+
if !@hostname && !machine.config.vm.hostname
|
263
|
+
errors << I18n.t("vagrant_softlayer.config.hostname_required")
|
264
|
+
end
|
265
|
+
|
266
|
+
# Fail if a load balancer has been specified without vip, port or destination port.
|
267
|
+
unless @load_balancers.empty?
|
268
|
+
@load_balancers.each do |lb|
|
269
|
+
errors << I18n.t("vagrant_softlayer.config.lb_port_vip_required") unless (lb[:vip] && lb[:port] && lb[:service].destination_port)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Fail if two or more load balancers has been specified with same vip and port.
|
274
|
+
if @load_balancers.map { |lb| { :port => lb[:port], :vip => lb[:vip] } }.uniq!
|
275
|
+
errors << I18n.t("vagrant_softlayer.config.lb_duplicate")
|
276
|
+
end
|
277
|
+
|
278
|
+
{ "SoftLayer" => errors }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|