vagrant-softlayer 0.3.3 → 0.4.0

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