knife-softlayer 0.1.3 → 0.2.0.pre.f0ba31b95
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 +8 -8
- data/knife-softlayer.gemspec +1 -1
- data/lib/chef/knife/flavor/base.rb +9 -134
- data/lib/chef/knife/softlayer_base.rb +31 -95
- data/lib/chef/knife/softlayer_datacenter_list.rb +28 -0
- data/lib/chef/knife/softlayer_datacenter_show.rb +45 -0
- data/lib/chef/knife/softlayer_flavor_list.rb +16 -7
- data/lib/chef/knife/softlayer_global_ip_list.rb +34 -0
- data/lib/chef/knife/softlayer_image_list.rb +26 -0
- data/lib/chef/knife/softlayer_server_create.rb +170 -77
- data/lib/chef/knife/softlayer_server_destroy.rb +5 -6
- data/lib/chef/knife/softlayer_vlan_create.rb +42 -0
- data/lib/chef/knife/softlayer_vlan_list.rb +28 -0
- data/lib/chef/knife/softlayer_vlan_show.rb +40 -0
- data/lib/knife-softlayer/version.rb +1 -1
- data/spec/unit/softlayer_base_spec.rb +5 -4
- data/spec/unit/softlayer_server_create_spec.rb +52 -84
- data/spec/unit/softlayer_server_destroy_spec.rb +29 -53
- metadata +14 -7
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerGlobalIpList < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer global ips list (options)'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
|
21
|
+
if connection(:network).get_global_ip_records.body.empty?
|
22
|
+
puts ui.color("No global ip addresses found.", :green)
|
23
|
+
else
|
24
|
+
puts ui.color("This operation can take several minutes. ", :yellow)
|
25
|
+
table_data = connection(:network).ips.map do |ip|
|
26
|
+
{:address => ip.address, :destination => ip.destination_ip.respond_to?(:address) ? ip.destination_ip.address : 'NOT ROUTED'} if ip.global?
|
27
|
+
end.compact
|
28
|
+
puts Formatador.display_table(table_data, [:address, :destination])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerImageList < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer image list'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
table_data = connection(:compute).images.map { |i| {:id => i.id, :name => i.name, :access => i.public? ? 'PUBLIC' : 'PRIVATE' } }
|
21
|
+
puts Formatador.display_table(table_data, [:id, :access, :name,])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -6,7 +6,6 @@
|
|
6
6
|
#
|
7
7
|
|
8
8
|
require 'chef/knife/softlayer_base'
|
9
|
-
require 'chef/knife/flavor/base'
|
10
9
|
|
11
10
|
class Chef
|
12
11
|
class Knife
|
@@ -33,31 +32,28 @@ class Chef
|
|
33
32
|
:long => '--domain VALUE',
|
34
33
|
:short => '-D VALUE',
|
35
34
|
:description => 'The FQDN SoftLayer will assign to the VM instance.',
|
36
|
-
:default =>
|
35
|
+
:default => Chef::Config[:knife][:softlayer_default_domain]
|
37
36
|
|
38
37
|
option :cores,
|
39
38
|
:long => '--cores VALUE',
|
40
39
|
:short => '-C VALUE',
|
41
|
-
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
42
|
-
:default => 1
|
40
|
+
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
43
41
|
|
44
42
|
option :os_code,
|
45
43
|
:long => '--os-code VALUE',
|
46
44
|
:short => '-O VALUE',
|
47
|
-
:description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.'
|
48
|
-
:default => 'UBUNTU_LATEST'
|
45
|
+
:description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.'
|
49
46
|
|
50
47
|
option :ram,
|
51
48
|
:long => '--ram VALUE',
|
52
49
|
:short => '-R VALUE',
|
53
|
-
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
54
|
-
|
50
|
+
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
51
|
+
|
55
52
|
|
56
53
|
option :block_storage,
|
57
54
|
:long => '--block-storage VALUE',
|
58
55
|
:short => '-B VALUE',
|
59
|
-
:description => 'The size in GB of the block storage devices (disks) for this instance. Specify 1 - 5 entries in a comma separated list following the format "dev:size". Example: "0:25,2:500" would be a 25GB volume on device 0 (the root partition) and a 100GB volume on on device 2. [NOTE: SoftLayer VMs always reserve device 1 for a swap device.] '
|
60
|
-
:default => '0:25'
|
56
|
+
:description => 'The size in GB of the block storage devices (disks) for this instance. Specify 1 - 5 entries in a comma separated list following the format "dev:size". Example: "0:25,2:500" would be a 25GB volume on device 0 (the root partition) and a 100GB volume on on device 2. [NOTE: SoftLayer VMs always reserve device 1 for a swap device.] '
|
61
57
|
|
62
58
|
option :nic,
|
63
59
|
:long => '--network-interface-speed VALUE',
|
@@ -71,11 +67,32 @@ class Chef
|
|
71
67
|
:boolean => true,
|
72
68
|
:default => false
|
73
69
|
|
74
|
-
option :
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
70
|
+
option :vlan,
|
71
|
+
:long => '--vlan VLAN-ID',
|
72
|
+
:description => 'Internal SoftLayer ID of the public VLAN into which the compute instance should be placed.'
|
73
|
+
|
74
|
+
option :private_vlan,
|
75
|
+
:long => '--private-vlan VLAN-ID',
|
76
|
+
:description => 'Internal SoftLayer ID of the private VLAN into which the compute instance should be placed.'
|
77
|
+
|
78
|
+
option :image_id,
|
79
|
+
:long => '--image-id IMAGE-ID',
|
80
|
+
:description => 'Internal SoftLayer uuid specifying the image template from which the compute instance should be booted.'
|
81
|
+
|
82
|
+
option :private_network_only,
|
83
|
+
:long => '--private-network-only',
|
84
|
+
:description => 'Flag to be passed when the compute instance should have no public facing network interface.',
|
85
|
+
:boolean => true
|
86
|
+
|
87
|
+
option :from_file,
|
88
|
+
:long => '--from-file PATH',
|
89
|
+
:description => 'Path to JSON file containing arguments for provisoning and bootstrap.'
|
90
|
+
|
91
|
+
#option :single_tenant,
|
92
|
+
# :long => '--single-tenant',
|
93
|
+
# :description => 'Create a VM with a dedicated physical host.',
|
94
|
+
# :boolean => true,
|
95
|
+
# :default => false
|
79
96
|
|
80
97
|
option :local_storage,
|
81
98
|
:long => '--local-storage',
|
@@ -85,7 +102,8 @@ class Chef
|
|
85
102
|
|
86
103
|
option :datacenter,
|
87
104
|
:long => '--datacenter VALUE',
|
88
|
-
:description => 'Create a VM in a particular datacenter.'
|
105
|
+
:description => 'Create a VM in a particular datacenter.',
|
106
|
+
:default => Chef::Config[:knife][:softlayer_default_datacenter]
|
89
107
|
|
90
108
|
option :tags,
|
91
109
|
:short => "-T T=V[,T=V,...]",
|
@@ -99,7 +117,6 @@ class Chef
|
|
99
117
|
:description => "The Chef node name for your new node",
|
100
118
|
:proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
|
101
119
|
|
102
|
-
|
103
120
|
option :ssh_user,
|
104
121
|
:short => "-x USERNAME",
|
105
122
|
:long => "--ssh-user USERNAME",
|
@@ -111,6 +128,12 @@ class Chef
|
|
111
128
|
:long => "--ssh-password PASSWORD",
|
112
129
|
:description => "The ssh password"
|
113
130
|
|
131
|
+
option :ssh_keys,
|
132
|
+
:short => "-S KEY",
|
133
|
+
:long => "--ssh-keys KEY",
|
134
|
+
:description => "The ssh keys for the SoftLayer Virtual Guest environment. Accepts a space separated list of integers.",
|
135
|
+
:proc => Proc.new { |ssh_keys| ssh_keys.split(' ').map { |k| {:id => k}} }
|
136
|
+
|
114
137
|
option :ssh_port,
|
115
138
|
:short => "-p PORT",
|
116
139
|
:long => "--ssh-port PORT",
|
@@ -203,7 +226,7 @@ class Chef
|
|
203
226
|
:default => nil
|
204
227
|
|
205
228
|
option :new_global_ip,
|
206
|
-
:long => "--new-global-ip",
|
229
|
+
:long => "--new-global-ip VERSION",
|
207
230
|
:description => "Order a new SoftLayer Global IP address and assign it to the instance."
|
208
231
|
|
209
232
|
option :hint,
|
@@ -219,94 +242,127 @@ class Chef
|
|
219
242
|
# Run the procedure to create a SoftLayer VM and bootstrap it.
|
220
243
|
# @return [nil]
|
221
244
|
def run
|
222
|
-
|
223
245
|
$stdout.sync = true
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
if config[:
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
246
|
+
config.merge!(slurp_from_file(config[:from_file])) if config[:from_file]
|
247
|
+
|
248
|
+
# TODO: Windows support.
|
249
|
+
raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}" if config[:os_code] =~ /^WIN_/
|
250
|
+
raise SoftlayerServerCreateError, "#{ui.color("identity file (-i) option is incompatible with password (-P) option required.", :red)}" if !!config[:identity_file] and !!config[:ssh_password]
|
251
|
+
raise SoftlayerServerCreateError, "#{ui.color("--new-global-ip value must be 'v4' or 'v6'.", :red)}" if config[:new_global_ip] and !config[:new_global_ip].to_s.match(/^v[4,6]$/i)
|
252
|
+
|
253
|
+
# TODO: create a pre-launch method for clean up tasks.
|
254
|
+
# TODO: create a pre-launch method for clean up tasks.
|
255
|
+
config[:vlan] = config[:vlan].to_i if config[:vlan]
|
256
|
+
config[:private_vlan] = config[:private_vlan].to_i if config[:private_vlan]
|
257
|
+
Fog.credentials[:private_key_path] = config[:identity_file] if config[:identity_file]
|
258
|
+
# TODO: create a pre-launch method for clean up tasks.
|
259
|
+
# TODO: create a pre-launch method for clean up tasks.
|
260
|
+
|
261
|
+
opts = {
|
262
|
+
:flavor => :flavor_id,
|
263
|
+
:hostname => :name,
|
264
|
+
:domain => nil,
|
265
|
+
:cores => :cpu,
|
266
|
+
:os_code => nil,
|
267
|
+
:ram => nil,
|
268
|
+
:block_storage => :disk,
|
269
|
+
:local_storage => :ephemeral_storage,
|
270
|
+
:datacenter => nil,
|
271
|
+
:ssh_keys => :key_pairs,
|
272
|
+
:vlan => nil,
|
273
|
+
:private_vlan => nil,
|
274
|
+
:image_id => nil,
|
275
|
+
:private_network_only => nil,
|
276
|
+
#:tags => nil
|
277
|
+
}
|
278
|
+
|
279
|
+
|
280
|
+
opts.keys.each do |opt|
|
281
|
+
if opts[opt].nil?
|
282
|
+
opts[opt] = config[opt]
|
283
|
+
else
|
284
|
+
opts[opts.delete(opt)] = config[opt] # clever shit like this is why I like programming :-]
|
285
|
+
end
|
238
286
|
end
|
239
287
|
|
240
|
-
|
241
|
-
@template['hostname'] = config[:hostname]
|
242
|
-
@template['domain'] = config[:domain]
|
243
|
-
@template['dedicatedAccountHostOnlyFlag'] = config[:single_tenant]
|
244
|
-
@template['operatingSystemReferenceCode'] = config[:os_code]
|
245
|
-
@template['hourlyBillingFlag'] = !config[:bill_monthly]
|
246
|
-
@template['networkComponents'] = [{ 'maxSpeed' => config[:nic]}]
|
247
|
-
|
248
|
-
@template['datacenter'] = { 'name' => config[:datacenter] } if config[:datacenter]
|
249
|
-
|
250
|
-
@response = connection.createObject(@template)
|
288
|
+
opts.delete_if { |k,v| v.nil? }
|
251
289
|
|
252
290
|
puts ui.color("Launching SoftLayer VM, this may take a few minutes.", :green)
|
291
|
+
instance = connection.servers.create(opts)
|
292
|
+
progress Proc.new { instance.wait_for { ready? and sshable? } }
|
293
|
+
putc("\n")
|
294
|
+
|
295
|
+
if config[:tags]
|
296
|
+
puts ui.color("Applying tags to SoftLayer instance.", :green)
|
297
|
+
progress Proc.new { instance.add_tags(config[:tags]) }
|
298
|
+
putc("\n")
|
299
|
+
end
|
253
300
|
|
254
|
-
begin
|
255
|
-
@cci = connection.object_mask('mask.operatingSystem.passwords.password').object_with_id(@response['id']).getObject
|
256
|
-
sleep 1
|
257
|
-
putc('.')
|
258
|
-
end while @cci['operatingSystem'].nil? or @cci['operatingSystem']['passwords'].empty?
|
259
|
-
|
260
|
-
linux_bootstrap(@cci).run
|
261
301
|
|
262
302
|
if config[:new_global_ip] || config[:assign_global_ip]
|
263
|
-
if config[:new_global_ip]
|
303
|
+
if config[:new_global_ip] # <— the value of this will be v4 or v6
|
264
304
|
begin
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
raise 'Unable to find Global IP Address ID. Address not created.'
|
305
|
+
puts ui.color('Provisioning new Global IP' + config[:new_global_ip].downcase + ', this may take a few minutes.', :green)
|
306
|
+
create_global_ip = Proc.new do
|
307
|
+
existing_records = connection(:network).get_global_ip_records.body
|
308
|
+
connection(:network).send('create_new_global_ip' + config[:new_global_ip].downcase) or raise SoftlayerServerCreateError, "Unable to create new Global IP Address.";
|
309
|
+
sleep 20 # if we look for the new record too quickly it won't be there yet...
|
310
|
+
new_record_global_id = (connection(:network).get_global_ip_records.body - existing_records).reduce['id']
|
311
|
+
connection(:network).ips.select { |ip| ip.global_id == new_record_global_id }.reduce
|
273
312
|
end
|
274
|
-
|
313
|
+
global_ip = progress(create_global_ip) or raise SoftlayerServerCreateError, "Error encountered creating Global IP Address."
|
314
|
+
puts ui.color('Global IP Address successfully created.', :green)
|
315
|
+
rescue SoftlayerServerCreateError => e
|
275
316
|
puts ui.color('We have encountered a problem ordering the requested global IP. The transaction may not have completed.', :red)
|
276
317
|
puts ui.color(e.message, :yellow)
|
277
318
|
end
|
278
319
|
end
|
279
320
|
|
280
321
|
if config[:assign_global_ip]
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
322
|
+
case config[:assign_global_ip].to_s
|
323
|
+
#ipv4
|
324
|
+
when /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/
|
325
|
+
global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
|
326
|
+
#ipv6
|
327
|
+
when /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
|
328
|
+
global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
|
329
|
+
else
|
330
|
+
raise SoftlayerServerCreateError, "--assign-global-ip value must be valid IPv4 or IPv6 address"
|
286
331
|
end
|
332
|
+
global_ip or raise SoftlayerServerCreateError, "Global IP address not found. Please check the address or id supplied and try again."
|
333
|
+
global_ip.reload
|
287
334
|
end
|
288
335
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
336
|
+
route_global_ip = Proc.new do
|
337
|
+
puts ui.color('Routing Global IP Address to Instance.', :green)
|
338
|
+
global_ip.route(connection(:network).ips.by_address(instance.public_ip_address)) or raise SoftlayerServerCreateError, "Global IP address failed to route."
|
339
|
+
puts ui.color('Global IP Address has been assigned.', :green)
|
340
|
+
puts ui.color('Global IP Address will not function without networking rules on the endpoint operating system. See http://knowledgelayer.softlayer.com/learning/global-ip-addresses for details.', :yellow)
|
341
|
+
end
|
342
|
+
progress(route_global_ip)
|
294
343
|
|
295
344
|
end
|
296
345
|
|
346
|
+
puts ui.color('Bootstrapping Chef node, this may take a few minutes.', :green)
|
347
|
+
linux_bootstrap(instance).run
|
348
|
+
|
349
|
+
puts ui.color("Applying tags to Chef node.", :green)
|
350
|
+
progress apply_tags(instance)
|
351
|
+
|
297
352
|
end
|
298
353
|
|
299
|
-
# @param [Hash]
|
354
|
+
# @param [Hash] instance
|
300
355
|
# @return [Chef::Knife::Bootstrap]
|
301
|
-
def linux_bootstrap(
|
356
|
+
def linux_bootstrap(instance)
|
302
357
|
bootstrap = Chef::Knife::Bootstrap.new
|
303
|
-
|
358
|
+
instance.ssh_ip_address = instance.private_ip_address if config[:private_network_only]
|
359
|
+
bootstrap.name_args = [instance.ssh_ip_address]
|
304
360
|
bootstrap.config[:ssh_user] = config[:ssh_user]
|
305
|
-
bootstrap.config[:ssh_password] =
|
361
|
+
bootstrap.config[:ssh_password] = config[:ssh_password] if config[:ssh_password]
|
362
|
+
bootstrap.config[:identity_file] = config[:identity_file] if config[:identity_file]
|
306
363
|
bootstrap.config[:ssh_port] = config[:ssh_port]
|
307
364
|
bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
|
308
|
-
bootstrap.config[:
|
309
|
-
bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || cci['id']
|
365
|
+
bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || instance.id
|
310
366
|
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
311
367
|
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
312
368
|
shared_bootstrap(bootstrap)
|
@@ -337,6 +393,43 @@ class Chef
|
|
337
393
|
# TODO: Windows support....
|
338
394
|
end
|
339
395
|
|
396
|
+
def progress(proc)
|
397
|
+
t = Thread.new { Thread.current[:output] = proc.call }
|
398
|
+
i = 0
|
399
|
+
while t.alive?
|
400
|
+
sleep 0.5
|
401
|
+
putc('.')
|
402
|
+
i += 1
|
403
|
+
putc("\n") if i == 76
|
404
|
+
i = 0 if i == 76
|
405
|
+
end
|
406
|
+
putc("\n")
|
407
|
+
t.join
|
408
|
+
t[:output]
|
409
|
+
end
|
410
|
+
|
411
|
+
def slurp_from_file(path)
|
412
|
+
args = JSON.parse(IO.read(path))
|
413
|
+
args.keys.each { |key| args[key.gsub('-', '_').to_sym] = args.delete(key) }
|
414
|
+
# TODO: Something less ugly than the empty rescue block below. The :proc Procs/Lambdas aren't idempotent...
|
415
|
+
args.keys.each { |key| begin; args[key] = options[key][:proc] ? options[key][:proc].call(args[key]) : args[key]; rescue; end }
|
416
|
+
args
|
417
|
+
end
|
418
|
+
|
419
|
+
def apply_tags(instance)
|
420
|
+
Proc.new do
|
421
|
+
chef = Chef::Search::Query.new
|
422
|
+
chef.search('node', "name:#{locate_config_value(:chef_node_name) || instance.id}") do |n|
|
423
|
+
config[:tags] = [] if config[:tags].nil? # we're going to tag every Chef node with the SL id no matter what
|
424
|
+
config[:tags] << "slid=#{instance.id}"
|
425
|
+
config[:tags].each do |tag|
|
426
|
+
n.tag(tag)
|
427
|
+
end
|
428
|
+
n.save
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
340
433
|
end
|
341
434
|
end
|
342
435
|
end
|
@@ -23,7 +23,7 @@ class Chef
|
|
23
23
|
option :chef_node_name,
|
24
24
|
:short => "-N NAME",
|
25
25
|
:long => "--node-name NAME",
|
26
|
-
:description => "The
|
26
|
+
:description => "The name of the node to be destroyed."
|
27
27
|
|
28
28
|
option :ip_address,
|
29
29
|
:long => "--ip-address ADDRESS",
|
@@ -53,12 +53,11 @@ class Chef
|
|
53
53
|
else
|
54
54
|
raise "#{ui.color("FATAL: Please supply the node name or IP address.", :red)}"
|
55
55
|
end
|
56
|
-
|
57
|
-
@
|
58
|
-
|
56
|
+
@slid = @node.tags.select { |s| s =~ /^slid=/ }.reduce.gsub('slid=', '').to_i
|
57
|
+
@instance = connection.servers.get(@slid)
|
59
58
|
|
60
59
|
@node.nil? and raise "#{ui.color('Chef node not found!', :red)}"
|
61
|
-
@
|
60
|
+
@instance.nil? and raise "#{ui.color('VM instance with IP: ' + config[:ip_address] +' not found!', :red)}"
|
62
61
|
|
63
62
|
|
64
63
|
begin
|
@@ -81,7 +80,7 @@ class Chef
|
|
81
80
|
end
|
82
81
|
|
83
82
|
begin
|
84
|
-
|
83
|
+
@instance.destroy
|
85
84
|
puts ui.color("SoftLayer VM successfully deleted. You are no longer being billed for this instance.", :green)
|
86
85
|
rescue Exception => e
|
87
86
|
err_msg ui.color("ERROR DELETING SOFTLAYER VM. IT'S POSSIBLE YOU ARE STILL BEING BILLED FOR THIS INSTANCE. PLEASE CONTACT SUPPORT FOR FURTHER ASSISTANCE", :red)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerVlanCreate < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer vlan create'
|
17
|
+
|
18
|
+
def run
|
19
|
+
#unless name_args.size == 1
|
20
|
+
# puts ui.color("Specify exactly one vlan to show.", :red)
|
21
|
+
# show_usage
|
22
|
+
# exit 1
|
23
|
+
#end
|
24
|
+
|
25
|
+
$stdout.sync = true
|
26
|
+
|
27
|
+
opts = {
|
28
|
+
:name => ui.ask_question("Enter a vlan name:"),
|
29
|
+
:datacenter => connection(:network).datacenters.by_name(ui.ask_question("Enter a datacenter name:")),
|
30
|
+
:router => {'hostname' => ui.ask_question("Enter a router hostname:")},
|
31
|
+
:network_space => ui.ask_question("Enter a network space:", :default => 'PUBLIC'),
|
32
|
+
}
|
33
|
+
|
34
|
+
vlan = connection(:network).networks.create(opts)
|
35
|
+
|
36
|
+
!!vlan and puts "#{ui.color("VLAN successfully created. Provisioning may take a few minutes to complete.", :green)}"
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|