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.
@@ -1,416 +1,418 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'getoptlong'
4
- require 'tmpdir'
5
-
6
- require 'rubygems'
7
- require 'softlayer_api'
8
-
9
- $sl = {
10
- :data => {
11
- :create_opts => nil,
12
- :private_images => nil,
13
- :public_images => nil
14
- },
15
- :service => {
16
- :account => nil,
17
- :virtual_guest => nil,
18
- :virtual_guest_block_device_template_group => nil
19
- },
20
- :sl_credentials => {
21
- :api_key => nil,
22
- :endpoint_url => SoftLayer::API_PUBLIC_ENDPOINT,
23
- :username => nil
24
- }
25
- }
26
-
27
- $boxes = {
28
- :data => {},
29
- :datacenter => nil,
30
- :dir => {
31
- :boxes => "boxes",
32
- :vagrantfiles => "vagrantfiles",
33
- :boxes_root => nil,
34
- :output_root => File.expand_path("."),
35
- :vagrantfiles_root => nil
36
- },
37
- :domain => nil,
38
- :file_templates => {
39
- :created_by => "# vagrant-softlayer-boxes automatic template export",
40
- :end => "end",
41
- :image_detail => "# Compute or flex image details:",
42
- :indent => Hash.new() { |hash, key| hash[key] = " " * key },
43
- :metadata_json => "{\"provider\": \"softlayer\"}",
44
- :os_templates_avail => "# Available versions of this OS template (based on VM property selections):",
45
- :sl_config_start => "config.vm.provider :softlayer do |sl|",
46
- :vagrant_config_start => "Vagrant.configure(\"2\") do |config|"
47
- },
48
- :filter => nil,
49
- :images => {
50
- :private => true,
51
- :public => true
52
- },
53
- :metadata_path => nil,
54
- :monthly_billing => nil,
55
- :templates => true,
56
- :vlan_private => nil,
57
- :vlan_public => nil
58
- }
59
-
60
- def procCliOptions()
61
- opts = GetoptLong.new(
62
- [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
63
- [ '--output_dir', '-o', GetoptLong::REQUIRED_ARGUMENT ],
64
- [ '--only_images', '-i', GetoptLong::NO_ARGUMENT ],
65
- [ '--only_private_images', '-p', GetoptLong::NO_ARGUMENT ],
66
- [ '--only_public_images', '-g', GetoptLong::NO_ARGUMENT ],
67
- [ '--only_templates', '-t', GetoptLong::NO_ARGUMENT ],
68
- [ '--default_domain', GetoptLong::REQUIRED_ARGUMENT ],
69
- [ '--default_datacenter', GetoptLong::REQUIRED_ARGUMENT ],
70
- [ '--default_monthly_billing', GetoptLong::NO_ARGUMENT ],
71
- [ '--default_vlan_private', GetoptLong::REQUIRED_ARGUMENT ],
72
- [ '--default_vlan_public', GetoptLong::REQUIRED_ARGUMENT ],
73
- [ '--sl_api_key', '-k', GetoptLong::REQUIRED_ARGUMENT ],
74
- [ '--sl_endpoint_url', '-e', GetoptLong::REQUIRED_ARGUMENT ],
75
- [ '--sl_username','-u', GetoptLong::REQUIRED_ARGUMENT ]
76
- )
77
-
78
- opts.each do | opt, optval |
79
- case opt
80
- when '--help'
81
- puts <<-EOF
82
- vagrant-softlayer-boxes [OPTION] [FILTER]
83
-
84
- --help, -h:
85
- Print this help.
86
-
87
- --sl_username USERNAME, -u USERNAME:
88
- Sets the SoftLayer account user name. If not specified, it is assumed SL_API_USERNAME environment variable is set.
89
-
90
- --sl_api_key SL_API_KEY, -k SL_API_KEY:
91
- Sets the SoftLayer API key. If not specified, it is assumed SL_API_KEY environment variable is set.
92
-
93
- --sl_endpoint_url SL_API_BASE_URL, -e SL_API_BASE_URL:
94
- Sets the SoftLayer endpoint URL. If not specified, it assumed SL_API_BASE_URL environment variable is set to API_PUBLIC_ENDPOINT or API_PRIVATE_ENDPOINT.
95
- Defaults to API_PUBLIC_ENDPOINT.
96
-
97
- --output_dir OUTPUTDIR, -o OUTPUTDIR:
98
- Sets the root directory to create box output under.
99
-
100
- --only_templates, -t:
101
- Only create boxes for the CCI templates and not compute or flex images.
102
-
103
- --only_images, -i:
104
- Only create boxes for the compute or flex images and not the CCI templates.
105
-
106
- --only_public_images, -g:
107
- Only create boxes for the public compute or flex images and not the CCI templates.
108
-
109
- --only_private_images, -p:
110
- Only create boxes for the private compute or flex images and not the CCI templates.
111
-
112
- --default_domain:
113
- Set default vm domain.
114
-
115
- --default_datacenter:
116
- Set default vm datacenter.
117
-
118
- --default_monthly_billing:
119
- Set default billing type to monthly.
120
-
121
- --default_vlan_private:
122
- Set default vm private vlan.
123
-
124
- --default_vlan_public:
125
- Set default vm public vlan.
126
-
127
- FILTER
128
- String used to filter template and compute/flex images by name to export boxes for specific matches. Supports filtering by regular expression.
129
-
130
- EOF
131
-
132
- exit 0
133
-
134
- when '--sl_username'
135
- $sl[:sl_credentials][:username] = optval.to_s
136
-
137
- when '--sl_api_key'
138
- $sl[:sl_credentials][:api_key] = optval.to_s
139
-
140
- when '--sl_endpoint_url'
141
- if ! [ "API_PUBLIC_ENDPOINT", "API_PRIVATE_ENDPOINT" ].include?(optval.to_s.upcase)
142
- $stderr.puts "ERROR: Invalid endpoint_url value: " + optval.to_s.upcase
143
- exit 2
144
- end
145
-
146
- $sl[:sl_credentials][:endpoint_url] = (optval.to_s.upcase == 'API_PUBLIC_ENDPOINT' ? SoftLayer::API_PUBLIC_ENDPOINT : SoftLayer::API_PRIVATE_ENDPOINT )
147
-
148
- when '--output_dir'
149
- if File.exists?(optval.to_s) && File.ftype(optval.to_s) != 'directory'
150
- $stderr.puts "ERROR: Path is not a directory: " + optval.to_s
151
- exit 2
152
- end
153
-
154
- $boxes[:dir][:output_root] = File.expand_path(optval.to_s)
155
- $boxes[:dir][:vagrantfiles_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles])
156
- $boxes[:dir][:boxes_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:boxes])
157
-
158
- if File.exists?($boxes[:dir][:vagrantfiles_root]) && File.ftype($boxes[:dir][:vagrantfiles_root]) != 'directory'
159
- $stderr.puts "ERROR: Output directory subdir is not a directory: " + $boxes[:dir][:vagrantfiles_root]
160
- exit 2
161
- end
162
-
163
- if File.exists?($boxes[:dir][:boxes_root]) && File.ftype($boxes[:dir][:boxes_root]) != 'directory'
164
- $stderr.puts "ERROR: Output directory subdir is not a directory: " + $boxes[:dir][:boxes_root]
165
- exit 2
166
- end
167
-
168
- when '--only_templates'
169
- $boxes[:images][:public] = false
170
- $boxes[:images][:private] = false
171
-
172
- when '--only_images'
173
- $boxes[:templates] = false
174
-
175
- when '--only_public_images'
176
- $boxes[:templates] = false
177
- $boxes[:images][:private] = false
178
-
179
- when '--only_private_images'
180
- $boxes[:templates] = false
181
- $boxes[:images][:public] = false
182
-
183
- when '--default_domain'
184
- $boxes[:domain] = optval.to_s
185
-
186
- when '--default_datacenter'
187
- $boxes[:datacenter] = optval.to_s
188
-
189
- when '--default_monthly_billing'
190
- $boxes[:monthly_billing] = true
191
-
192
- when '--default_vlan_private'
193
- $boxes[:vlan_private] = optval.to_s
194
-
195
- when'--default_vlan_public'
196
- $boxes[:vlan_public] = optval.to_s
197
-
198
- end
199
- end
200
-
201
- $boxes[:dir][:vagrantfiles_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles])
202
- $boxes[:dir][:boxes_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:boxes])
203
- $boxes[:metadata_path] = File.join($boxes[:dir][:output_root], "metadata.json")
204
-
205
- begin
206
- [ $boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles_root], $boxes[:dir][:boxes_root] ].each do |path|
207
- Dir.mkdir(path, 0755) if ! File.exists?(path)
208
- end
209
- rescue Exception => e
210
- $stderr.puts "ERROR: Failed to create output directories: " + e.message
211
- exit 1
212
- end
213
-
214
- $sl[:sl_credentials][:username] = ENV["SL_API_USERNAME"] if $sl[:sl_credentials][:username].nil? && ENV.include?("SL_API_USERNAME")
215
- $sl[:sl_credentials][:api_key] = ENV["SL_API_KEY"] if $sl[:sl_credentials][:api_key].nil? && ENV.include?("SL_API_KEY")
216
-
217
- if $sl[:sl_credentials][:endpoint_url].nil? && ENV.include?("SL_API_BASE_URL")
218
- if ! [ 'API_PRIVATE_ENDPOINT', 'API_PUBLIC_ENDPOINT' ].include?(ENV["SL_API_BASE_URL"])
219
- $stderr.puts "ERROR: Invalid SoftLayer endpoint URL specified in environment variable SL_API_BASE_URL, expected one of #{[ 'API_PRIVATE_ENDPOINT', 'API_PUBLIC_ENDPOINT' ].inspect}: #{ENV["SL_API_BASE_URL"].inspect}"
220
- exit 2
221
- end
222
-
223
- $sl[:sl_credentials][:endpoint_url] = (ENV["SL_API_BASE_URL"] == "API_PUBLIC_ENDPOINT" ? SoftLayer::API_PUBLIC_ENDPOINT : SoftLayer::API_PRIVATE_ENDPOINT )
224
- end
225
-
226
- if $sl[:sl_credentials][:username].nil?
227
- $stderr.puts "ERROR: No SoftLayer username specified"
228
- exit 2
229
- end
230
-
231
- if $sl[:sl_credentials][:username].nil?
232
- $stderr.puts "ERROR: No SoftLayer user name specified"
233
- exit 2
234
- end
235
-
236
- if $sl[:sl_credentials][:api_key].nil?
237
- $stderr.puts "ERROR: No SoftLayer API key specified"
238
- exit 2
239
- end
240
- end
241
-
242
- def procCliArgs()
243
- if ARGV.length > 2
244
- $stderr.puts "ERROR: Invalid argument supplied, please check help"
245
- exit 2
246
- elsif ARGV.length == 1
247
- begin
248
- $boxes[:filter] = Regexp.new(ARGV[0], Regexp::IGNORECASE)
249
- rescue Exception => e
250
- $stderr.puts "ERROR: Filter value is not a valid regular expression: " + e.message
251
- exit 2
252
- end
253
- end
254
- end
255
-
256
- def getBoxData()
257
- begin
258
- $sl[:service][:virtual_guest] = SoftLayer::Service.new("SoftLayer_Virtual_Guest", $sl[:sl_credentials]) if $boxes[:templates]
259
- $sl[:service][:account] = SoftLayer::Service.new("SoftLayer_Account", $sl[:sl_credentials]) if $boxes[:images][:private]
260
- $sl[:service][:virtual_guest_block_device_template_group] = SoftLayer::Service.new("SoftLayer_Virtual_Guest_Block_Device_Template_Group", $sl[:sl_credentials]) if $boxes[:images][:public]
261
- rescue Exception => e
262
- $stderr.puts "ERROR: Failed to create SoftLayer service object: " + e.message
263
- exit 1
264
- end
265
-
266
- begin
267
- $sl[:data][:create_opts] = $sl[:service][:virtual_guest].getCreateObjectOptions if $boxes[:templates]
268
- $sl[:data][:private_images] = $sl[:service][:account].getBlockDeviceTemplateGroups.delete_if { |block_device| ! block_device.has_key?("globalIdentifier") } if $boxes[:images][:private]
269
- $sl[:data][:public_images] = $sl[:service][:virtual_guest_block_device_template_group].getPublicImages.delete_if { |block_device| ! block_device.has_key?("globalIdentifier") } if $boxes[:images][:public]
270
- rescue Exception => e
271
- $stderr.puts "ERROR: Failed to retrieve SoftLayer service data: " + e.message
272
- exit 1
273
- end
274
-
275
- if $boxes[:templates]
276
- $sl[:data][:create_opts]["operatingSystems"].each do |os|
277
- boxgroup = os["template"]["operatingSystemReferenceCode"].strip.gsub(/[()]/,'').gsub(/[^a-zA-Z0-9_.-]/, '_').upcase
278
-
279
- #We dont want to allow separate instances for templates of same group name since the only difference is additional descriptions
280
- if ! $boxes[:data].has_key?(boxgroup)
281
- $boxes[:data][boxgroup] = Array.new()
282
-
283
- $boxes[:data][boxgroup].push(
284
- {
285
- :type => :template,
286
- :name => os["template"]["operatingSystemReferenceCode"],
287
- :description => [ os["itemPrice"]["item"]["description"] ]
288
- }
289
- )
290
- else
291
- $boxes[:data][boxgroup][0][:description].push(os["itemPrice"]["item"]["description"])
292
- end
293
- end
294
- end
295
-
296
- $boxes[:images].each_key do |image_view|
297
- if $boxes[:images][image_view]
298
- $sl[:data][(image_view == :public ? :public_images : :private_images)].each do |image|
299
- boxgroup = image["name"].strip.gsub(/[()]/,'').gsub(/[^a-zA-Z0-9_.-]/, '_').upcase
300
-
301
- $boxes[:data][boxgroup] = Array.new() if ! $boxes[:data].has_key?(boxgroup)
302
- $boxes[:data][boxgroup].push(
303
- {
304
- :type => :block_image,
305
- :name => image["name"],
306
- :global_id => image["globalIdentifier"],
307
- :note => image["note"].to_s,
308
- :summary => image["summary"].to_s,
309
- :visibility => image_view
310
- }
311
- )
312
- end
313
- end
314
- end
315
- end
316
-
317
- def filterBoxes()
318
- if ! $boxes[:filter].nil?
319
- $boxes[:data].each_key do |boxgroup|
320
- begin
321
- $boxes[:data][boxgroup].delete_if { |box| ! box[:name].match($boxes[:filter]) }
322
- rescue Exception => e
323
- $stderr.puts "ERROR: Failed to filter Softlayer boxes: " + e.message
324
- exit 1
325
- end
326
- end
327
- end
328
- end
329
-
330
- def saveBoxFiles()
331
- begin
332
- File.open($boxes[:metadata_path], "w", 0644) { |fout| fout.puts $boxes[:file_templates][:metadata_json] }
333
- rescue Exception => e
334
- $stderr.puts "ERROR: Failed to save box metadata JSON data: " + e.message
335
- exit 1
336
- end
337
-
338
- $boxes[:data].each_key do |boxgroup|
339
- $boxes[:data][boxgroup].each do |box|
340
- boxFileName = ($boxes[:data][boxgroup].length > 1 ? boxgroup + "_" + ($boxes[:data][boxgroup].index(box) + 1).to_s : boxgroup)
341
-
342
- begin
343
- File.open(File.join($boxes[:dir][:vagrantfiles_root], boxFileName + ".vagrantfile"), "w", 0644) do |fout|
344
- fout.puts $boxes[:file_templates][:created_by]
345
- fout.puts
346
-
347
- fout.puts $boxes[:file_templates][:os_templates_avail] if box[:type] == :template
348
- fout.puts box[:description].map { |descr| "# " + descr }.join("\n") if box[:type] == :template
349
-
350
- fout.puts $boxes[:file_templates][:image_detail] if box[:type] == :block_image
351
- fout.puts "# Global Identifier: " + box[:global_id].to_s if box[:type] == :block_image
352
- fout.puts "# Name: " + box[:name] if box[:type] == :block_image
353
- fout.puts "# Summary: " + box[:summary].to_s if box[:type] == :block_image
354
- fout.puts "# Note: " + box[:note].to_s if box[:type] == :block_image
355
- fout.puts "# Visibility: " + (box[:visibility] == :public ? "public" : "private" ) if box[:type] == :block_image
356
-
357
- fout.puts
358
- fout.puts $boxes[:file_templates][:vagrant_config_start]
359
- fout.puts $boxes[:file_templates][:indent][4] + $boxes[:file_templates][:sl_config_start]
360
-
361
- fout.puts $boxes[:file_templates][:indent][8] + "sl.datacenter = \"" + $boxes[:datacenter] + "\"" if $boxes[:datacenter]
362
- fout.puts $boxes[:file_templates][:indent][8] + "sl.domain = \"" + $boxes[:domain] + "\"" if $boxes[:domain]
363
- fout.puts $boxes[:file_templates][:indent][8] + "sl.hourly_billing = false" if $boxes[:monthly_billing]
364
- fout.puts $boxes[:file_templates][:indent][8] + "sl.operating_system = \"" + box[:name] + "\"" if box[:type] == :template
365
- fout.puts $boxes[:file_templates][:indent][8] + "sl.image_guid = \"" + box[:global_id].to_s + "\"" if box[:type] == :block_image
366
- fout.puts $boxes[:file_templates][:indent][8] + "sl.vlan_private = \"" + $boxes[:vlan_private] + "\"" if $boxes[:vlan_private]
367
- fout.puts $boxes[:file_templates][:indent][8] + "sl.vlan_public = \"" + $boxes[:vlan_public] + "\"" if $boxes[:vlan_public]
368
-
369
- fout.puts $boxes[:file_templates][:indent][4] + $boxes[:file_templates][:end]
370
- fout.puts $boxes[:file_templates][:end]
371
- end
372
- rescue Exception => e
373
- $stderr.puts "ERROR: Failed to save box Vagrantfile: " + e.message
374
- exit 1
375
- end
376
- end
377
- end
378
- end
379
-
380
- def createBoxes()
381
- $boxes[:data].each_key do |boxgroup|
382
- $boxes[:data][boxgroup].each do |box|
383
- boxFileName = ($boxes[:data][boxgroup].length > 1 ? boxgroup + "_" + ($boxes[:data][boxgroup].index(box) + 1).to_s : boxgroup)
384
-
385
- begin
386
- Dir.mktmpdir do |tmpDir|
387
- FileUtils.cd(tmpDir) do
388
- FileUtils.cp($boxes[:metadata_path], File.join(tmpDir,"metadata.json"), :preserve => true)
389
- FileUtils.cp(File.join($boxes[:dir][:vagrantfiles_root], boxFileName + ".vagrantfile"), File.join(tmpDir, "Vagrantfile"), :preserve => true)
390
-
391
- result = %x(tar -czvf "#{File.join($boxes[:dir][:boxes_root], boxFileName + ".box.tar.gz")}" Vagrantfile metadata.json)
392
-
393
- raise result if $?.exitstatus != 0
394
-
395
- FileUtils.mv(File.join($boxes[:dir][:boxes_root], boxFileName + ".box.tar.gz"), File.join($boxes[:dir][:boxes_root], boxFileName + ".box"), :force => true)
396
- FileUtils.chmod(0644, File.join($boxes[:dir][:boxes_root], boxFileName + ".box"))
397
- end
398
- end
399
- rescue Exception => e
400
- $stderr.puts "ERROR: Failed to save Vagrant box: " + e.message
401
- exit 1
402
- end
403
- end
404
- end
405
- end
406
-
407
- def main()
408
- procCliOptions()
409
- procCliArgs()
410
- getBoxData()
411
- filterBoxes()
412
- saveBoxFiles()
413
- createBoxes()
414
- end
415
-
416
- main
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ require 'tmpdir'
5
+
6
+ require 'rubygems'
7
+ require 'softlayer_api'
8
+
9
+ $sl = {
10
+ :data => {
11
+ :create_opts => nil,
12
+ :private_images => nil,
13
+ :public_images => nil
14
+ },
15
+ :service => {
16
+ :account => nil,
17
+ :virtual_guest => nil,
18
+ :virtual_guest_block_device_template_group => nil
19
+ },
20
+ :sl_credentials => {
21
+ :api_key => nil,
22
+ :endpoint_url => SoftLayer::API_PUBLIC_ENDPOINT,
23
+ :timeout => 60,
24
+ :user_agent => "vagrant-softlayer",
25
+ :username => nil
26
+ }
27
+ }
28
+
29
+ $boxes = {
30
+ :data => {},
31
+ :datacenter => nil,
32
+ :dir => {
33
+ :boxes => "boxes",
34
+ :vagrantfiles => "vagrantfiles",
35
+ :boxes_root => nil,
36
+ :output_root => File.expand_path("."),
37
+ :vagrantfiles_root => nil
38
+ },
39
+ :domain => nil,
40
+ :file_templates => {
41
+ :created_by => "# vagrant-softlayer-boxes automatic template export",
42
+ :end => "end",
43
+ :image_detail => "# Compute or flex image details:",
44
+ :indent => Hash.new() { |hash, key| hash[key] = " " * key },
45
+ :metadata_json => "{\"provider\": \"softlayer\"}",
46
+ :os_templates_avail => "# Available versions of this OS template (based on VM property selections):",
47
+ :sl_config_start => "config.vm.provider :softlayer do |sl|",
48
+ :vagrant_config_start => "Vagrant.configure(\"2\") do |config|"
49
+ },
50
+ :filter => nil,
51
+ :images => {
52
+ :private => true,
53
+ :public => true
54
+ },
55
+ :metadata_path => nil,
56
+ :monthly_billing => nil,
57
+ :templates => true,
58
+ :vlan_private => nil,
59
+ :vlan_public => nil
60
+ }
61
+
62
+ def procCliOptions()
63
+ opts = GetoptLong.new(
64
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
65
+ [ '--output_dir', '-o', GetoptLong::REQUIRED_ARGUMENT ],
66
+ [ '--only_images', '-i', GetoptLong::NO_ARGUMENT ],
67
+ [ '--only_private_images', '-p', GetoptLong::NO_ARGUMENT ],
68
+ [ '--only_public_images', '-g', GetoptLong::NO_ARGUMENT ],
69
+ [ '--only_templates', '-t', GetoptLong::NO_ARGUMENT ],
70
+ [ '--default_domain', GetoptLong::REQUIRED_ARGUMENT ],
71
+ [ '--default_datacenter', GetoptLong::REQUIRED_ARGUMENT ],
72
+ [ '--default_monthly_billing', GetoptLong::NO_ARGUMENT ],
73
+ [ '--default_vlan_private', GetoptLong::REQUIRED_ARGUMENT ],
74
+ [ '--default_vlan_public', GetoptLong::REQUIRED_ARGUMENT ],
75
+ [ '--sl_api_key', '-k', GetoptLong::REQUIRED_ARGUMENT ],
76
+ [ '--sl_endpoint_url', '-e', GetoptLong::REQUIRED_ARGUMENT ],
77
+ [ '--sl_username','-u', GetoptLong::REQUIRED_ARGUMENT ]
78
+ )
79
+
80
+ opts.each do | opt, optval |
81
+ case opt
82
+ when '--help'
83
+ puts <<-EOF
84
+ vagrant-softlayer-boxes [OPTION] [FILTER]
85
+
86
+ --help, -h:
87
+ Print this help.
88
+
89
+ --sl_username USERNAME, -u USERNAME:
90
+ Sets the SoftLayer account user name. If not specified, it is assumed SL_API_USERNAME environment variable is set.
91
+
92
+ --sl_api_key SL_API_KEY, -k SL_API_KEY:
93
+ Sets the SoftLayer API key. If not specified, it is assumed SL_API_KEY environment variable is set.
94
+
95
+ --sl_endpoint_url SL_API_BASE_URL, -e SL_API_BASE_URL:
96
+ Sets the SoftLayer endpoint URL. If not specified, it assumed SL_API_BASE_URL environment variable is set to API_PUBLIC_ENDPOINT or API_PRIVATE_ENDPOINT.
97
+ Defaults to API_PUBLIC_ENDPOINT.
98
+
99
+ --output_dir OUTPUTDIR, -o OUTPUTDIR:
100
+ Sets the root directory to create box output under.
101
+
102
+ --only_templates, -t:
103
+ Only create boxes for the CCI templates and not compute or flex images.
104
+
105
+ --only_images, -i:
106
+ Only create boxes for the compute or flex images and not the CCI templates.
107
+
108
+ --only_public_images, -g:
109
+ Only create boxes for the public compute or flex images and not the CCI templates.
110
+
111
+ --only_private_images, -p:
112
+ Only create boxes for the private compute or flex images and not the CCI templates.
113
+
114
+ --default_domain:
115
+ Set default vm domain.
116
+
117
+ --default_datacenter:
118
+ Set default vm datacenter.
119
+
120
+ --default_monthly_billing:
121
+ Set default billing type to monthly.
122
+
123
+ --default_vlan_private:
124
+ Set default vm private vlan.
125
+
126
+ --default_vlan_public:
127
+ Set default vm public vlan.
128
+
129
+ FILTER
130
+ String used to filter template and compute/flex images by name to export boxes for specific matches. Supports filtering by regular expression.
131
+
132
+ EOF
133
+
134
+ exit 0
135
+
136
+ when '--sl_username'
137
+ $sl[:sl_credentials][:username] = optval.to_s
138
+
139
+ when '--sl_api_key'
140
+ $sl[:sl_credentials][:api_key] = optval.to_s
141
+
142
+ when '--sl_endpoint_url'
143
+ if ! [ "API_PUBLIC_ENDPOINT", "API_PRIVATE_ENDPOINT" ].include?(optval.to_s.upcase)
144
+ $stderr.puts "ERROR: Invalid endpoint_url value: " + optval.to_s.upcase
145
+ exit 2
146
+ end
147
+
148
+ $sl[:sl_credentials][:endpoint_url] = (optval.to_s.upcase == 'API_PUBLIC_ENDPOINT' ? SoftLayer::API_PUBLIC_ENDPOINT : SoftLayer::API_PRIVATE_ENDPOINT )
149
+
150
+ when '--output_dir'
151
+ if File.exists?(optval.to_s) && File.ftype(optval.to_s) != 'directory'
152
+ $stderr.puts "ERROR: Path is not a directory: " + optval.to_s
153
+ exit 2
154
+ end
155
+
156
+ $boxes[:dir][:output_root] = File.expand_path(optval.to_s)
157
+ $boxes[:dir][:vagrantfiles_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles])
158
+ $boxes[:dir][:boxes_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:boxes])
159
+
160
+ if File.exists?($boxes[:dir][:vagrantfiles_root]) && File.ftype($boxes[:dir][:vagrantfiles_root]) != 'directory'
161
+ $stderr.puts "ERROR: Output directory subdir is not a directory: " + $boxes[:dir][:vagrantfiles_root]
162
+ exit 2
163
+ end
164
+
165
+ if File.exists?($boxes[:dir][:boxes_root]) && File.ftype($boxes[:dir][:boxes_root]) != 'directory'
166
+ $stderr.puts "ERROR: Output directory subdir is not a directory: " + $boxes[:dir][:boxes_root]
167
+ exit 2
168
+ end
169
+
170
+ when '--only_templates'
171
+ $boxes[:images][:public] = false
172
+ $boxes[:images][:private] = false
173
+
174
+ when '--only_images'
175
+ $boxes[:templates] = false
176
+
177
+ when '--only_public_images'
178
+ $boxes[:templates] = false
179
+ $boxes[:images][:private] = false
180
+
181
+ when '--only_private_images'
182
+ $boxes[:templates] = false
183
+ $boxes[:images][:public] = false
184
+
185
+ when '--default_domain'
186
+ $boxes[:domain] = optval.to_s
187
+
188
+ when '--default_datacenter'
189
+ $boxes[:datacenter] = optval.to_s
190
+
191
+ when '--default_monthly_billing'
192
+ $boxes[:monthly_billing] = true
193
+
194
+ when '--default_vlan_private'
195
+ $boxes[:vlan_private] = optval.to_s
196
+
197
+ when'--default_vlan_public'
198
+ $boxes[:vlan_public] = optval.to_s
199
+
200
+ end
201
+ end
202
+
203
+ $boxes[:dir][:vagrantfiles_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles])
204
+ $boxes[:dir][:boxes_root] = File.join($boxes[:dir][:output_root], $boxes[:dir][:boxes])
205
+ $boxes[:metadata_path] = File.join($boxes[:dir][:output_root], "metadata.json")
206
+
207
+ begin
208
+ [ $boxes[:dir][:output_root], $boxes[:dir][:vagrantfiles_root], $boxes[:dir][:boxes_root] ].each do |path|
209
+ Dir.mkdir(path, 0755) if ! File.exists?(path)
210
+ end
211
+ rescue Exception => e
212
+ $stderr.puts "ERROR: Failed to create output directories: " + e.message
213
+ exit 1
214
+ end
215
+
216
+ $sl[:sl_credentials][:username] = ENV["SL_API_USERNAME"] if $sl[:sl_credentials][:username].nil? && ENV.include?("SL_API_USERNAME")
217
+ $sl[:sl_credentials][:api_key] = ENV["SL_API_KEY"] if $sl[:sl_credentials][:api_key].nil? && ENV.include?("SL_API_KEY")
218
+
219
+ if $sl[:sl_credentials][:endpoint_url].nil? && ENV.include?("SL_API_BASE_URL")
220
+ if ! [ 'API_PRIVATE_ENDPOINT', 'API_PUBLIC_ENDPOINT' ].include?(ENV["SL_API_BASE_URL"])
221
+ $stderr.puts "ERROR: Invalid SoftLayer endpoint URL specified in environment variable SL_API_BASE_URL, expected one of #{[ 'API_PRIVATE_ENDPOINT', 'API_PUBLIC_ENDPOINT' ].inspect}: #{ENV["SL_API_BASE_URL"].inspect}"
222
+ exit 2
223
+ end
224
+
225
+ $sl[:sl_credentials][:endpoint_url] = (ENV["SL_API_BASE_URL"] == "API_PUBLIC_ENDPOINT" ? SoftLayer::API_PUBLIC_ENDPOINT : SoftLayer::API_PRIVATE_ENDPOINT )
226
+ end
227
+
228
+ if $sl[:sl_credentials][:username].nil?
229
+ $stderr.puts "ERROR: No SoftLayer username specified"
230
+ exit 2
231
+ end
232
+
233
+ if $sl[:sl_credentials][:username].nil?
234
+ $stderr.puts "ERROR: No SoftLayer user name specified"
235
+ exit 2
236
+ end
237
+
238
+ if $sl[:sl_credentials][:api_key].nil?
239
+ $stderr.puts "ERROR: No SoftLayer API key specified"
240
+ exit 2
241
+ end
242
+ end
243
+
244
+ def procCliArgs()
245
+ if ARGV.length > 2
246
+ $stderr.puts "ERROR: Invalid argument supplied, please check help"
247
+ exit 2
248
+ elsif ARGV.length == 1
249
+ begin
250
+ $boxes[:filter] = Regexp.new(ARGV[0], Regexp::IGNORECASE)
251
+ rescue Exception => e
252
+ $stderr.puts "ERROR: Filter value is not a valid regular expression: " + e.message
253
+ exit 2
254
+ end
255
+ end
256
+ end
257
+
258
+ def getBoxData()
259
+ begin
260
+ $sl[:service][:virtual_guest] = SoftLayer::Client.new($sl[:sl_credentials])["SoftLayer_Virtual_Guest"] if $boxes[:templates]
261
+ $sl[:service][:account] = SoftLayer::Client.new($sl[:sl_credentials])["SoftLayer_Account"] if $boxes[:images][:private]
262
+ $sl[:service][:virtual_guest_block_device_template_group] = SoftLayer::Client.new($sl[:sl_credentials])["SoftLayer_Virtual_Guest_Block_Device_Template_Group"] if $boxes[:images][:public]
263
+ rescue Exception => e
264
+ $stderr.puts "ERROR: Failed to create SoftLayer service object: " + e.message
265
+ exit 1
266
+ end
267
+
268
+ begin
269
+ $sl[:data][:create_opts] = $sl[:service][:virtual_guest].getCreateObjectOptions if $boxes[:templates]
270
+ $sl[:data][:private_images] = $sl[:service][:account].getBlockDeviceTemplateGroups.delete_if { |block_device| ! block_device.has_key?("globalIdentifier") } if $boxes[:images][:private]
271
+ $sl[:data][:public_images] = $sl[:service][:virtual_guest_block_device_template_group].getPublicImages.delete_if { |block_device| ! block_device.has_key?("globalIdentifier") } if $boxes[:images][:public]
272
+ rescue Exception => e
273
+ $stderr.puts "ERROR: Failed to retrieve SoftLayer service data: " + e.message
274
+ exit 1
275
+ end
276
+
277
+ if $boxes[:templates]
278
+ $sl[:data][:create_opts]["operatingSystems"].each do |os|
279
+ boxgroup = os["template"]["operatingSystemReferenceCode"].strip.gsub(/[()]/,'').gsub(/[^a-zA-Z0-9_.-]/, '_').upcase
280
+
281
+ #We dont want to allow separate instances for templates of same group name since the only difference is additional descriptions
282
+ if ! $boxes[:data].has_key?(boxgroup)
283
+ $boxes[:data][boxgroup] = Array.new()
284
+
285
+ $boxes[:data][boxgroup].push(
286
+ {
287
+ :type => :template,
288
+ :name => os["template"]["operatingSystemReferenceCode"],
289
+ :description => [ os["itemPrice"]["item"]["description"] ]
290
+ }
291
+ )
292
+ else
293
+ $boxes[:data][boxgroup][0][:description].push(os["itemPrice"]["item"]["description"])
294
+ end
295
+ end
296
+ end
297
+
298
+ $boxes[:images].each_key do |image_view|
299
+ if $boxes[:images][image_view]
300
+ $sl[:data][(image_view == :public ? :public_images : :private_images)].each do |image|
301
+ boxgroup = image["name"].strip.gsub(/[()]/,'').gsub(/[^a-zA-Z0-9_.-]/, '_').upcase
302
+
303
+ $boxes[:data][boxgroup] = Array.new() if ! $boxes[:data].has_key?(boxgroup)
304
+ $boxes[:data][boxgroup].push(
305
+ {
306
+ :type => :block_image,
307
+ :name => image["name"],
308
+ :global_id => image["globalIdentifier"],
309
+ :note => image["note"].to_s,
310
+ :summary => image["summary"].to_s,
311
+ :visibility => image_view
312
+ }
313
+ )
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ def filterBoxes()
320
+ if ! $boxes[:filter].nil?
321
+ $boxes[:data].each_key do |boxgroup|
322
+ begin
323
+ $boxes[:data][boxgroup].delete_if { |box| ! box[:name].match($boxes[:filter]) }
324
+ rescue Exception => e
325
+ $stderr.puts "ERROR: Failed to filter Softlayer boxes: " + e.message
326
+ exit 1
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ def saveBoxFiles()
333
+ begin
334
+ File.open($boxes[:metadata_path], "w", 0644) { |fout| fout.puts $boxes[:file_templates][:metadata_json] }
335
+ rescue Exception => e
336
+ $stderr.puts "ERROR: Failed to save box metadata JSON data: " + e.message
337
+ exit 1
338
+ end
339
+
340
+ $boxes[:data].each_key do |boxgroup|
341
+ $boxes[:data][boxgroup].each do |box|
342
+ boxFileName = ($boxes[:data][boxgroup].length > 1 ? boxgroup + "_" + ($boxes[:data][boxgroup].index(box) + 1).to_s : boxgroup)
343
+
344
+ begin
345
+ File.open(File.join($boxes[:dir][:vagrantfiles_root], boxFileName + ".vagrantfile"), "w", 0644) do |fout|
346
+ fout.puts $boxes[:file_templates][:created_by]
347
+ fout.puts
348
+
349
+ fout.puts $boxes[:file_templates][:os_templates_avail] if box[:type] == :template
350
+ fout.puts box[:description].map { |descr| "# " + descr }.join("\n") if box[:type] == :template
351
+
352
+ fout.puts $boxes[:file_templates][:image_detail] if box[:type] == :block_image
353
+ fout.puts "# Global Identifier: " + box[:global_id].to_s if box[:type] == :block_image
354
+ fout.puts "# Name: " + box[:name] if box[:type] == :block_image
355
+ fout.puts "# Summary: " + box[:summary].to_s if box[:type] == :block_image
356
+ fout.puts "# Note: " + box[:note].to_s if box[:type] == :block_image
357
+ fout.puts "# Visibility: " + (box[:visibility] == :public ? "public" : "private" ) if box[:type] == :block_image
358
+
359
+ fout.puts
360
+ fout.puts $boxes[:file_templates][:vagrant_config_start]
361
+ fout.puts $boxes[:file_templates][:indent][4] + $boxes[:file_templates][:sl_config_start]
362
+
363
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.datacenter = \"" + $boxes[:datacenter] + "\"" if $boxes[:datacenter]
364
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.domain = \"" + $boxes[:domain] + "\"" if $boxes[:domain]
365
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.hourly_billing = false" if $boxes[:monthly_billing]
366
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.operating_system = \"" + box[:name] + "\"" if box[:type] == :template
367
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.image_guid = \"" + box[:global_id].to_s + "\"" if box[:type] == :block_image
368
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.vlan_private = \"" + $boxes[:vlan_private] + "\"" if $boxes[:vlan_private]
369
+ fout.puts $boxes[:file_templates][:indent][8] + "sl.vlan_public = \"" + $boxes[:vlan_public] + "\"" if $boxes[:vlan_public]
370
+
371
+ fout.puts $boxes[:file_templates][:indent][4] + $boxes[:file_templates][:end]
372
+ fout.puts $boxes[:file_templates][:end]
373
+ end
374
+ rescue Exception => e
375
+ $stderr.puts "ERROR: Failed to save box Vagrantfile: " + e.message
376
+ exit 1
377
+ end
378
+ end
379
+ end
380
+ end
381
+
382
+ def createBoxes()
383
+ $boxes[:data].each_key do |boxgroup|
384
+ $boxes[:data][boxgroup].each do |box|
385
+ boxFileName = ($boxes[:data][boxgroup].length > 1 ? boxgroup + "_" + ($boxes[:data][boxgroup].index(box) + 1).to_s : boxgroup)
386
+
387
+ begin
388
+ Dir.mktmpdir do |tmpDir|
389
+ FileUtils.cd(tmpDir) do
390
+ FileUtils.cp($boxes[:metadata_path], File.join(tmpDir,"metadata.json"), :preserve => true)
391
+ FileUtils.cp(File.join($boxes[:dir][:vagrantfiles_root], boxFileName + ".vagrantfile"), File.join(tmpDir, "Vagrantfile"), :preserve => true)
392
+
393
+ result = %x(tar -czvf "#{File.join($boxes[:dir][:boxes_root], boxFileName + ".box.tar.gz")}" Vagrantfile metadata.json)
394
+
395
+ raise result if $?.exitstatus != 0
396
+
397
+ FileUtils.mv(File.join($boxes[:dir][:boxes_root], boxFileName + ".box.tar.gz"), File.join($boxes[:dir][:boxes_root], boxFileName + ".box"), :force => true)
398
+ FileUtils.chmod(0644, File.join($boxes[:dir][:boxes_root], boxFileName + ".box"))
399
+ end
400
+ end
401
+ rescue Exception => e
402
+ $stderr.puts "ERROR: Failed to save Vagrant box: " + e.message
403
+ exit 1
404
+ end
405
+ end
406
+ end
407
+ end
408
+
409
+ def main()
410
+ procCliOptions()
411
+ procCliArgs()
412
+ getBoxData()
413
+ filterBoxes()
414
+ saveBoxFiles()
415
+ createBoxes()
416
+ end
417
+
418
+ main