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 +20 -0
- data/README.org +24 -0
- data/bin/scli +9 -0
- data/lib/formatters.rb +146 -0
- data/lib/generic.rb +99 -0
- data/lib/plugins/attach-volume.rb +29 -0
- data/lib/plugins/create-address.rb +22 -0
- data/lib/plugins/create-instance.rb +35 -0
- data/lib/plugins/create-key.rb +21 -0
- data/lib/plugins/create-volume.rb +31 -0
- data/lib/plugins/describe-address-offering.rb +15 -0
- data/lib/plugins/describe-address.rb +15 -0
- data/lib/plugins/describe-image.rb +20 -0
- data/lib/plugins/describe-instance-types.rb +35 -0
- data/lib/plugins/describe-instance.rb +18 -0
- data/lib/plugins/describe-key.rb +11 -0
- data/lib/plugins/describe-location.rb +11 -0
- data/lib/plugins/describe-vlan.rb +18 -0
- data/lib/plugins/describe-volume-offering.rb +14 -0
- data/lib/plugins/describe-volume.rb +18 -0
- data/lib/plugins/detach-volume.rb +29 -0
- data/lib/plugins/get-console-output.rb +20 -0
- data/lib/plugins/reboot.rb +23 -0
- data/lib/plugins/terminate-address.rb +19 -0
- data/lib/plugins/terminate-image.rb +19 -0
- data/lib/plugins/terminate-instance.rb +24 -0
- data/lib/plugins/terminate-key.rb +19 -0
- data/lib/plugins/terminate-volume.rb +24 -0
- data/lib/scli.rb +174 -0
- data/lib/validators.rb +56 -0
- data/scli.gemspec +22 -0
- metadata +143 -0
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
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: []
|