morpheus-cli 2.10.3 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/morpheus +5 -96
- data/lib/morpheus/api/api_client.rb +23 -1
- data/lib/morpheus/api/checks_interface.rb +106 -0
- data/lib/morpheus/api/incidents_interface.rb +102 -0
- data/lib/morpheus/api/monitoring_apps_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_contacts_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_groups_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_interface.rb +36 -0
- data/lib/morpheus/api/option_type_lists_interface.rb +47 -0
- data/lib/morpheus/api/option_types_interface.rb +47 -0
- data/lib/morpheus/api/roles_interface.rb +0 -1
- data/lib/morpheus/api/setup_interface.rb +19 -9
- data/lib/morpheus/cli.rb +20 -1
- data/lib/morpheus/cli/accounts.rb +8 -3
- data/lib/morpheus/cli/app_templates.rb +19 -11
- data/lib/morpheus/cli/apps.rb +52 -37
- data/lib/morpheus/cli/cli_command.rb +229 -53
- data/lib/morpheus/cli/cli_registry.rb +48 -40
- data/lib/morpheus/cli/clouds.rb +55 -26
- data/lib/morpheus/cli/command_error.rb +12 -0
- data/lib/morpheus/cli/credentials.rb +68 -26
- data/lib/morpheus/cli/curl_command.rb +98 -0
- data/lib/morpheus/cli/dashboard_command.rb +2 -7
- data/lib/morpheus/cli/deployments.rb +4 -4
- data/lib/morpheus/cli/deploys.rb +1 -2
- data/lib/morpheus/cli/dot_file.rb +5 -8
- data/lib/morpheus/cli/error_handler.rb +179 -15
- data/lib/morpheus/cli/groups.rb +21 -13
- data/lib/morpheus/cli/hosts.rb +220 -110
- data/lib/morpheus/cli/instance_types.rb +2 -2
- data/lib/morpheus/cli/instances.rb +257 -167
- data/lib/morpheus/cli/key_pairs.rb +15 -9
- data/lib/morpheus/cli/library.rb +673 -27
- data/lib/morpheus/cli/license.rb +2 -2
- data/lib/morpheus/cli/load_balancers.rb +4 -4
- data/lib/morpheus/cli/log_level_command.rb +6 -4
- data/lib/morpheus/cli/login.rb +17 -3
- data/lib/morpheus/cli/logout.rb +25 -11
- data/lib/morpheus/cli/man_command.rb +388 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +434 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +620 -112
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
- data/lib/morpheus/cli/monitoring_apps_command.rb +29 -0
- data/lib/morpheus/cli/monitoring_checks_command.rb +427 -0
- data/lib/morpheus/cli/monitoring_contacts_command.rb +373 -0
- data/lib/morpheus/cli/monitoring_groups_command.rb +29 -0
- data/lib/morpheus/cli/monitoring_incidents_command.rb +711 -0
- data/lib/morpheus/cli/option_types.rb +10 -1
- data/lib/morpheus/cli/recent_activity_command.rb +2 -5
- data/lib/morpheus/cli/remote.rb +874 -134
- data/lib/morpheus/cli/roles.rb +54 -27
- data/lib/morpheus/cli/security_group_rules.rb +2 -2
- data/lib/morpheus/cli/security_groups.rb +23 -19
- data/lib/morpheus/cli/set_prompt_command.rb +50 -0
- data/lib/morpheus/cli/shell.rb +222 -157
- data/lib/morpheus/cli/tasks.rb +19 -15
- data/lib/morpheus/cli/users.rb +27 -17
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +28 -13
- data/lib/morpheus/cli/whoami.rb +131 -52
- data/lib/morpheus/cli/workflows.rb +24 -9
- data/lib/morpheus/formatters.rb +195 -3
- data/lib/morpheus/logging.rb +86 -0
- data/lib/morpheus/terminal.rb +371 -0
- data/scripts/generate_morpheus_commands_help.morpheus +60 -0
- metadata +21 -2
data/lib/morpheus/cli/deploys.rb
CHANGED
@@ -57,8 +57,7 @@ class Morpheus::Cli::Deploys
|
|
57
57
|
end
|
58
58
|
instance = instance_results['instances'][0]
|
59
59
|
instance_id = instance['id']
|
60
|
-
|
61
|
-
|
60
|
+
print_h1 "Morpheus Deployment"
|
62
61
|
if !deploy_args['script'].nil?
|
63
62
|
print cyan, bold, " - Executing Pre Deploy Script...", reset, "\n"
|
64
63
|
|
@@ -7,9 +7,6 @@ require 'term/ansicolor'
|
|
7
7
|
class Morpheus::Cli::DotFile
|
8
8
|
include Term::ANSIColor
|
9
9
|
|
10
|
-
DEFAULT_EXEC_PROC = lambda {|args|
|
11
|
-
|
12
|
-
}
|
13
10
|
EXPORTED_ALIASES_HEADER = "# exported aliases"
|
14
11
|
|
15
12
|
# the path of the profile source file
|
@@ -25,9 +22,9 @@ class Morpheus::Cli::DotFile
|
|
25
22
|
end
|
26
23
|
|
27
24
|
attr_reader :filename
|
28
|
-
attr_reader :file_contents
|
29
|
-
attr_reader :commands
|
30
|
-
attr_reader :cmd_results
|
25
|
+
# attr_reader :file_contents
|
26
|
+
# attr_reader :commands
|
27
|
+
# attr_reader :cmd_results
|
31
28
|
|
32
29
|
def initialize(fn)
|
33
30
|
@filename = fn
|
@@ -44,9 +41,9 @@ class Morpheus::Cli::DotFile
|
|
44
41
|
if !File.exists?(@filename)
|
45
42
|
print "#{Term::ANSIColor.red}source file not found: #{@filename}#{Term::ANSIColor.reset}\n" # if Morpheus::Logging.debug?
|
46
43
|
else
|
47
|
-
|
44
|
+
Morpheus::Logging::DarkPrinter.puts "executing source file #{@filename}" if Morpheus::Logging.debug?
|
48
45
|
end
|
49
|
-
file_contents
|
46
|
+
file_contents = File.read(@filename)
|
50
47
|
lines = file_contents.split("\n")
|
51
48
|
cmd_results = []
|
52
49
|
line_num = 0
|
@@ -1,40 +1,204 @@
|
|
1
1
|
require 'term/ansicolor'
|
2
2
|
require 'optparse'
|
3
|
-
|
3
|
+
require 'json'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'net/https'
|
4
6
|
require 'morpheus/logging'
|
7
|
+
require 'morpheus/cli/command_error'
|
8
|
+
|
5
9
|
class Morpheus::Cli::ErrorHandler
|
6
|
-
include
|
10
|
+
include Term::ANSIColor
|
11
|
+
|
12
|
+
def initialize(io=$stderr)
|
13
|
+
@stderr = io
|
14
|
+
end
|
7
15
|
|
8
16
|
def handle_error(err, options={})
|
17
|
+
exit_code = 1
|
9
18
|
# heh
|
10
19
|
if Morpheus::Logging.debug? && options[:debug].nil?
|
11
20
|
options[:debug] = true
|
12
21
|
end
|
22
|
+
do_print_stacktrace = true
|
13
23
|
case (err)
|
14
|
-
when OptionParser::InvalidOption, OptionParser::AmbiguousOption,
|
24
|
+
when ::OptionParser::InvalidOption, ::OptionParser::AmbiguousOption,
|
25
|
+
::OptionParser::MissingArgument, ::OptionParser::InvalidArgument,
|
26
|
+
::OptionParser::NeedlessArgument
|
15
27
|
# raise err
|
16
|
-
|
17
|
-
|
28
|
+
# @stderr.puts "#{red}#{err.message}#{reset}"
|
29
|
+
puts_angry_error err.message
|
30
|
+
@stderr.puts "Try -h for help with this command."
|
31
|
+
do_print_stacktrace = false
|
32
|
+
# exit_code = 127
|
33
|
+
when Morpheus::Cli::CommandError
|
34
|
+
# @stderr.puts "#{red}#{err.message}#{reset}"
|
35
|
+
puts_angry_error err.message
|
36
|
+
do_print_stacktrace = false
|
37
|
+
# @stderr.puts "Try -h for help with this command."
|
38
|
+
when SocketError
|
39
|
+
@stderr.puts "#{red}Error Communicating with the Appliance.#{reset}"
|
40
|
+
@stderr.puts "#{red}#{err.message}#{reset}"
|
41
|
+
when RestClient::Exceptions::Timeout
|
42
|
+
@stderr.puts "#{red}Error Communicating with the Appliance.#{reset}"
|
43
|
+
@stderr.puts "#{red}#{err.message}#{reset}"
|
18
44
|
when Errno::ECONNREFUSED
|
19
|
-
|
20
|
-
#
|
45
|
+
@stderr.puts "#{red}Error Communicating with the Appliance.#{reset}"
|
46
|
+
@stderr.puts "#{red}#{err.message}#{reset}"
|
47
|
+
# @stderr.puts "Try -h for help with this command."
|
21
48
|
when OpenSSL::SSL::SSLError
|
22
|
-
|
49
|
+
@stderr.puts "#{red}Error Communicating with the Appliance.#{reset}"
|
50
|
+
@stderr.puts "#{red}#{err.message}#{reset}"
|
23
51
|
when RestClient::Exception
|
24
52
|
print_rest_exception(err, options)
|
25
53
|
else
|
26
|
-
|
27
|
-
|
28
|
-
|
54
|
+
@stderr.puts "#{red}Unexpected Error#{reset}"
|
55
|
+
end
|
56
|
+
|
57
|
+
if do_print_stacktrace
|
58
|
+
if options[:debug]
|
59
|
+
if err.is_a?(Exception)
|
60
|
+
print_stacktrace(err)
|
61
|
+
else
|
62
|
+
@stderr.puts err.to_s
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@stderr.puts "Use --debug for more information."
|
29
66
|
end
|
30
67
|
end
|
31
68
|
|
32
|
-
|
33
|
-
|
34
|
-
|
69
|
+
return exit_code
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def print_stacktrace(err)
|
74
|
+
@stderr.print red, "\n", "#{err.class}: #{err.message}", "\n", reset
|
75
|
+
@stderr.print err.backtrace.join("\n"), "\n\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
# def handle_rest_exception(err, io=@stderr)
|
79
|
+
# if !err.is_a?(RestClient::Exception)
|
80
|
+
# raise err
|
81
|
+
# end
|
82
|
+
# print_rest_exception(err, io=@stderr)
|
83
|
+
# end
|
84
|
+
|
85
|
+
|
86
|
+
def print_rest_exception(err, options)
|
87
|
+
e = err
|
88
|
+
if err.response
|
89
|
+
if options[:debug]
|
90
|
+
begin
|
91
|
+
print_rest_exception_request_and_response(e)
|
92
|
+
ensure
|
93
|
+
@stderr.print reset
|
94
|
+
end
|
95
|
+
return
|
96
|
+
end
|
97
|
+
if err.response.code == 400
|
98
|
+
response = JSON.parse(err.response.to_s)
|
99
|
+
print_rest_errors(response, options)
|
100
|
+
else
|
101
|
+
@stderr.print red, "Error Communicating with the Appliance. #{e}", reset, "\n"
|
102
|
+
if options[:json] || options[:debug]
|
103
|
+
begin
|
104
|
+
response = JSON.parse(e.response.to_s)
|
105
|
+
# @stderr.print red
|
106
|
+
@stderr.print JSON.pretty_generate(response)
|
107
|
+
@stderr.print reset, "\n"
|
108
|
+
rescue TypeError, JSON::ParserError => ex
|
109
|
+
@stderr.print red, "Failed to parse JSON response: #{ex}", reset, "\n"
|
110
|
+
# @stderr.print red
|
111
|
+
@stderr.print response.to_s
|
112
|
+
@stderr.print reset, "\n"
|
113
|
+
ensure
|
114
|
+
@stderr.print reset
|
115
|
+
end
|
116
|
+
else
|
117
|
+
@stderr.puts "Use --debug for more information."
|
118
|
+
end
|
119
|
+
end
|
35
120
|
else
|
36
|
-
|
121
|
+
@stderr.print red, "Error Communicating with the Appliance. #{e}", reset, "\n"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def print_rest_errors(response, options={})
|
126
|
+
begin
|
127
|
+
if options[:json]
|
128
|
+
@stderr.print red
|
129
|
+
@stderr.print JSON.pretty_generate(response)
|
130
|
+
@stderr.print reset, "\n"
|
131
|
+
else
|
132
|
+
if !response['success']
|
133
|
+
@stderr.print red,bold
|
134
|
+
if response['msg']
|
135
|
+
@stderr.puts response['msg']
|
136
|
+
end
|
137
|
+
if response['errors']
|
138
|
+
response['errors'].each do |key, value|
|
139
|
+
@stderr.print "* #{key}: #{value}\n"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
@stderr.print reset
|
143
|
+
else
|
144
|
+
# this should not really happen
|
145
|
+
@stderr.print cyan,bold, "\nSuccess!"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
ensure
|
149
|
+
@stderr.print reset
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def print_rest_request(req)
|
154
|
+
@stderr.print "Request:"
|
155
|
+
@stderr.print "\n"
|
156
|
+
@stderr.print "#{req.method.to_s.upcase} #{req.url.inspect}"
|
157
|
+
@stderr.print "\n"
|
158
|
+
end
|
159
|
+
|
160
|
+
def print_rest_response(res)
|
161
|
+
# size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
|
162
|
+
size = (res.body.nil? ? 0 : res.body.size)
|
163
|
+
@stderr.print "Response:"
|
164
|
+
@stderr.print "\n"
|
165
|
+
display_size = Filesize.from("#{size} B").pretty rescue size
|
166
|
+
@stderr.print "HTTP #{res.net_http_res.code} - #{res.net_http_res.message} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{display_size}"
|
167
|
+
@stderr.print "\n"
|
168
|
+
begin
|
169
|
+
@stderr.print JSON.pretty_generate(JSON.parse(res.body))
|
170
|
+
rescue
|
171
|
+
@stderr.print res.body.to_s
|
37
172
|
end
|
173
|
+
@stderr.print "\n"
|
38
174
|
end
|
39
175
|
|
176
|
+
def print_rest_exception_request_and_response(e)
|
177
|
+
@stderr.puts "#{red}Error Communicating with the Appliance. (#{e.response.code})#{reset}"
|
178
|
+
response = e.response
|
179
|
+
request = response.instance_variable_get("@request")
|
180
|
+
@stderr.print red
|
181
|
+
print_rest_request(request)
|
182
|
+
@stderr.print "\n"
|
183
|
+
print_rest_response(response)
|
184
|
+
@stderr.print reset
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
protected
|
189
|
+
|
190
|
+
def puts_angry_error(*msgs)
|
191
|
+
# @stderr.print "#{Term::ANSIColor.red}morpheus: #{Term::ANSIColor.reset}#{msg}\n"
|
192
|
+
@stderr.print(Morpheus::Terminal.angry_prompt)
|
193
|
+
@stderr.puts(*msgs)
|
194
|
+
end
|
195
|
+
|
196
|
+
# def puts(*args)
|
197
|
+
# @stderr.puts(*args)
|
198
|
+
# end
|
199
|
+
|
200
|
+
# def print(*args)
|
201
|
+
# @stderr.print(*args)
|
202
|
+
# end
|
203
|
+
|
40
204
|
end
|
data/lib/morpheus/cli/groups.rb
CHANGED
@@ -52,7 +52,7 @@ class Morpheus::Cli::Groups
|
|
52
52
|
return
|
53
53
|
end
|
54
54
|
groups = json_response['groups']
|
55
|
-
|
55
|
+
print_h1 "Morpheus Groups"
|
56
56
|
if groups.empty?
|
57
57
|
puts yellow,"No groups currently configured.",reset
|
58
58
|
else
|
@@ -62,7 +62,7 @@ class Morpheus::Cli::Groups
|
|
62
62
|
active_group = groups.find { |it| it['id'] == @active_group_id }
|
63
63
|
active_group = active_group || find_group_by_name_or_id(@active_group_id)
|
64
64
|
#unless @appliances.keys.size == 1
|
65
|
-
print cyan, "\n# => Currently using #{active_group['name']}\n", reset
|
65
|
+
print cyan, "\n# => Currently using group #{active_group['name']}\n", reset
|
66
66
|
#end
|
67
67
|
else
|
68
68
|
unless options[:remote]
|
@@ -99,17 +99,24 @@ class Morpheus::Cli::Groups
|
|
99
99
|
return
|
100
100
|
end
|
101
101
|
group = json_response['group']
|
102
|
-
|
103
102
|
is_active = @active_group_id && (@active_group_id == group['id'])
|
104
|
-
|
105
|
-
print "\n" ,cyan, bold, "Group Details\n","==================", reset, "\n\n"
|
103
|
+
print_h1 "Group Details"
|
106
104
|
print cyan
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
105
|
+
description_cols = {
|
106
|
+
"ID" => 'id',
|
107
|
+
"Name" => 'name',
|
108
|
+
"Code" => 'code',
|
109
|
+
"Location" => 'location',
|
110
|
+
"Clouds" => lambda {|it| it['zones'].collect {|z| z['name'] }.join(', ') },
|
111
|
+
"Hosts" => 'serverCount'
|
112
|
+
}
|
113
|
+
print_description_list(description_cols, group)
|
114
|
+
# puts "ID: #{group['id']}"
|
115
|
+
# puts "Name: #{group['name']}"
|
116
|
+
# puts "Code: #{group['code']}"
|
117
|
+
# puts "Location: #{group['location']}"
|
118
|
+
# puts "Clouds: #{group['zones'].collect {|it| it['name'] }.join(', ')}"
|
119
|
+
# puts "Hosts: #{group['serverCount']}"
|
113
120
|
if is_active
|
114
121
|
puts "\n => This is the active group."
|
115
122
|
end
|
@@ -441,7 +448,8 @@ class Morpheus::Cli::Groups
|
|
441
448
|
if group
|
442
449
|
print cyan,group['name'].to_s,"\n",reset
|
443
450
|
else
|
444
|
-
print
|
451
|
+
print yellow,"No active group. See `groups use`","\n",reset
|
452
|
+
return false
|
445
453
|
end
|
446
454
|
end
|
447
455
|
|
@@ -539,7 +547,7 @@ public
|
|
539
547
|
def load_group_file
|
540
548
|
fn = groups_file_path
|
541
549
|
if File.exist? fn
|
542
|
-
|
550
|
+
Morpheus::Logging::DarkPrinter.puts "loading groups file #{fn}" if Morpheus::Logging.debug?
|
543
551
|
return YAML.load_file(fn)
|
544
552
|
else
|
545
553
|
{}
|
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -44,7 +44,44 @@ class Morpheus::Cli::Hosts
|
|
44
44
|
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
45
45
|
options[:cloud] = val
|
46
46
|
end
|
47
|
-
|
47
|
+
opts.on( '-M', '--managed', "Show only Managed Servers" ) do |val|
|
48
|
+
params[:managed] = true
|
49
|
+
end
|
50
|
+
opts.on( '-U', '--unmanaged', "Show only Unmanaged Servers" ) do |val|
|
51
|
+
params[:managed] = false
|
52
|
+
end
|
53
|
+
opts.on( '-t', '--type TYPE', "Show only Certain Server Types" ) do |val|
|
54
|
+
params[:serverType] = val
|
55
|
+
end
|
56
|
+
opts.on( '-p', '--power STATE', "Filter by Power Status" ) do |val|
|
57
|
+
params[:powerState] = val
|
58
|
+
end
|
59
|
+
opts.on( '-i', '--ip IPADDRESS', "Filter by IP Address" ) do |val|
|
60
|
+
params[:ip] = val
|
61
|
+
end
|
62
|
+
opts.on( '', '--vm', "Show only virtual machines" ) do |val|
|
63
|
+
params[:vm] = true
|
64
|
+
end
|
65
|
+
opts.on( '', '--hypervisor', "Show only VM Hypervisors" ) do |val|
|
66
|
+
params[:vmHypervisor] = true
|
67
|
+
end
|
68
|
+
opts.on( '', '--container', "Show only Container Hypervisors" ) do |val|
|
69
|
+
params[:containerHypervisor] = true
|
70
|
+
end
|
71
|
+
opts.on( '', '--baremetal', "Show only Baremetal Servers" ) do |val|
|
72
|
+
params[:bareMetalHost] = true
|
73
|
+
end
|
74
|
+
opts.on( '', '--status STATUS', "Filter by Status" ) do |val|
|
75
|
+
params[:status] = val
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on( '', '--agent', "Show only Servers with the agent installed" ) do |val|
|
79
|
+
params[:agentInstalled] = true
|
80
|
+
end
|
81
|
+
opts.on( '', '--noagent', "Show only Servers with No agent" ) do |val|
|
82
|
+
params[:agentInstalled] = false
|
83
|
+
end
|
84
|
+
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
48
85
|
end
|
49
86
|
optparse.parse!(args)
|
50
87
|
connect(options)
|
@@ -72,8 +109,27 @@ class Morpheus::Cli::Hosts
|
|
72
109
|
json_response = @servers_interface.get(params)
|
73
110
|
|
74
111
|
if options[:json]
|
75
|
-
|
76
|
-
|
112
|
+
if options[:include_fields]
|
113
|
+
json_response = {"servers" => filter_data(json_response["servers"], options[:include_fields]) }
|
114
|
+
end
|
115
|
+
puts as_json(json_response, options)
|
116
|
+
return 0
|
117
|
+
elsif options[:yaml]
|
118
|
+
if options[:include_fields]
|
119
|
+
json_response = {"servers" => filter_data(json_response["servers"], options[:include_fields]) }
|
120
|
+
end
|
121
|
+
puts as_yaml(json_response, options)
|
122
|
+
return 0
|
123
|
+
elsif options[:csv]
|
124
|
+
# merge stats to be nice here..
|
125
|
+
if json_response['servers']
|
126
|
+
all_stats = json_response['stats'] || {}
|
127
|
+
json_response['servers'].each do |it|
|
128
|
+
it['stats'] ||= all_stats[it['id'].to_s] || all_stats[it['id']]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
puts records_as_csv(json_response['servers'], options)
|
132
|
+
return 0
|
77
133
|
else
|
78
134
|
servers = json_response['servers']
|
79
135
|
title = "Morpheus Hosts"
|
@@ -87,12 +143,59 @@ class Morpheus::Cli::Hosts
|
|
87
143
|
if params[:phrase]
|
88
144
|
subtitles << "Search: #{params[:phrase]}".strip
|
89
145
|
end
|
90
|
-
|
91
|
-
print "\n" ,cyan, bold, title, (subtitle.empty? ? "" : " - #{subtitle}"), "\n", "==================", reset, "\n\n"
|
146
|
+
print_h1 title, subtitles
|
92
147
|
if servers.empty?
|
93
|
-
|
148
|
+
print yellow,"No hosts found.",reset,"\n"
|
94
149
|
else
|
95
|
-
print_servers_table(servers)
|
150
|
+
# print_servers_table(servers)
|
151
|
+
# server returns stats in a separate key stats => {"id" => {} }
|
152
|
+
# the id is a string right now..for some reason..
|
153
|
+
all_stats = json_response['stats'] || {}
|
154
|
+
servers.each do |it|
|
155
|
+
found_stats = all_stats[it['id'].to_s] || all_stats[it['id']]
|
156
|
+
if !it['stats']
|
157
|
+
it['stats'] = found_stats # || {}
|
158
|
+
else
|
159
|
+
it['stats'] = found_stats.merge!(it['stats'])
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
rows = servers.collect {|server|
|
164
|
+
stats = server['stats']
|
165
|
+
|
166
|
+
if !stats['maxMemory']
|
167
|
+
stats['maxMemory'] = stats['usedMemory'] + stats['freeMemory']
|
168
|
+
end
|
169
|
+
cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
|
170
|
+
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
171
|
+
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
172
|
+
row = {
|
173
|
+
id: server['id'],
|
174
|
+
name: server['name'],
|
175
|
+
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
176
|
+
cloud: server['zone'] ? server['zone']['name'] : '',
|
177
|
+
type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
|
178
|
+
nodes: server['containers'] ? server['containers'].size : '',
|
179
|
+
status: format_host_status(server, cyan),
|
180
|
+
power: format_server_power_state(server, cyan),
|
181
|
+
cpu: cpu_usage_str + cyan,
|
182
|
+
memory: memory_usage_str + cyan,
|
183
|
+
storage: storage_usage_str + cyan
|
184
|
+
}
|
185
|
+
row
|
186
|
+
}
|
187
|
+
columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
|
188
|
+
term_width = current_terminal_width()
|
189
|
+
if term_width > 170
|
190
|
+
columns += [:cpu, :memory, :storage]
|
191
|
+
end
|
192
|
+
# custom pretty table columns ...
|
193
|
+
if options[:include_fields]
|
194
|
+
columns = options[:include_fields]
|
195
|
+
end
|
196
|
+
print cyan
|
197
|
+
print as_pretty_table(rows, columns, options)
|
198
|
+
print reset
|
96
199
|
print_results_pagination(json_response)
|
97
200
|
end
|
98
201
|
print reset,"\n"
|
@@ -107,7 +210,7 @@ class Morpheus::Cli::Hosts
|
|
107
210
|
options = {}
|
108
211
|
optparse = OptionParser.new do|opts|
|
109
212
|
opts.banner = subcommand_usage("[name]")
|
110
|
-
build_common_options(opts, options, [:json, :dry_run, :remote])
|
213
|
+
build_common_options(opts, options, [:json, :csv, :yaml, :fields, :dry_run, :remote])
|
111
214
|
end
|
112
215
|
optparse.parse!(args)
|
113
216
|
if args.count < 1
|
@@ -115,58 +218,65 @@ class Morpheus::Cli::Hosts
|
|
115
218
|
exit 1
|
116
219
|
end
|
117
220
|
connect(options)
|
221
|
+
id_list = parse_id_list(args)
|
222
|
+
return run_command_for_each_arg(id_list) do |arg|
|
223
|
+
_get(arg, options)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def _get(arg, options)
|
118
228
|
begin
|
119
229
|
if options[:dry_run]
|
120
|
-
if
|
121
|
-
print_dry_run @servers_interface.dry.get(
|
230
|
+
if arg.to_s =~ /\A\d{1,}\Z/
|
231
|
+
print_dry_run @servers_interface.dry.get(arg.to_i)
|
122
232
|
else
|
123
|
-
print_dry_run @servers_interface.dry.get({name:
|
233
|
+
print_dry_run @servers_interface.dry.get({name: arg})
|
124
234
|
end
|
125
235
|
return
|
126
236
|
end
|
127
|
-
server = find_host_by_name_or_id(
|
237
|
+
server = find_host_by_name_or_id(arg)
|
128
238
|
json_response = @servers_interface.get(server['id'])
|
129
239
|
if options[:json]
|
130
|
-
|
131
|
-
|
240
|
+
if options[:include_fields]
|
241
|
+
json_response = {"server" => filter_data(json_response["server"], options[:include_fields]) }
|
242
|
+
end
|
243
|
+
puts as_json(json_response, options)
|
244
|
+
return 0
|
245
|
+
elsif options[:yaml]
|
246
|
+
if options[:include_fields]
|
247
|
+
json_response = {"server" => filter_data(json_response["server"], options[:include_fields]) }
|
248
|
+
end
|
249
|
+
puts as_yaml(json_response, options)
|
250
|
+
return 0
|
251
|
+
end
|
252
|
+
if options[:csv]
|
253
|
+
puts records_as_csv([json_response['server']], options)
|
254
|
+
return 0
|
132
255
|
end
|
133
256
|
server = json_response['server']
|
134
257
|
#stats = server['stats'] || json_response['stats'] || {}
|
135
258
|
stats = json_response['stats'] || {}
|
136
|
-
|
137
|
-
|
259
|
+
title = "Host Details"
|
260
|
+
print_h1 title
|
138
261
|
print cyan
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
if ((stats['maxMemory'].to_i != 0) || (stats['maxStorage'].to_i != 0))
|
158
|
-
# stats_map = {}
|
159
|
-
print "\n"
|
160
|
-
#print "\n" ,cyan, bold, "Host Stats\n","==================", reset, "\n\n"
|
161
|
-
# stats_map[:memory] = "#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}"
|
162
|
-
# stats_map[:storage] = "#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}"
|
163
|
-
# stats_map[:cpu] = "#{stats['cpuUsage'].to_f.round(2)}%"
|
164
|
-
# tp [stats_map], :memory,:storage,:cpu
|
165
|
-
print_stats_usage(stats)
|
166
|
-
else
|
167
|
-
#print yellow, "No stat data.", reset
|
168
|
-
end
|
169
|
-
|
262
|
+
print_description_list({
|
263
|
+
"ID" => 'id',
|
264
|
+
"Name" => 'name',
|
265
|
+
"Description" => 'description',
|
266
|
+
"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
267
|
+
#"Group" => lambda {|it| it['group'] ? it['group']['name'] : '' },
|
268
|
+
"Cloud" => lambda {|it| it['zone'] ? it['zone']['name'] : '' },
|
269
|
+
"Type" => lambda {|it| it['computeServerType'] ? it['computeServerType']['name'] : 'unmanaged' },
|
270
|
+
"Platform" => lambda {|it| it['serverOs'] ? it['serverOs']['name'].upcase : 'N/A' },
|
271
|
+
"Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
|
272
|
+
"Agent" => lambda {|it| it['agentInstalled'] ? "#{server['agentVersion'] || ''} updated at #{format_local_dt(server['lastAgentUpdate'])}" : '(not installed)' },
|
273
|
+
"Status" => lambda {|it| format_host_status(it) },
|
274
|
+
"Nodes" => lambda {|it| it['containers'] ? it['containers'].size : 0 },
|
275
|
+
"Power" => lambda {|it| format_server_power_state(it) },
|
276
|
+
}, server)
|
277
|
+
|
278
|
+
print_h2 "Host Usage"
|
279
|
+
print_stats_usage(stats)
|
170
280
|
print reset, "\n"
|
171
281
|
|
172
282
|
rescue RestClient::Exception => e
|
@@ -179,7 +289,7 @@ class Morpheus::Cli::Hosts
|
|
179
289
|
options = {}
|
180
290
|
optparse = OptionParser.new do|opts|
|
181
291
|
opts.banner = subcommand_usage("[name]")
|
182
|
-
build_common_options(opts, options, [:json, :dry_run, :remote])
|
292
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
183
293
|
end
|
184
294
|
optparse.parse!(args)
|
185
295
|
if args.count < 1
|
@@ -187,43 +297,51 @@ class Morpheus::Cli::Hosts
|
|
187
297
|
exit 1
|
188
298
|
end
|
189
299
|
connect(options)
|
300
|
+
ids = args
|
301
|
+
id_list = parse_id_list(args)
|
302
|
+
return run_command_for_each_arg(id_list) do |arg|
|
303
|
+
_stats(arg, options)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def _stats(arg, options)
|
190
308
|
begin
|
191
309
|
if options[:dry_run]
|
192
|
-
if
|
193
|
-
print_dry_run @servers_interface.dry.get(
|
310
|
+
if arg.to_s =~ /\A\d{1,}\Z/
|
311
|
+
print_dry_run @servers_interface.dry.get(arg.to_i)
|
194
312
|
else
|
195
|
-
print_dry_run @servers_interface.dry.get({name:
|
313
|
+
print_dry_run @servers_interface.dry.get({name: arg})
|
196
314
|
end
|
197
315
|
return
|
198
316
|
end
|
199
|
-
server = find_host_by_name_or_id(
|
317
|
+
server = find_host_by_name_or_id(arg)
|
200
318
|
json_response = @servers_interface.get(server['id'])
|
201
319
|
if options[:json]
|
202
320
|
print JSON.pretty_generate(json_response), "\n"
|
203
|
-
return
|
321
|
+
return 0
|
322
|
+
elsif options[:yaml]
|
323
|
+
if options[:include_fields]
|
324
|
+
json_response = {"stats" => filter_data(json_response["stats"], options[:include_fields]) }
|
325
|
+
end
|
326
|
+
puts as_yaml(json_response, options)
|
327
|
+
return 0
|
328
|
+
elsif options[:csv]
|
329
|
+
puts records_as_csv([json_response['stats']], options)
|
330
|
+
return 0
|
204
331
|
end
|
205
332
|
server = json_response['server']
|
206
333
|
#stats = server['stats'] || json_response['stats'] || {}
|
207
334
|
stats = json_response['stats'] || {}
|
208
|
-
|
209
|
-
|
210
|
-
puts "
|
211
|
-
puts "
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
#print "\n" ,cyan, bold, "Host Stats\n","==================", reset, "\n\n"
|
216
|
-
# stats_map[:memory] = "#{Filesize.from("#{stats['usedMemory']} B").pretty} / #{Filesize.from("#{stats['maxMemory']} B").pretty}"
|
217
|
-
# stats_map[:storage] = "#{Filesize.from("#{stats['usedStorage']} B").pretty} / #{Filesize.from("#{stats['maxStorage']} B").pretty}"
|
218
|
-
# stats_map[:cpu] = "#{stats['cpuUsage'].to_f.round(2)}%"
|
219
|
-
# tp [stats_map], :memory,:storage,:cpu
|
220
|
-
print_stats_usage(stats)
|
221
|
-
else
|
222
|
-
#print yellow, "No stat data.", reset
|
223
|
-
end
|
335
|
+
title = "Host Stats: #{server['name']} (#{server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged'})"
|
336
|
+
print_h1 title
|
337
|
+
puts cyan + "Power: ".rjust(12) + format_server_power_state(server).to_s
|
338
|
+
puts cyan + "Status: ".rjust(12) + format_host_status(server).to_s
|
339
|
+
puts cyan + "Nodes: ".rjust(12) + (server['containers'] ? server['containers'].size : '').to_s
|
340
|
+
#print_h2 "Host Usage"
|
341
|
+
print_stats_usage(stats, {label_width: 10})
|
224
342
|
|
225
343
|
print reset, "\n"
|
226
|
-
|
344
|
+
return 0
|
227
345
|
rescue RestClient::Exception => e
|
228
346
|
print_rest_exception(e, options)
|
229
347
|
exit 1
|
@@ -243,36 +361,47 @@ class Morpheus::Cli::Hosts
|
|
243
361
|
end
|
244
362
|
connect(options)
|
245
363
|
begin
|
246
|
-
|
364
|
+
server = find_host_by_name_or_id(args[0])
|
247
365
|
params = {}
|
248
366
|
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
249
367
|
params[k] = options[k] unless options[k].nil?
|
250
368
|
end
|
251
369
|
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
252
370
|
if options[:dry_run]
|
253
|
-
print_dry_run @logs_interface.dry.server_logs([
|
371
|
+
print_dry_run @logs_interface.dry.server_logs([server['id']], params)
|
254
372
|
return
|
255
373
|
end
|
256
|
-
logs = @logs_interface.server_logs([
|
374
|
+
logs = @logs_interface.server_logs([server['id']], params)
|
257
375
|
output = ""
|
258
376
|
if options[:json]
|
259
377
|
output << JSON.pretty_generate(logs)
|
260
378
|
else
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
379
|
+
title = "Host Logs: #{server['name']} (#{server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged'})"
|
380
|
+
subtitles = []
|
381
|
+
if params[:query]
|
382
|
+
subtitles << "Search: #{params[:query]}".strip
|
383
|
+
end
|
384
|
+
# todo: startMs, endMs, sorts insteaad of sort..etc
|
385
|
+
print_h1 title, subtitles
|
386
|
+
if logs['data'].empty?
|
387
|
+
output << "#{cyan}No logs found.#{reset}\n"
|
388
|
+
else
|
389
|
+
logs['data'].reverse.each do |log_entry|
|
390
|
+
log_level = ''
|
391
|
+
case log_entry['level']
|
392
|
+
when 'INFO'
|
393
|
+
log_level = "#{blue}#{bold}INFO#{reset}"
|
394
|
+
when 'DEBUG'
|
395
|
+
log_level = "#{white}#{bold}DEBUG#{reset}"
|
396
|
+
when 'WARN'
|
397
|
+
log_level = "#{yellow}#{bold}WARN#{reset}"
|
398
|
+
when 'ERROR'
|
399
|
+
log_level = "#{red}#{bold}ERROR#{reset}"
|
400
|
+
when 'FATAL'
|
401
|
+
log_level = "#{red}#{bold}FATAL#{reset}"
|
402
|
+
end
|
403
|
+
output << "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}\n"
|
274
404
|
end
|
275
|
-
output << "[#{log_entry['ts']}] #{log_level} - #{log_entry['message']}\n"
|
276
405
|
end
|
277
406
|
end
|
278
407
|
print output, reset, "\n"
|
@@ -305,9 +434,9 @@ class Morpheus::Cli::Hosts
|
|
305
434
|
print JSON.pretty_generate(cloud_server_types)
|
306
435
|
print "\n"
|
307
436
|
else
|
308
|
-
|
437
|
+
print_h1 "Morpheus Server Types - Cloud: #{zone['name']}"
|
309
438
|
if cloud_server_types.nil? || cloud_server_types.empty?
|
310
|
-
|
439
|
+
print yellow,"No server types found for the selected cloud",reset,"\n"
|
311
440
|
else
|
312
441
|
cloud_server_types.each do |server_type|
|
313
442
|
print cyan, "[#{server_type['code']}]".ljust(20), " - ", "#{server_type['name']}", "\n"
|
@@ -424,7 +553,7 @@ class Morpheus::Cli::Hosts
|
|
424
553
|
payload[:networkInterfaces] = network_interfaces
|
425
554
|
end
|
426
555
|
rescue RestClient::Exception => e
|
427
|
-
|
556
|
+
print yellow,"Unable to load network options. Proceeding...",reset,"\n"
|
428
557
|
print_rest_exception(e, options) if Morpheus::Logging.debug?
|
429
558
|
end
|
430
559
|
end
|
@@ -644,7 +773,7 @@ class Morpheus::Cli::Hosts
|
|
644
773
|
# payload[:networkInterfaces] = network_interfaces
|
645
774
|
# end
|
646
775
|
# rescue RestClient::Exception => e
|
647
|
-
#
|
776
|
+
# print yellow,"Unable to load network options. Proceeding...",reset,"\n"
|
648
777
|
# print_rest_exception(e, options) if Morpheus::Logging.debug?
|
649
778
|
# end
|
650
779
|
# end
|
@@ -898,25 +1027,6 @@ class Morpheus::Cli::Hosts
|
|
898
1027
|
end
|
899
1028
|
end
|
900
1029
|
|
901
|
-
def print_servers_table(servers, opts={})
|
902
|
-
table_color = opts[:color] || cyan
|
903
|
-
rows = servers.collect do |server|
|
904
|
-
{
|
905
|
-
id: server['id'],
|
906
|
-
name: server['name'],
|
907
|
-
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
908
|
-
cloud: server['zone'] ? server['zone']['name'] : '',
|
909
|
-
type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
|
910
|
-
nodes: server['containers'] ? server['containers'].size : '',
|
911
|
-
status: format_host_status(server, table_color),
|
912
|
-
power: format_server_power_state(server, table_color)
|
913
|
-
}
|
914
|
-
end
|
915
|
-
columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
|
916
|
-
print table_color
|
917
|
-
tp rows, columns
|
918
|
-
print reset
|
919
|
-
end
|
920
1030
|
|
921
1031
|
def format_server_power_state(server, return_color=cyan)
|
922
1032
|
out = ""
|