scli 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Sonian Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.org ADDED
@@ -0,0 +1,24 @@
1
+ * Scli
2
+ scli is a command line tool used to access and interface with IBM SmartCloud. It currently has support for creating, viewing and deleting instances, volumes, keys and addresses, etc.
3
+
4
+ ** Documentation
5
+ Please run `scli help`
6
+
7
+ ** Contributions
8
+ Please fork the project, make your commits and do a pull request.
9
+
10
+ ** Configuration
11
+ You can either set ENV var's (IBM_SC_USERNAME and IBM_SC_PASSWORD)
12
+
13
+ OR
14
+
15
+ ~/.scli/config.rb should contain:
16
+
17
+ ibm_username some@gmail.com
18
+ ibm_password mypassword
19
+
20
+ However if you run the tool without this file present it will prompt you for all the information.
21
+
22
+ ** License
23
+ Scli is released under the [[https://github.com/sensu/sensu/blob/master/MIT-LICENSE.txt][MIT license]]. Copyright (c) 2012 Sonian Inc
24
+
data/bin/scli ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+ $: << File.dirname(__FILE__) + '/../lib'
5
+ end
6
+
7
+ require 'scli'
8
+
9
+ Scli::Main.run
data/lib/formatters.rb ADDED
@@ -0,0 +1,146 @@
1
+ module Scli
2
+ def self.word_for_title(string)
3
+ string.gsub("_"," ").gsub(/\w+/) do |word|
4
+ word.capitalize
5
+ end
6
+ end
7
+
8
+ def self.format_table_titles(titles)
9
+ titles.collect do |title|
10
+ word_for_title(title.to_s).green
11
+ end
12
+ end
13
+
14
+ def self.format_state(state)
15
+ case state
16
+ when "Active", "Attached", "Available"
17
+ state.green
18
+ when "Requesting", "Provisioning", "New"
19
+ state.yellow
20
+ else
21
+ state.red
22
+ end
23
+ end
24
+
25
+ def self.format_is_default?(default)
26
+ default ? "True".green : "False".red
27
+ end
28
+
29
+ def self.format_owner(owner)
30
+ (owner.size > 8) ? "#{owner[0..6]}.." : owner
31
+ end
32
+
33
+ def self.format_name(name)
34
+ (name.size > 34) ? "#{name[0..32]}.." : name
35
+ end
36
+
37
+ def self.format_description(description)
38
+ (description.size > 14) ? "#{description[0..12]}.." : description
39
+ end
40
+
41
+ def self.format_capabilities(capable, full = false)
42
+ capabilities = capable.collect do |cap|
43
+ "#{cap['id']} => #{cap['entries']}" unless cap['entries'].size == 0 || cap.nil?
44
+ end
45
+ cap_print = capabilities.reject{|cap| cap.nil?}.join(",")
46
+ full ? cap_print : "#{cap_print[0..12]}.."
47
+ end
48
+
49
+ def self.format_ip(ip)
50
+ return "NA".red if ip.nil? || ip == ""
51
+ first_octet = ip.split(".").first
52
+ first_octet == "10" ? ip.cyan : ip.magenta
53
+ end
54
+
55
+ def self.format_instance_id(instance_id)
56
+ if instance_id.class == String
57
+ (instance_id.nil? || instance_id.to_s == "0") ? "Detached".red : instance_id.green
58
+ elsif instance_id.nil?
59
+ "NA".red
60
+ else
61
+ instance_id.join(",")
62
+ end
63
+ end
64
+
65
+ def self.format_type(instance_type)
66
+ instance_type.to_s.split(".").first
67
+ end
68
+
69
+ def self.format_image_instance_types(instance_types, single = false)
70
+ supported_types = []
71
+ instance_types.each do |it|
72
+ supported_types << ((single) ? it.id : format_type(it.id))
73
+ end
74
+ (single ? supported_types.join("\n") : supported_types.join(","))
75
+ end
76
+
77
+ def self.format_size(vol_size)
78
+ if vol_size.to_i > 1024
79
+ "#{vol_size.to_i / 1024}TB"
80
+ else
81
+ "#{vol_size}GB"
82
+ end
83
+ end
84
+
85
+ def self.format_location(location)
86
+ case location.to_i
87
+ when 41
88
+ "Raleigh"
89
+ when 61
90
+ "Ehningen"
91
+ when 82
92
+ "Boulder"
93
+ when 101
94
+ "Markham"
95
+ when 121
96
+ "Makuhari"
97
+ when 141
98
+ "Singapore"
99
+ else
100
+ location
101
+ end
102
+ end
103
+
104
+ def self.process_data_to_format(object, data_to_print, single = false)
105
+ print_array = []
106
+ data_to_print.each do |data|
107
+ print_array << case data.to_s
108
+ when /state/
109
+ format_state(object.send(data))
110
+ when /supported_instance_types/
111
+ single ? format_image_instance_types(object.send(data), true) : format_image_instance_types(object.send(data))
112
+ when /capabilities/
113
+ single ? format_capabilities(object.send(data), true) : format_capabilities(object.send(data))
114
+ when /default/
115
+ format_is_default?(object.send(data))
116
+ when /owner/
117
+ single ? object.send(data) : format_owner(object.send(data))
118
+ when /name/
119
+ format_name(object.send(data))
120
+ when /description/
121
+ single ? object.send(data) : format_description(object.send(data))
122
+ when "ip"
123
+ format_ip(object.send(data))
124
+ when /volume_ids/
125
+ object.send(data).join(",")
126
+ when /instance_id/
127
+ format_instance_id(object.send(data))
128
+ when /public_key/
129
+ format_description(object.send(data))
130
+ when /type/
131
+ if object.send(data).class == String
132
+ format_type(object.send(data))
133
+ else
134
+ object.send(data)
135
+ end
136
+ when /size/
137
+ format_size(object.send(data))
138
+ when /location/
139
+ format_location(object.send(data))
140
+ else
141
+ object.send(data)
142
+ end
143
+ end
144
+ print_array
145
+ end
146
+ end
data/lib/generic.rb ADDED
@@ -0,0 +1,99 @@
1
+ module Scli
2
+ class Compute
3
+ def initialize(options={})
4
+ cli_opts = Scli.options
5
+ cli_opts.merge!(options)
6
+ ibm_user = (Scli.env_populated?) ? ENV['IBM_SC_USERNAME'] : cli_opts[:ibm_username]
7
+ ibm_pass = (Scli.env_populated?) ? ENV['IBM_SC_PASSWORD'] : cli_opts[:ibm_password]
8
+ @fog_compute = Fog::Compute.new(:ibm_username => ibm_user, :ibm_password => ibm_pass, :provider => 'IBM')
9
+ end
10
+
11
+ def method_missing(method, *args, &block)
12
+ @fog_compute.send(method, *args, &block)
13
+ end
14
+ end
15
+
16
+ class Storage
17
+ def initialize(options={})
18
+ cli_opts = Scli.options
19
+ cli_opts.merge!(options)
20
+ ibm_user = (Scli.env_populated?) ? ENV['IBM_SC_USERNAME'] : cli_opts[:ibm_username]
21
+ ibm_pass = (Scli.env_populated?) ? ENV['IBM_SC_PASSWORD'] : cli_opts[:ibm_password]
22
+ @fog_storage = Fog::Storage.new(:ibm_username => ibm_user, :ibm_password => ibm_pass, :provider => 'IBM')
23
+ end
24
+
25
+ def method_missing(method, *args, &block)
26
+ @fog_storage.send(method, *args, &block)
27
+ end
28
+ end
29
+
30
+ def self.config_file_exists?(config_file_path)
31
+ File.exists?(File.expand_path(config_file_path))
32
+ end
33
+
34
+ def self.generate_config_file(config_file_path)
35
+ puts "Config file #{config_file_path} does not exist, let's create it."
36
+ puts "What is your IBM SC Username?"
37
+ ibm_username = $stdin.gets
38
+ puts "What is your IBM SC Password?"
39
+ ibm_password = $stdin.gets
40
+ Dir.mkdir(File.expand_path(File.dirname(config_file_path))) unless File.exists?(File.expand_path(File.dirname(config_file_path)))
41
+ ibm_config = File.open(File.expand_path(config_file_path), "w")
42
+ ibm_config.puts "ibm_username #{ibm_username}"
43
+ ibm_config.puts "ibm_password #{ibm_password}"
44
+ ibm_config.close
45
+ puts "Config file written."
46
+ end
47
+
48
+ def self.print_object(title, objects, data_to_print, options = {})
49
+ table_to_print = []
50
+ if objects.methods.include?(:each) # Its not a single object
51
+ title = title + "(#{objects.count})"
52
+ objects.each do |object|
53
+ table_to_print << process_data_to_format(object, data_to_print)
54
+ table_to_print << :separator if options[:separator]
55
+ end
56
+ else
57
+ table_to_print << process_data_to_format(objects, data_to_print, true)
58
+ end
59
+ table = Terminal::Table.new :title => title.cyan, :headings => format_table_titles(data_to_print), :rows => table_to_print
60
+ puts table
61
+ end
62
+
63
+ def self.print_volumes(volumes)
64
+ print_object("Volumes", volumes, [:id, :name, :size, :instance_id, :owner, :format, :location_id, :offering_id, :created_at, :state])
65
+ if volumes.class.to_s == "Fog::Storage::IBM::Volumes"
66
+ total_vol_size = 0
67
+ volumes.each do |vol|
68
+ next if vol.size.nil?
69
+ total_vol_size += vol.size.to_i
70
+ end
71
+ puts "Total #{(total_vol_size.to_i / 1024.0).round(2)}Tb of storage"
72
+ end
73
+ end
74
+
75
+ def self.print_servers(servers)
76
+ print_object("Servers", servers, [:id, :name, :ip, :owner, :vlan_id, :volume_ids, :instance_type, :launched_at, :location_id, :state])
77
+ end
78
+
79
+ def self.opt_merge(options, name, data)
80
+ options.merge!({name => data}) unless data.nil?
81
+ end
82
+
83
+ def self.env_populated?
84
+ (ENV['IBM_SC_USERNAME'].nil? || ENV['IBM_SC_PASSWORD'].nil?) ? false : (!ENV['IBM_SC_USERNAME'].empty? && !ENV['IBM_SC_PASSWORD'].empty?)
85
+ end
86
+
87
+ def self.read_config(config_file_path)
88
+ options = {}
89
+ if File.exists?(config_file_path)
90
+ config_file = File.open(config_file_path,'r')
91
+ config_file.each_line do |row|
92
+ option, data = row.split
93
+ options[option.to_sym] = data
94
+ end
95
+ end
96
+ options
97
+ end
98
+ end
99
+
@@ -0,0 +1,29 @@
1
+ module Scli
2
+ def self.attvol
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ volume_id = cli.config[:volume_id]
6
+ instance_id = cli.config[:instance_id]
7
+ if volume_id.nil? || instance_id.nil?
8
+ puts "Instance and volume id's are required, please retry."
9
+ else
10
+ server = Scli::Compute.new.servers.get(instance_id)
11
+ volume = Scli::Storage.new.volumes.get(volume_id)
12
+ if server.nil?
13
+ puts "Could not find server: #{instance_id}"
14
+ elsif volume.nil?
15
+ puts "Could not find volume: #{volume_id}"
16
+ else
17
+ if server.attach(volume_id.to_i) # Note: attach() takes a string, not an object
18
+ print_volumes(volume)
19
+ puts "Is being attached to instance:".red
20
+ print_servers(server)
21
+ else
22
+ puts "Volume could not be attached for some reason..."
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ PLUGINS << ["attvol", "attach-volume", "Volume ID (Req -v), Instance ID (Req -i)", "scli attvol -i 123456 -v 23456"]
@@ -0,0 +1,22 @@
1
+ module Scli
2
+ def self.cadd
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ offering_id = cli.config[:offering_id]
6
+ location_id = cli.config[:location_id]
7
+ vlan_id = cli.config[:vlan_id]
8
+ if offering_id.nil? || location_id.nil?
9
+ puts "No offering and or location id was found, please retry"
10
+ else
11
+ options = vlan_id.nil? ? {} : {:vlan_id => vlan_id}
12
+ response = Scli::Compute.new.create_address(location_id, offering_id, options)
13
+ if response.status == 200
14
+ puts "IP Created successfully: #{response.body.inspect}"
15
+ else
16
+ puts "Failed with #{response.status} error of: #{response.body.inspect}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ PLUGINS << ["cadd", "create-address", "Location ID (Req -l), Offering ID (Req -o), Vlan ID (Opt -V)", "scli cadd -o 20025212 -l 41 (-V XX)"]
@@ -0,0 +1,35 @@
1
+ module Scli
2
+ def self.cin
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ name = cli.config[:name]
6
+ image_id = cli.config[:image_id]
7
+ instance_type = cli.config[:instance_type]
8
+ location_id = cli.config[:location_id]
9
+ #Optional args
10
+ opts = {}
11
+ opt_merge(opts, :key_name, cli.config[:key_id])
12
+ opt_merge(opts, :ip, cli.config[:address_id])
13
+ opt_merge(opts, :volume_id, cli.config[:volume_id])
14
+ opt_merge(opts, :vlan_id, cli.config[:vlan_id])
15
+ opt_merge(opts, :secondary_ip, cli.config[:secondary_address_id])
16
+ opt_merge(opts, :is_mini_ephemeral, cli.config[:mini_ephemeral])
17
+ opt_merge(opts, :configuration_data, cli.config[:configuration_data])
18
+ opt_merge(opts, :anti_collocation_instance, cli.config[:anti_colo])
19
+
20
+ if name.nil? || location_id.nil? || image_id.nil? || instance_type.nil?
21
+ puts "Missing one of the following (name, location_id, image_id OR instance_type)."
22
+ else
23
+ response = Scli::Compute.new.create_instance(name, image_id, instance_type, location_id, opts)
24
+ if response.status == 200
25
+ puts "Instance Created successfully: #{response.body.inspect}"
26
+ else
27
+ puts "Failed with #{response.status} error of: #{response.body.inspect}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ PLUGINS << :separator
34
+ PLUGINS << ["cin", "create-instance", "Name (Req -n), Image ID (Req -m), Instance Type (Req -t), Location ID (Req -l),\nKey name (Req if linux -k), Addr id eth0 (Opt -a), Addr id eth1 (Opt -A),\nVlan ID (Opt -V), Volume ID (Opt -v), Mini Ephemeral (Opt --mini),\nAnti Colo (Opt --anti-colo), Img Config (Opt --config-data)", "scli cin -n 'new_inst' -i 345678 -t 'COP32.1/2048/60' -l 41"]
35
+ PLUGINS << :separator
@@ -0,0 +1,21 @@
1
+ module Scli
2
+ def self.ckey
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ name = cli.config[:name]
6
+ if name.nil?
7
+ puts "You must provide a name for the key."
8
+ else
9
+ response = Scli::Compute.new.create_key(name) #Note: This method can take a pub key too, not supported yet.
10
+ if response.status == 200
11
+ puts "Key Created successfully... Copy private key to a safe location."
12
+ puts "Key name: #{response.body['keyName']}"
13
+ puts "Key Contents:\n #{response.body['keyMaterial']}"
14
+ else
15
+ puts "Failed with #{response.status} error of: #{response.body.inspect}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ PLUGINS << ["ckey", "create-key", "Key Name (Req -n)", "scli ckey -n my_new_keypair"]
@@ -0,0 +1,31 @@
1
+ module Scli
2
+ def self.cvol
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ offering_id = cli.config[:offering_id]
6
+ location_id = cli.config[:location_id]
7
+ format = cli.config[:format]
8
+ size = cli.config[:size]
9
+ name = cli.config[:name]
10
+
11
+ if offering_id.nil?
12
+ puts "You did not provide an offering id, using a generic id of 20035200."
13
+ offering_id = 20035200
14
+ end
15
+
16
+ if format.nil? || name.nil? || size.nil? || location_id.nil? || offering_id.nil?
17
+ puts "Missing offering_id or location_id or format or size or name, please retry."
18
+ else
19
+ if volume_format_valid?(format) && volume_size_valid?(size) && volume_offering_valid?(offering_id)
20
+ response = Scli::Storage.new.create_volume(name, offering_id, format, location_id, size)
21
+ if response.status == 200
22
+ puts "Volume Created successfully: #{response.body.inspect}"
23
+ else
24
+ puts "Failed with #{response.status} error of: #{response.body.inspect}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ PLUGINS << ["cvol", "create-volume", "Name (Req -n), Offering ID (Req -o), Format (Req -f), Location ID (Req -l), Size (Req -s)", "scli cvol -n 'new_vol' -o 342 -f EXT3 -l 41 -s 1024"]
@@ -0,0 +1,15 @@
1
+ module Scli
2
+ def self.daddoff
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ addresses = Scli::Compute.new.list_address_offerings.body['addresses']
6
+ table_to_print = addresses.collect do |offering|
7
+ [offering['id'], offering['ipType'], offering['location'], offering['price']['rate'], offering['price']['countryCode'], offering['price']['currencyCode']]
8
+ end
9
+ table = Terminal::Table.new :title => "IP Address Offerings (#{addresses.count})".cyan, :headings => ["id".green, "IpType".green, "location".green, "price rate".green, "price country".green, "price currency".green], :rows => table_to_print
10
+ puts table
11
+ end
12
+ end
13
+
14
+ PLUGINS << ["daddoff", "describe-address-offerings", "", "scli daddoff, scli describe-address-offerings"]
15
+
@@ -0,0 +1,15 @@
1
+ module Scli
2
+ def self.dadd
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ address_id = cli.config[:address_id] || ARGV[1]
6
+ if address_id.nil? || is_address_id?(address_id)
7
+ addresses = (address_id.nil?) ? Scli::Compute.new.addresses : Scli::Compute.new.addresses.get(address_id)
8
+ print_object("Addresses", addresses, [:id, :location, :ip, :state, :instance_id, :hostname, :mode, :owner])
9
+ else
10
+ puts "Got an invalid address id, please retry."
11
+ end
12
+ end
13
+ end
14
+
15
+ PLUGINS << ["dadd", "describe-address, describe-addresses", "Address ID (Opt -a)", "scli dadd -a 345313, scli describe-address 345313"]
@@ -0,0 +1,20 @@
1
+ module Scli
2
+ def self.dim
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ image_id = cli.config[:image_id] || yield_regular_input(ARGV[1])
6
+ private_imgs = cli.config[:private]
7
+ if image_id.nil?
8
+ images = private_imgs ? Scli::Compute.new.images.reject{|img| img.visibility != "PRIVATE"} : Scli::Compute.new.images
9
+ print_object("Images", images, [:id, :name, :architecture, :platform, :visibility, :supported_instance_types, :description, :location, :owner, :created_at, :state])
10
+ else
11
+ if is_image_id?(image_id)
12
+ print_object("Image", Scli::Compute.new.images.get(image_id), [:id, :name, :architecture, :platform, :visibility, :supported_instance_types, :description, :location, :owner, :created_at, :state])
13
+ else
14
+ puts "Image id provided is invalid"
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ PLUGINS << ["dim", "describe-image, describe-images", "Image ID (Opt -m), Private Only (Opt -p)", "scli dim -m 34567, scli describe-image 34567"]
@@ -0,0 +1,35 @@
1
+ module Scli
2
+ def self.dint
3
+ instance_types = []
4
+
5
+ instance_types << "COP32.1/2048/60"
6
+ instance_types << "BRZ32.1/2048/60*175"
7
+ instance_types << "SLV32.2/4096/60*350"
8
+ instance_types << "GLD32.4/4096/60*350"
9
+
10
+ instance_types << "COP64.2/4096/60"
11
+ instance_types << "BRZ64.2/4096/60*500*350"
12
+ instance_types << "SLV64.4/8192/60*500*500"
13
+ instance_types << "GLD64.8/16384/60*500*500"
14
+ instance_types << "PLT64.16/16384/60*500*500*500*500"
15
+
16
+ table_to_print = []
17
+
18
+ instance_types.each do |inst_type|
19
+ name, ram, disks = inst_type.split("/")
20
+ color = name.slice!(0..2)
21
+ bits, cpus = name.split(".")
22
+ disk = disks.split("*")
23
+ total_ephemeral_size = disk.inject{|sum,x| sum.to_i + x.to_i }
24
+ total_ephemeral_non_root = (disk.count > 1) ? (disk.drop(1).inject{|sum,x| sum.to_i + x.to_i }) : 0
25
+ total_ephemeral_devices = (disk.count > 1) ? (disk.drop(1).count) : 0
26
+ table_to_print << [inst_type, color, bits, cpus, ram, total_ephemeral_size, total_ephemeral_non_root, total_ephemeral_devices]
27
+ end
28
+
29
+ table = Terminal::Table.new :title => "Instance Types".cyan, :headings => ["id".green, "color".green, "bits".green, "CPU(s)".green, "RAM".green, "Total Ephemeral".green, "Total Ephemeral (Non-Root)".green, "Ephemeral devices (Non-Root)".green], :rows => table_to_print
30
+ puts table
31
+ puts "Note: This table is not from an API, its simply a helper for the non-rememberable naming IBM gives their instances - List may update/change."
32
+ end
33
+ end
34
+
35
+ PLUGINS << ["dint", "describe-instance-types", "", "scli dint"]
@@ -0,0 +1,18 @@
1
+ module Scli
2
+ def self.din
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ instance_id = cli.config[:instance_id] || ARGV[1]
6
+ if instance_id.nil?
7
+ print_servers(Scli::Compute.new.servers)
8
+ else
9
+ if is_instance_id?(instance_id)
10
+ print_servers(Scli::Compute.new.servers.get(instance_id))
11
+ else
12
+ puts "Instance id provided is invalid"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ PLUGINS << ["din", "describe-instances, describe-instance", "Instance ID (Opt -i)", "scli din, scli din 123456"]
@@ -0,0 +1,11 @@
1
+ module Scli
2
+ def self.dkey
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ key_id = cli.config[:key_id] || ARGV[1]
6
+ keys = key_id.nil? ? Scli::Compute.new.keys : Scli::Compute.new.keys.get(key_id)
7
+ print_object("Keys", keys, [:name, :default, :public_key, :instance_ids, :modified_at])
8
+ end
9
+ end
10
+
11
+ PLUGINS << ["dkey", "describe-key, describe-keys", "Key ID (Opt -k)", "scli dkey -k 345678, scli describe-key 345678"]
@@ -0,0 +1,11 @@
1
+ module Scli
2
+ def self.dloc
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ location_id = cli.config[:location_id] || ARGV[1]
6
+ locations = location_id.nil? ? Scli::Compute.new.locations : Scli::Compute.new.locations.get(location_id)
7
+ print_object("Locations", locations, [:id, :name, :location, :capabilities, :description])
8
+ end
9
+ end
10
+
11
+ PLUGINS << ["dloc", "describe-location, describe-locations", "Location ID (Opt -l)", "scli dloc -l 345, scli describe-location 345"]
@@ -0,0 +1,18 @@
1
+ module Scli
2
+ def self.dvlan
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ vlan_id = cli.config[:vlan_id] || ARGV[1]
6
+ if vlan_id.nil?
7
+ print_object("VLANs", Scli::Compute.new.vlans, [:id, :name, :location])
8
+ else
9
+ if is_vlan_id?(vlan_id)
10
+ print_object("VLANs", Scli::Compute.new.vlans.get(vlan_id), [:id, :name, :location])
11
+ else
12
+ puts "Got an invalid VLAN id, please retry."
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ PLUGINS << ["dvlan", "describe-vlan, describe-vlans", "Vlan ID (Opt -V)", "scli dvlan -V 345, scli describe-vlan 345"]
@@ -0,0 +1,14 @@
1
+ module Scli
2
+ def self.dvoloff
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ offerings = Scli::Storage.new.list_offerings.body['volumes']
6
+ table_to_print = offerings.collect do |offering|
7
+ [offering['id'], offering['name'], format_location(offering['location']), offering['price']['rate'], offering['price']['currencyCode'], offering['price']['unitOfMeasure'], offering['formats'].collect{|format| format['id']}.join(","), offering['capacity']]
8
+ end
9
+ table = Terminal::Table.new :title => "Volume Offerings (#{offerings.count})".cyan, :headings => ["id".green, "Name".green, "location".green, "price rate".green, "price currency".green, "Price Measure".green, "Formats".green, "Capacity".green], :rows => table_to_print
10
+ puts table
11
+ end
12
+ end
13
+
14
+ PLUGINS << ["dvoloff", "describe-volume-offerings", "", "scli dvoloff"]
@@ -0,0 +1,18 @@
1
+ module Scli
2
+ def self.dvol
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ volume_id = cli.config[:volume_id] || ARGV[1]
6
+ if volume_id.nil?
7
+ print_volumes(Scli::Storage.new.volumes)
8
+ else
9
+ if is_volume_id?(volume_id)
10
+ print_volumes(Scli::Storage.new.volumes.get(volume_id))
11
+ else
12
+ puts "Volume id provided is invalid"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ PLUGINS << ["dvol", "describe-volume, describe-volumes", "Volume ID (Opt -v)", "scli dvol 23456"]
@@ -0,0 +1,29 @@
1
+ module Scli
2
+ def self.detvol
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ volume_id = cli.config[:volume_id]
6
+ instance_id = cli.config[:instance_id]
7
+ if volume_id.nil? || instance_id.nil?
8
+ puts "Instance and volume id's are required, please retry."
9
+ else
10
+ server = Scli::Compute.new.servers.get(instance_id)
11
+ volume = Scli::Storage.new.volumes.get(volume_id)
12
+ if server.nil?
13
+ puts "Could not find server: #{instance_id}"
14
+ elsif volume.nil?
15
+ puts "Could not find volume: #{volume_id}"
16
+ else
17
+ if server.detach(volume_id) # Note: detach() takes a string, not an object
18
+ print_volumes(volume)
19
+ puts "Is being detached from instance:".red
20
+ print_servers(server)
21
+ else
22
+ puts "Volume could not be detached for some reason..."
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ PLUGINS << ["detvol", "detach-volume", "Volume ID (Req -v), Instance ID (Req -i)", "scli detvol -i 123456 -v 23456"]
@@ -0,0 +1,20 @@
1
+ module Scli
2
+ def self.gco
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ instance_id = cli.config[:instance_id] || yield_regular_input(ARGV[1])
6
+ if instance_id.nil? || !is_instance_id?(instance_id)
7
+ puts "An invalid instance id was provided."
8
+ else
9
+ response = Scli::Compute.new.get_instance_logs(instance_id)
10
+ if response.status == 200
11
+ puts "Console log output:"
12
+ puts "#{response.body['logs'].join("\n")}"
13
+ else
14
+ puts "Failed with #{response.status} error of: #{response.body.inspect}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ PLUGINS << ["gco", "get-console-output", "Instance ID (Req -i)", "scli gco 123456"]
20
+
@@ -0,0 +1,23 @@
1
+ module Scli
2
+ def self.reboot
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ instance_id = cli.config[:instance_id] || ARGV[1]
6
+ if instance_id.nil? || !is_instance_id?(instance_id)
7
+ puts "Instance id provided is invalid"
8
+ else
9
+ server = Scli::Compute.new.servers.get(instance_id)
10
+ if server.nil?
11
+ puts "Server could not be found, check instance_id and state to ensure it can be rebooted..."
12
+ else
13
+ if server.reboot
14
+ puts "Reboot successful..."
15
+ else
16
+ puts "Reboot failed, check instance_id and state to ensure it can be rebooted..."
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ PLUGINS << ["reboot", "", "Instance ID (Req -i)", "scli reboot 123456"]
@@ -0,0 +1,19 @@
1
+ module Scli
2
+ def self.tad
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ address_id = cli.config[:address_id] || ARGV[1]
6
+ if address_id.nil? || !is_address_id?(address_id)
7
+ puts "Address id provided is invalid"
8
+ else
9
+ response = Scli::Compute.new.delete_address(address_id)
10
+ if response.status == 200
11
+ puts "Address successfully terminated: #{response.body.inspect}"
12
+ else
13
+ puts "Address terminated failed #{response.status} with #{response.body.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ PLUGINS << ["tad", "terminate-address", "Address ID (Req -a)", "scli tad 200001"]
@@ -0,0 +1,19 @@
1
+ module Scli
2
+ def self.tim
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ image_id = cli.config[:image_id] || ARGV[1]
6
+ if image_id.nil? || !is_image_id?(image_id)
7
+ puts "Image id provided is invalid"
8
+ else
9
+ response = Scli::Compute.new.delete_image(image_id)
10
+ if response.status == 200
11
+ puts "Image successfully terminated: #{response.body.inspect}"
12
+ else
13
+ puts "Image terminated failed #{response.status} with #{response.body.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ PLUGINS << ["tim", "terminate-image", "Image ID (Req -m)", "scli tim 200001"]
@@ -0,0 +1,24 @@
1
+ module Scli
2
+ def self.tin
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ instance_id = cli.config[:instance_id] || ARGV[1]
6
+ if instance_id.nil? || !is_instance_id?(instance_id)
7
+ puts "Instance id provided is invalid"
8
+ else
9
+ server = Scli::Compute.new.servers.get(instance_id)
10
+ if server.nil?
11
+ puts "Could not find server #{instance_id}, please check instance_id and state and retry."
12
+ else
13
+ if server.destroy
14
+ print_server(server)
15
+ puts "Is being destroyed...".red
16
+ else
17
+ puts "Could not destroy server #{instance_id}, please check instance_id and state and retry."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ PLUGINS << ["tin", "terminate-instance", "Instance ID (Req -i)", "scli tin 123456"]
@@ -0,0 +1,19 @@
1
+ module Scli
2
+ def self.tkey
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ name = cli.config[:name] || yield_regular_input(ARGV[1])
6
+ if name.nil?
7
+ puts "Key name not provided"
8
+ else
9
+ response = Scli::Compute.new.delete_key(name)
10
+ if response.status == 200
11
+ puts "Key successfully terminated: #{response.body.inspect}"
12
+ else
13
+ puts "Key termination failed #{response.status} with #{response.body.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ PLUGINS << ["tkey", "terminate-key", "Name (Req -n)", "scli tkey 200001"]
@@ -0,0 +1,24 @@
1
+ module Scli
2
+ def self.delvol
3
+ cli = MyCLI.new
4
+ cli.parse_options
5
+ volume_id = cli.config[:volume_id] || ARGV[1]
6
+ if volume_id.nil? || !is_volume_id?(volume_id)
7
+ puts "Volume ID is either missing or invalid, please retry."
8
+ else
9
+ volume = Scli::Storage.new.volumes.get(volume_id)
10
+ if volume.nil?
11
+ puts "Could not find volume: #{volume_id}"
12
+ else
13
+ if volume.destroy
14
+ print_volume(volume)
15
+ puts "Is being destroyed...".red
16
+ else
17
+ puts "Volume could not be destroyed for some reason..."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ PLUGINS << ["delvol", "delete-volume,terminate-volume", "Volume ID (Req -v)", "scli delvol -v 23456, scli delvol 23456"]
data/lib/scli.rb ADDED
@@ -0,0 +1,174 @@
1
+ require 'rubygems'
2
+ require 'mixlib/cli'
3
+ require 'fog'
4
+ require 'colored'
5
+ require 'terminal-table'
6
+ require 'validators'
7
+ require 'formatters'
8
+ require 'generic'
9
+
10
+ PLUGINS = []
11
+
12
+ class MyCLI
13
+ include Mixlib::CLI
14
+
15
+ option :config_file,
16
+ :short => "-C CONFIG",
17
+ :long => "--config CONFIG",
18
+ :default => '~/.scli/config.rb',
19
+ :description => "The configuration file to use"
20
+
21
+ option :address_id,
22
+ :short => "-a ADDRESS",
23
+ :long => "--address-id ADDRESS",
24
+ :description => "IP Address ID to use"
25
+
26
+ option :instance_id,
27
+ :short => "-i INSTANCE",
28
+ :long => "--instance-id INSTANCE",
29
+ :description => "Instance ID to use"
30
+
31
+ option :volume_id,
32
+ :short => "-v VOLUME",
33
+ :long => "--volume-id VOLUME",
34
+ :description => "Volume ID to use"
35
+
36
+ option :instance_type,
37
+ :short => "-t TYPE",
38
+ :long => "--instance-type TYPE",
39
+ :description => "Instance Type (e.g. COP32.1/2048/60)"
40
+
41
+ option :image_id,
42
+ :short => "-m IMAGE",
43
+ :long => "--image-id IMAGE",
44
+ :description => "Image ID to use"
45
+
46
+ option :key_id,
47
+ :short => "-k IMAGE",
48
+ :long => "--key-id KEY",
49
+ :description => "Key ID to use"
50
+
51
+ option :offering_id,
52
+ :short => "-o OFFERING",
53
+ :long => "--offering-id OFFERING",
54
+ :description => "Offering ID to use"
55
+
56
+ option :vlan_id,
57
+ :short => "-V VLAN",
58
+ :long => "--vlan-id VLAN",
59
+ :description => "Vlan ID to use"
60
+
61
+ option :location_id,
62
+ :short => "-l LOCATION",
63
+ :long => "--location-id LOCATION",
64
+ :description => "Location ID to use"
65
+
66
+ option :name,
67
+ :short => "-n NAME",
68
+ :long => "--name NAME",
69
+ :description => "Name to use"
70
+
71
+ option :size,
72
+ :short => "-s SIZE",
73
+ :long => "--size SIZE",
74
+ :description => "Size to use"
75
+
76
+ option :format,
77
+ :short => "-f FORMAT",
78
+ :long => "--format FORMAT",
79
+ :description => "Format to use (ext3, raw)"
80
+
81
+ option :private,
82
+ :short => "-p",
83
+ :long => "--private",
84
+ :boolean => true,
85
+ :description => "Only show private images, etc"
86
+
87
+ option :mini_ephemeral,
88
+ :long => "--mini",
89
+ :boolean => true,
90
+ :description => "(Create instance only) Set mini ephemeral)"
91
+
92
+ option :configuration_data,
93
+ :long => "--config-data DATA",
94
+ :description => "(Create instance only) Extra config data required by image"
95
+
96
+ option :anti_colo,
97
+ :long => "--anti-colo INST_ID",
98
+ :description => "(Create instance only) Instance ID to anti-colo against"
99
+
100
+ option :secondary_address_id,
101
+ :short => "-A SADDRESS",
102
+ :long => "--secondary-address-id SADDRESS",
103
+ :description => "(Create instance only) IP To use for eth1"
104
+
105
+ option :help,
106
+ :short => "-h",
107
+ :long => "--help",
108
+ :description => "SCLI Help",
109
+ :on => :tail,
110
+ :boolean => true,
111
+ :show_options => true,
112
+ :exit => 0
113
+
114
+ end
115
+
116
+ module Scli
117
+ def self.options
118
+ cli = MyCLI.new
119
+ cli.parse_options
120
+ cli.config.merge!(read_config(File.expand_path(cli.config[:config_file])) || {})
121
+ end
122
+ end
123
+
124
+ module Scli
125
+ class Main
126
+ def self.run
127
+ cli = MyCLI.new
128
+ cli.parse_options
129
+
130
+ Scli.generate_config_file(cli.config[:config_file]) unless Scli.config_file_exists?(cli.config[:config_file]) || Scli.env_populated?
131
+
132
+ Dir.glob("#{File.dirname(__FILE__)}/plugins/*").each do |plugin|
133
+ require plugin
134
+ end
135
+
136
+ help_table = Terminal::Table.new :title => "Help", :headings => ["Command", "Aliases", "Arguments", "Examples"], :rows => PLUGINS
137
+
138
+ #
139
+ # every array in plugins contains the main command [0], comma seperated command aliases [1], options [2] and examples [3]. So we match on [0] or [1] below.
140
+ #
141
+ begin
142
+ plugin_executed = false
143
+ PLUGINS.each do |plugin|
144
+ commands = [plugin[0]] + plugin[1].split(",")
145
+ if commands.include?(ARGV[0])
146
+ Scli.send(plugin[0])
147
+ plugin_executed = true
148
+ end
149
+ end
150
+
151
+ unless plugin_executed || !ARGV[0] == "help"
152
+ puts help_table
153
+ puts "\n Run scli -h to get CLI Args"
154
+ end
155
+
156
+ rescue Excon::Errors::InternalServerError => e
157
+ puts "Got an internal server error while trying to talk to IBM: #{e.response.body}"
158
+ rescue Excon::Errors::PreconditionFailed => e
159
+ puts "A precondition failed while trying to do our API Request: #{e.response.body}"
160
+ rescue Excon::Errors::SocketError => e
161
+ puts "Could not connect to IBM: #{e}"
162
+ rescue Excon::Errors::Unauthorized => e
163
+ puts "You were not authorized to access a resource, Are you sure its owned by the account in your config file? -- #{e.response.body}"
164
+ rescue Exception => e
165
+ if e.methods.include?(:response)
166
+ puts "Fog API Took an exception while speaking to IBM: #{e.response.body}"
167
+ else
168
+ puts "Took an exception, You probably put an invalid instance, volume or address id in as a command line argument, Check to make sure it really exists and retry."
169
+ puts "#{e.backtrace.join("\n")} -- #{e.message}"
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
data/lib/validators.rb ADDED
@@ -0,0 +1,56 @@
1
+ module Scli
2
+ def self.yield_regular_input(argument)
3
+ return nil if argument.nil?
4
+ (argument[0] == "-") ? nil : argument
5
+ end
6
+
7
+ def self.is_address_id?(address_id)
8
+ address_id.to_i.to_s.size >= 6
9
+ end
10
+
11
+ def self.is_volume_id?(volume_id)
12
+ volume_id.to_i.to_s.size >= 5
13
+ end
14
+
15
+ def self.is_vlan_id?(vlan_id)
16
+ vlan_id.to_i.to_s.size == 3
17
+ end
18
+
19
+ def self.is_image_id?(image_id)
20
+ image_id.to_i.to_s.size >= 8
21
+ end
22
+
23
+ def self.is_instance_id?(instance_id)
24
+ instance_id.to_i.to_s.size >= 6
25
+ end
26
+
27
+ def self.volume_format_valid?(format)
28
+ valid_formats = ["EXT3", "RAW"]
29
+ if valid_formats.include?(format)
30
+ true
31
+ else
32
+ puts "The format you provided is invalid, it must be one of #{valid_formats.join(",")}"
33
+ false
34
+ end
35
+ end
36
+
37
+ def self.volume_size_valid?(size)
38
+ valid_sizes = [60, 256, 512, 1024, 2048, 4112, 8224, 10240]
39
+ if valid_sizes.include?(size.to_i)
40
+ true
41
+ else
42
+ puts "The size you provided is invalid, it must be one of #{valid_sizes.join(",")}"
43
+ false
44
+ end
45
+ end
46
+
47
+ def self.volume_offering_valid?(offering_id)
48
+ unless offering_id.to_s == "20035200"
49
+ puts "=" * 60
50
+ puts "WARN: IBM has many offering ID's for volumes, however most do not support large blocks and they seem to be migrating away from using them."
51
+ puts "WARN: Every volume created in the WebUI uses a offering id of 20035200, so you probably want to use that."
52
+ puts "=" * 60
53
+ end
54
+ true
55
+ end
56
+ end
data/scli.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'scli'
3
+ s.version = '0.0.3'
4
+ s.date = '2012-06-30'
5
+ s.platform = Gem::Platform::RUBY
6
+ s.authors = ['Josh Pasqualetto']
7
+ s.email = ['josh.pasqualetto@sonian.net']
8
+ s.homepage = 'https://github.com/sonian/scli'
9
+ s.summary = 'CLI Interface to IBM SmartCloud API'
10
+ s.description = 'A command line tool to interface with IBM SmartCloud: create, view & delete instances, keys, volumes, addresses, etc'
11
+ s.license = 'MIT'
12
+ s.has_rdoc = false
13
+
14
+ s.add_dependency('fog', '~> 1.3.1')
15
+ s.add_dependency('mixlib-cli', '~> 1.2.2')
16
+ s.add_dependency('terminal-table', '~> 1.4.5')
17
+ s.add_dependency('colored', '1.2')
18
+
19
+ s.files = Dir.glob('{bin,lib}/**/*') + %w[scli.gemspec README.org MIT-LICENSE.txt]
20
+ s.executables = Dir.glob('bin/**/*').map { |file| File.basename(file) }
21
+ s.require_paths = ['lib']
22
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh Pasqualetto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fog
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.3.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: mixlib-cli
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: terminal-table
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.4.5
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.5
62
+ - !ruby/object:Gem::Dependency
63
+ name: colored
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: '1.2'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: '1.2'
78
+ description: ! 'A command line tool to interface with IBM SmartCloud: create, view
79
+ & delete instances, keys, volumes, addresses, etc'
80
+ email:
81
+ - josh.pasqualetto@sonian.net
82
+ executables:
83
+ - scli
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - bin/scli
88
+ - lib/formatters.rb
89
+ - lib/generic.rb
90
+ - lib/plugins/attach-volume.rb
91
+ - lib/plugins/create-address.rb
92
+ - lib/plugins/create-instance.rb
93
+ - lib/plugins/create-key.rb
94
+ - lib/plugins/create-volume.rb
95
+ - lib/plugins/describe-address-offering.rb
96
+ - lib/plugins/describe-address.rb
97
+ - lib/plugins/describe-image.rb
98
+ - lib/plugins/describe-instance-types.rb
99
+ - lib/plugins/describe-instance.rb
100
+ - lib/plugins/describe-key.rb
101
+ - lib/plugins/describe-location.rb
102
+ - lib/plugins/describe-vlan.rb
103
+ - lib/plugins/describe-volume-offering.rb
104
+ - lib/plugins/describe-volume.rb
105
+ - lib/plugins/detach-volume.rb
106
+ - lib/plugins/get-console-output.rb
107
+ - lib/plugins/reboot.rb
108
+ - lib/plugins/terminate-address.rb
109
+ - lib/plugins/terminate-image.rb
110
+ - lib/plugins/terminate-instance.rb
111
+ - lib/plugins/terminate-key.rb
112
+ - lib/plugins/terminate-volume.rb
113
+ - lib/scli.rb
114
+ - lib/validators.rb
115
+ - scli.gemspec
116
+ - README.org
117
+ - MIT-LICENSE.txt
118
+ homepage: https://github.com/sonian/scli
119
+ licenses:
120
+ - MIT
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.8.24
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: CLI Interface to IBM SmartCloud API
143
+ test_files: []