knife-softlayer 0.1.3 → 0.2.0.pre.f0ba31b95

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 => 'example.com'
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
- :default => 1024
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 :single_tenant,
75
- :long => '--single-tenant',
76
- :description => 'Create a VM with a dedicated physical host.',
77
- :boolean => true,
78
- :default => false
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
- config[:os_code] =~ /^WIN_/ and raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}"
226
-
227
- if config[:flavor]
228
- @template = SoftlayerFlavorBase.load_flavor(config[:flavor])
229
- else
230
- @template = {}
231
- @template['startCpus'] = config[:cores]
232
- @template['maxMemory'] = config[:ram]
233
- @template['localDiskFlag'] = config[:local_storage]
234
- @template['blockDevices'] = config[:block_storage].split(',').map do |i|
235
- dev, cap = i.split(':')
236
- {'device' => dev, 'diskImage' => {'capacity' => cap } }
237
- end
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
- @template['complexType'] = SoftlayerBase.cci
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
- order = SoftlayerBase.build_global_ipv4_order
266
- response = connection(:order).placeOrder(order)
267
- global_ip_id = response['placedOrder']['account']['globalIpv4Records'].first['id']
268
-
269
- if global_ip_id
270
- puts ui.color('Global IP Address successfully created.', :green)
271
- else
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
- rescue Exception => e
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
- global_ip_id = connection(:account).object_mask('ipAddress').getGlobalIpv4Records.map do |global_ip|
282
- global_ip['id'] if global_ip['ipAddress']['ipAddress'] == config[:assign_global_ip]
283
- end.compact.first
284
- if global_ip_id
285
- puts ui.color('Global IP Address ID found.', :green)
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
- puts ui.color('Assigning Global IP Address to Instance.', :green)
290
- connection(:global_ip).object_with_id(global_ip_id).route(@cci['primaryIpAddress'])
291
-
292
- puts ui.color('Global IP Address has been assigned.', :green)
293
- 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)
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] cci
354
+ # @param [Hash] instance
300
355
  # @return [Chef::Knife::Bootstrap]
301
- def linux_bootstrap(cci)
356
+ def linux_bootstrap(instance)
302
357
  bootstrap = Chef::Knife::Bootstrap.new
303
- bootstrap.name_args = [cci['primaryIpAddress']]
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] = cci['operatingSystem']['passwords'].first['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[:identity_file] = config[:identity_file]
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 Chef node name for your new node"
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
- @cci = connection.findByIpAddress(config[:ip_address])
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
- @cci.nil? and raise "#{ui.color('VM instance with IP: ' + config[:ip_address] +' not found!', :red)}"
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
- connection.object_with_id(@cci['id']).deleteObject
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