collins_shell 0.2.14
Sign up to get free protection for your applications and to get access to all the features.
- data/.pryrc +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +59 -0
- data/README.md +335 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/bin/collins-shell +36 -0
- data/collins_shell.gemspec +95 -0
- data/lib/collins_shell.rb +3 -0
- data/lib/collins_shell/asset.rb +198 -0
- data/lib/collins_shell/cli.rb +185 -0
- data/lib/collins_shell/console.rb +129 -0
- data/lib/collins_shell/console/asset.rb +127 -0
- data/lib/collins_shell/console/cache.rb +17 -0
- data/lib/collins_shell/console/command_helpers.rb +131 -0
- data/lib/collins_shell/console/commands.rb +28 -0
- data/lib/collins_shell/console/commands/cat.rb +123 -0
- data/lib/collins_shell/console/commands/cd.rb +61 -0
- data/lib/collins_shell/console/commands/io.rb +26 -0
- data/lib/collins_shell/console/commands/iterators.rb +190 -0
- data/lib/collins_shell/console/commands/tail.rb +178 -0
- data/lib/collins_shell/console/commands/versions.rb +42 -0
- data/lib/collins_shell/console/filesystem.rb +121 -0
- data/lib/collins_shell/console/options_helpers.rb +8 -0
- data/lib/collins_shell/errors.rb +7 -0
- data/lib/collins_shell/ip_address.rb +144 -0
- data/lib/collins_shell/ipmi.rb +67 -0
- data/lib/collins_shell/monkeypatch.rb +60 -0
- data/lib/collins_shell/provision.rb +152 -0
- data/lib/collins_shell/state.rb +98 -0
- data/lib/collins_shell/tag.rb +41 -0
- data/lib/collins_shell/thor.rb +209 -0
- data/lib/collins_shell/util.rb +120 -0
- data/lib/collins_shell/util/asset_printer.rb +265 -0
- data/lib/collins_shell/util/asset_stache.rb +32 -0
- data/lib/collins_shell/util/log_printer.rb +187 -0
- data/lib/collins_shell/util/printer_util.rb +28 -0
- metadata +200 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
module CollinsShell; module Console; module Commands
|
4
|
+
Versions = Pry::CommandSet.new do
|
5
|
+
create_command "latest", "Latest version of collins shell" do
|
6
|
+
group "Software"
|
7
|
+
|
8
|
+
def options(opt)
|
9
|
+
opt.banner <<-BANNER
|
10
|
+
Usage: latest
|
11
|
+
|
12
|
+
Display the latest version of collins shell
|
13
|
+
BANNER
|
14
|
+
end
|
15
|
+
|
16
|
+
def process
|
17
|
+
o = CollinsShell::Console.options
|
18
|
+
render_output CollinsShell::Cli.new([], o).get_latest_version
|
19
|
+
end
|
20
|
+
|
21
|
+
end # create_command
|
22
|
+
|
23
|
+
create_command "version", "Current version of collins shell" do
|
24
|
+
group "Software"
|
25
|
+
|
26
|
+
def options(opt)
|
27
|
+
opt.banner <<-BANNER
|
28
|
+
Usage: version
|
29
|
+
|
30
|
+
Display the current version of collins shell
|
31
|
+
BANNER
|
32
|
+
end
|
33
|
+
|
34
|
+
def process
|
35
|
+
o = CollinsShell::Console.options
|
36
|
+
render_output CollinsShell::Cli.new([], o).get_version
|
37
|
+
end
|
38
|
+
|
39
|
+
end # create_command
|
40
|
+
|
41
|
+
end # CommandSet
|
42
|
+
end; end; end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'collins_shell/console/asset'
|
2
|
+
|
3
|
+
module CollinsShell; module Console
|
4
|
+
class Filesystem
|
5
|
+
include CollinsShell::Console::CommandHelpers
|
6
|
+
|
7
|
+
attr_accessor :console_asset, :stack # Using console_asset, don't want to steal asset
|
8
|
+
|
9
|
+
def initialize optns = {}
|
10
|
+
options = Collins::Util.symbolize_hash(optns)
|
11
|
+
@console_asset = options.fetch(:console_asset, nil)
|
12
|
+
@options = options
|
13
|
+
@stack = options.fetch(:stack, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
def root?
|
17
|
+
@stack.size == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def asset?
|
21
|
+
!console_asset.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def path
|
25
|
+
'/' + @stack.join('/')
|
26
|
+
end
|
27
|
+
|
28
|
+
def pop
|
29
|
+
stk = @stack.dup
|
30
|
+
stk.pop
|
31
|
+
stk = [] if stk.nil?
|
32
|
+
asset = resolve_location(stk) if stk.size > 0
|
33
|
+
opts = @options.merge(:stack => stk, :console_asset => asset)
|
34
|
+
CollinsShell::Console::Filesystem.new(opts)
|
35
|
+
end
|
36
|
+
|
37
|
+
def push ctxt
|
38
|
+
stk = @stack.dup
|
39
|
+
stk << ctxt
|
40
|
+
asset = resolve_location stk
|
41
|
+
if asset? and asset then
|
42
|
+
raise StandardError.new("Can not nest assets in assets")
|
43
|
+
end
|
44
|
+
opts = @options.merge(:stack => stk, :console_asset => asset)
|
45
|
+
CollinsShell::Console::Filesystem.new(opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def available_commands
|
49
|
+
includes = ['clear_cache'] + pry_commands
|
50
|
+
if asset? then
|
51
|
+
excludes = ['respond_to?','to_s','cput'].map{|s|s.to_sym}
|
52
|
+
cmds1 = CollinsShell::Console::Asset.public_instance_methods(false).reject{|i| excludes.include?(i.to_sym)}
|
53
|
+
cmds2 = Collins::Asset.public_instance_methods(false).reject{|i| excludes.include?(i.to_sym)}.map{|s| "asset.#{s}"}
|
54
|
+
cmds1 + cmds2 + includes
|
55
|
+
else
|
56
|
+
includes
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"#{self.class}(path = '#{path}', asset? = '#{asset?}')"
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
def method_missing meth, *args, &block
|
66
|
+
if asset? then
|
67
|
+
a = @console_asset
|
68
|
+
if a.respond_to?(meth) then
|
69
|
+
a.send(meth, *args, &block)
|
70
|
+
else
|
71
|
+
Pry.output.puts("Command not found: #{meth}")
|
72
|
+
cmds = available_commands.join(', ')
|
73
|
+
Pry.output.puts("Available commands: #{cmds}")
|
74
|
+
end
|
75
|
+
elsif pry_commands.include?(meth) then
|
76
|
+
arg = args.unshift(meth).join(' ')
|
77
|
+
runner = CollinsShell::Console
|
78
|
+
runner.run_pry_command(arg, :context => self, :binding_stack => [Pry.binding_for(self)])
|
79
|
+
else
|
80
|
+
Pry.output.puts("command not found: #{meth}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def pry_commands
|
85
|
+
@pry_commands ||= Pry.commands.list_commands.select{|c| c.respond_to?(:to_sym)}.map{|c| c.to_sym}
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Collins::Asset,NilClass] asset if context is one, nil otherwise
|
89
|
+
# @raise [ExpectationFailedException] if invalid nesting is attempted
|
90
|
+
def resolve_location stack
|
91
|
+
context = stack.last
|
92
|
+
path = "/#{stack.join('/')}"
|
93
|
+
|
94
|
+
if get_all_tags.include?(context) then
|
95
|
+
Pry.output.puts("Found tag: '#{context}'")
|
96
|
+
nil
|
97
|
+
elsif asset_exists?(context) then
|
98
|
+
Pry.output.puts("Found asset: '#{context}'")
|
99
|
+
CollinsShell::Console::Asset.new(context)
|
100
|
+
elsif stack.size % 2 == 0 then # should be key/value, we know it's not root
|
101
|
+
begin
|
102
|
+
assets = find_assets(stack, false)
|
103
|
+
if assets.size == 0 then
|
104
|
+
raise StandardError.new("No assets in path #{path}")
|
105
|
+
elsif assets.size == 1 then
|
106
|
+
Pry.output.puts("Found asset #{assets[0]} in #{path}")
|
107
|
+
CollinsShell::Console::Asset.new(assets[0])
|
108
|
+
else
|
109
|
+
Pry.output.puts("#{assets.size} assets in path #{path}")
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
rescue Exception => e
|
113
|
+
raise StandardError.new("Invalid path #{path} - #{e}")
|
114
|
+
end
|
115
|
+
else
|
116
|
+
raise StandardError.new("Path #{path} is invalid")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end; end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'collins_shell/thor'
|
2
|
+
require 'collins_shell/util'
|
3
|
+
require 'thor'
|
4
|
+
require 'thor/group'
|
5
|
+
|
6
|
+
module CollinsShell
|
7
|
+
|
8
|
+
class IpAddress < Thor
|
9
|
+
|
10
|
+
include ThorHelper
|
11
|
+
include CollinsShell::Util
|
12
|
+
namespace :ip_address
|
13
|
+
def self.banner task, namespace = true, subcommand = false
|
14
|
+
"#{basename} #{task.formatted_usage(self, true, subcommand).gsub(':',' ')}"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'allocate POOL', 'allocate addresses for an asset in the specified pool'
|
18
|
+
use_collins_options
|
19
|
+
use_tag_option(true)
|
20
|
+
method_option :count, :type => :numeric, :default => 1, :desc => 'Number of addresses to allocate'
|
21
|
+
def allocate pool
|
22
|
+
call_collins get_collins_client, "ip_address allocate" do |client|
|
23
|
+
addresses = client.ipaddress_allocate! options.tag, pool, options["count"]
|
24
|
+
header = [["Gateway","Netmask","Address","Pool"]]
|
25
|
+
tags = header + addresses.map do |address|
|
26
|
+
[address.gateway, address.netmask, address.address, address.pool]
|
27
|
+
end
|
28
|
+
print_table tags
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'delete POOL', 'delete addresses for an asset in the specified pool'
|
33
|
+
use_collins_options
|
34
|
+
use_tag_option(true)
|
35
|
+
def delete pool
|
36
|
+
call_collins get_collins_client, "ip_address delete" do |client|
|
37
|
+
delete_count = client.ipaddress_delete! options.tag, pool
|
38
|
+
say_success "Deleted #{delete_count} addresses"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'delete_all', 'delete all addresses for an asset'
|
43
|
+
use_collins_options
|
44
|
+
use_tag_option(true)
|
45
|
+
def delete_all
|
46
|
+
call_collins get_collins_client, "ip_address delete_all" do |client|
|
47
|
+
delete_count = client.ipaddress_delete! options.tag
|
48
|
+
say_success "Deleted #{delete_count} addresses"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'find ADDRESS', 'find asset using specified IP address'
|
53
|
+
use_collins_options
|
54
|
+
def find address
|
55
|
+
call_collins get_collins_client, "ip_address find" do |client|
|
56
|
+
asset = client.asset_at_address address
|
57
|
+
if asset then
|
58
|
+
say_success "Asset #{asset.tag} is using address #{address}"
|
59
|
+
else
|
60
|
+
say_error "No asset using address #{address}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'assets POOL', 'find all assets in a pool'
|
66
|
+
use_collins_options
|
67
|
+
method_option :details, :type => :boolean, :default => false, :desc => 'Retrieve details for each asset. SLOW'
|
68
|
+
def assets pool
|
69
|
+
call_collins get_collins_client, "ip_address assets" do |client|
|
70
|
+
header = true
|
71
|
+
client.assets_in_pool(pool).each do |asset|
|
72
|
+
if options.details then
|
73
|
+
asset = client.get asset
|
74
|
+
print_find_results asset, [:tag,:status,:type,:hostname,:addresses], :header => header
|
75
|
+
header = false
|
76
|
+
else
|
77
|
+
puts(asset.tag)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
desc 'pools', 'find all pools that are in use'
|
84
|
+
use_collins_options
|
85
|
+
method_option :used, :type => :boolean, :default => false, :desc => 'Only pools that are in use'
|
86
|
+
def pools
|
87
|
+
call_collins get_collins_client, "ip_address pools" do |client|
|
88
|
+
# NAME, NETWORK, START_ADDRESS, SPECIFIED_GATEWAY, GATEWAY, BROADCAST, POSSIBLE_ADDRESSES
|
89
|
+
header = [["Pool","Network","Gateway","Broadcast","Possible Addresses","Start Address","Specified Gateway"]]
|
90
|
+
rows = client.ipaddress_pools(!options.used).map do |pool|
|
91
|
+
[pool["NAME"], pool["NETWORK"], pool["GATEWAY"], pool["BROADCAST"], pool["POSSIBLE_ADDRESSES"],
|
92
|
+
pool["START_ADDRESS"], pool["SPECIFIED_GATEWAY"]]
|
93
|
+
end
|
94
|
+
print_table header + rows
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
desc 'create', 'create a new IP address (Use allocate, not create)'
|
99
|
+
use_collins_options
|
100
|
+
use_tag_option(true)
|
101
|
+
method_option :address, :required => true, :type => :string, :desc => 'IP address'
|
102
|
+
method_option :gateway, :required => true, :type => :string, :desc => 'IP gateway'
|
103
|
+
method_option :netmask, :required => true, :type => :string, :desc => 'IP netmask'
|
104
|
+
method_option :pool, :type => :string, :desc => 'Name of pool'
|
105
|
+
def create
|
106
|
+
call_collins get_collins_client, "create address" do |client|
|
107
|
+
address = client.ipaddress_update! options.tag, nil,
|
108
|
+
:address => options.address,
|
109
|
+
:gateway => options.gateway,
|
110
|
+
:netmask => options.netmask,
|
111
|
+
:pool => options.pool
|
112
|
+
if address then
|
113
|
+
say_success "Address for #{options.tag} created"
|
114
|
+
else
|
115
|
+
say_error "Address for #{options.tag} not created"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
desc 'update OLD_ADDRESS', 'update the IP info for an asset'
|
121
|
+
use_collins_options
|
122
|
+
use_tag_option(true)
|
123
|
+
method_option :address, :type => :string, :desc => 'New IP address'
|
124
|
+
method_option :gateway, :type => :string, :desc => 'New IP gateway'
|
125
|
+
method_option :netmask, :type => :string, :desc => 'New IP netmask'
|
126
|
+
method_option :pool, :type => :string, :desc => 'New pool'
|
127
|
+
def update old_address
|
128
|
+
call_collins get_collins_client, "update address" do |client|
|
129
|
+
address = client.ipaddress_update! options.tag, old_address,
|
130
|
+
:address => options.address,
|
131
|
+
:gateway => options.gateway,
|
132
|
+
:netmask => options.netmask,
|
133
|
+
:pool => options.pool
|
134
|
+
if address then
|
135
|
+
say_success "Address for #{options.tag} updated"
|
136
|
+
else
|
137
|
+
say_error "Address for #{options.tag} not updated"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'collins_shell/thor'
|
2
|
+
require 'collins_shell/util'
|
3
|
+
require 'thor'
|
4
|
+
require 'thor/group'
|
5
|
+
|
6
|
+
module CollinsShell
|
7
|
+
|
8
|
+
class Ipmi < Thor
|
9
|
+
include ThorHelper
|
10
|
+
include CollinsShell::Util
|
11
|
+
namespace :ipmi
|
12
|
+
def self.banner task, namespace = true, subcommand = false
|
13
|
+
"#{basename} #{task.formatted_usage(self, true, subcommand).gsub(':',' ')}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.ipmi_options required = false
|
17
|
+
method_option :ipmi_username, :type => :string, :required => required, :desc => 'IPMI username'
|
18
|
+
method_option :ipmi_password, :type => :string, :required => required, :desc => 'IPMI password'
|
19
|
+
method_option :address, :type => :string, :required => required, :desc => 'IPMI address'
|
20
|
+
method_option :gateway, :type => :string, :required => required, :desc => 'IPMI gateway'
|
21
|
+
method_option :netmask, :type => :string, :required => required, :desc => 'IPMI netmask'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.print_ipmi ipmi
|
25
|
+
puts("address,gateway,netmask,username,password")
|
26
|
+
puts([ipmi.address,ipmi.gateway,ipmi.netmask,ipmi.username,ipmi.password].join(','))
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'create', 'create a new IPMI address for the specified asset'
|
30
|
+
use_collins_options
|
31
|
+
use_tag_option(true)
|
32
|
+
ipmi_options(true)
|
33
|
+
def create
|
34
|
+
call_collins get_collins_client, "create ipmi" do |client|
|
35
|
+
ipmi = client.ipmi_create options.tag, options.ipmi_username, options.ipmi_password, options.address, options.gateway, options.netmask
|
36
|
+
if ipmi then
|
37
|
+
asset = client.get options.tag
|
38
|
+
CollinsShell::Ipmi.print_ipmi asset.ipmi
|
39
|
+
else
|
40
|
+
say_error "create IPMI address"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'update', 'update IPMI settings for an asset'
|
46
|
+
use_collins_options
|
47
|
+
use_tag_option(true)
|
48
|
+
ipmi_options(false)
|
49
|
+
def update
|
50
|
+
call_collins get_collins_client, "update ipmi" do |client|
|
51
|
+
ipmi = client.ipmi_update options.tag, :username => options.ipmi_username,
|
52
|
+
:password => options.ipmi_password,
|
53
|
+
:address => options.address,
|
54
|
+
:gateway => options.gateway,
|
55
|
+
:netmask => options.netmask
|
56
|
+
if ipmi then
|
57
|
+
asset = client.get options.tag
|
58
|
+
CollinsShell::Ipmi.print_ipmi asset.ipmi
|
59
|
+
else
|
60
|
+
say_error "update IPMI address"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Numeric
|
2
|
+
SIZE_ARRAY = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
|
3
|
+
end
|
4
|
+
|
5
|
+
class Float
|
6
|
+
def to_human_size
|
7
|
+
return "0 Bytes" if (self == 0)
|
8
|
+
i = (Math.log(self) / Math.log(1024)).floor.to_i
|
9
|
+
small = self / (1024 ** i)
|
10
|
+
if i == 0 then
|
11
|
+
"#{self} #{SIZE_ARRAY[i]}"
|
12
|
+
else
|
13
|
+
sprintf("%.14f %s", small, SIZE_ARRAY[i])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Fixnum
|
19
|
+
|
20
|
+
def to_human_size
|
21
|
+
return "0 Bytes" if (self == 0)
|
22
|
+
i = (Math.log(self) / Math.log(1024)).floor.to_i
|
23
|
+
small = self / (1024 ** i)
|
24
|
+
if i == 0 then
|
25
|
+
"#{self} #{SIZE_ARRAY[i]}"
|
26
|
+
else
|
27
|
+
sprintf("%.2f %s", small, SIZE_ARRAY[i])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class String
|
34
|
+
|
35
|
+
def is_disk_size?
|
36
|
+
s = self.downcase
|
37
|
+
s.include?("gb") or s.include?("mb") or s.include?("tb")
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_bytes
|
41
|
+
s = self.downcase
|
42
|
+
size_h = ""
|
43
|
+
multiplier = 0
|
44
|
+
if s.include?("mb") then
|
45
|
+
multiplier = (1024 ** 2)
|
46
|
+
size_h = s.split('mb')[0]
|
47
|
+
elsif s.include?("gb") then
|
48
|
+
multiplier = (1024 ** 3)
|
49
|
+
size_h = s.split('gb')[0]
|
50
|
+
elsif s.include?("tb") then
|
51
|
+
multiplier = (1024 ** 4)
|
52
|
+
size_h = s.split('tb')[0]
|
53
|
+
else
|
54
|
+
raise Exception.new("Unknown size: #{s}")
|
55
|
+
end
|
56
|
+
(multiplier * size_h.to_f).floor.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
end
|