morpheus-cli 4.2.12 → 4.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/README.md +8 -6
- data/lib/morpheus/api/api_client.rb +32 -14
- data/lib/morpheus/api/auth_interface.rb +4 -2
- data/lib/morpheus/api/backup_jobs_interface.rb +9 -0
- data/lib/morpheus/api/backups_interface.rb +16 -0
- data/lib/morpheus/api/deploy_interface.rb +25 -56
- data/lib/morpheus/api/deployments_interface.rb +43 -54
- data/lib/morpheus/api/doc_interface.rb +57 -0
- data/lib/morpheus/api/instances_interface.rb +5 -0
- data/lib/morpheus/api/rest_interface.rb +40 -0
- data/lib/morpheus/api/user_sources_interface.rb +0 -15
- data/lib/morpheus/api/users_interface.rb +2 -3
- data/lib/morpheus/benchmarking.rb +2 -2
- data/lib/morpheus/cli.rb +3 -1
- data/lib/morpheus/cli/access_token_command.rb +27 -10
- data/lib/morpheus/cli/apps.rb +23 -16
- data/lib/morpheus/cli/backup_jobs_command.rb +276 -0
- data/lib/morpheus/cli/backups_command.rb +271 -0
- data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +176 -45
- data/lib/morpheus/cli/cli_registry.rb +10 -1
- data/lib/morpheus/cli/clusters.rb +2 -19
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +23 -20
- data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
- data/lib/morpheus/cli/containers_command.rb +2 -1
- data/lib/morpheus/cli/credentials.rb +14 -10
- data/lib/morpheus/cli/deploy.rb +374 -0
- data/lib/morpheus/cli/deployments.rb +521 -197
- data/lib/morpheus/cli/deploys.rb +271 -126
- data/lib/morpheus/cli/doc.rb +182 -0
- data/lib/morpheus/cli/error_handler.rb +23 -8
- data/lib/morpheus/cli/errors.rb +3 -2
- data/lib/morpheus/cli/health_command.rb +4 -3
- data/lib/morpheus/cli/hosts.rb +2 -1
- data/lib/morpheus/cli/image_builder_command.rb +2 -2
- data/lib/morpheus/cli/instances.rb +138 -18
- data/lib/morpheus/cli/invoices_command.rb +338 -223
- data/lib/morpheus/cli/library_layouts_command.rb +1 -1
- data/lib/morpheus/cli/library_option_lists_command.rb +61 -125
- data/lib/morpheus/cli/library_option_types_command.rb +32 -37
- data/lib/morpheus/cli/login.rb +9 -3
- data/lib/morpheus/cli/logs_command.rb +3 -2
- data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
- data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
- data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
- data/lib/morpheus/cli/mixins/library_helper.rb +32 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +18 -9
- data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +149 -84
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
- data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
- data/lib/morpheus/cli/network_routers_command.rb +1 -1
- data/lib/morpheus/cli/option_parser.rb +48 -5
- data/lib/morpheus/cli/option_types.rb +46 -10
- data/lib/morpheus/cli/price_sets_command.rb +1 -1
- data/lib/morpheus/cli/remote.rb +8 -10
- data/lib/morpheus/cli/roles.rb +49 -92
- data/lib/morpheus/cli/security_groups.rb +7 -1
- data/lib/morpheus/cli/service_plans_command.rb +10 -10
- data/lib/morpheus/cli/setup.rb +1 -1
- data/lib/morpheus/cli/shell.rb +7 -6
- data/lib/morpheus/cli/subnets_command.rb +1 -1
- data/lib/morpheus/cli/tenants_command.rb +133 -163
- data/lib/morpheus/cli/user_groups_command.rb +20 -65
- data/lib/morpheus/cli/user_settings_command.rb +115 -13
- data/lib/morpheus/cli/user_sources_command.rb +57 -24
- data/lib/morpheus/cli/users.rb +210 -186
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
- data/lib/morpheus/cli/whoami.rb +113 -6
- data/lib/morpheus/cli/workflows.rb +1 -1
- data/lib/morpheus/ext/hash.rb +21 -0
- data/lib/morpheus/formatters.rb +7 -19
- data/lib/morpheus/terminal.rb +1 -0
- metadata +12 -3
- data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::BackupsCommand
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
include Morpheus::Cli::BackupsHelper
|
6
|
+
include Morpheus::Cli::ProvisioningHelper
|
7
|
+
include Morpheus::Cli::OptionSourceHelper
|
8
|
+
|
9
|
+
set_command_hidden # hide until ready
|
10
|
+
|
11
|
+
set_command_name :'backups'
|
12
|
+
|
13
|
+
register_subcommands :list, :get, :add, :update, :remove, :run, :restore
|
14
|
+
|
15
|
+
def connect(opts)
|
16
|
+
@api_client = establish_remote_appliance_connection(opts)
|
17
|
+
@backups_interface = @api_client.backups
|
18
|
+
@backup_jobs_interface = @api_client.backup_jobs
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle(args)
|
22
|
+
handle_subcommand(args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def list(args)
|
26
|
+
options = {}
|
27
|
+
params = {}
|
28
|
+
ref_ids = []
|
29
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
30
|
+
opts.banner = subcommand_usage("[search]")
|
31
|
+
build_standard_list_options(opts, options)
|
32
|
+
opts.footer = "List backups."
|
33
|
+
end
|
34
|
+
optparse.parse!(args)
|
35
|
+
connect(options)
|
36
|
+
# verify_args!(args:args, optparse:optparse, count:0)
|
37
|
+
if args.count > 0
|
38
|
+
options[:phrase] = args.join(" ")
|
39
|
+
end
|
40
|
+
params.merge!(parse_list_options(options))
|
41
|
+
@backups_interface.setopts(options)
|
42
|
+
if options[:dry_run]
|
43
|
+
print_dry_run @backups_interface.dry.list(params)
|
44
|
+
return
|
45
|
+
end
|
46
|
+
json_response = @backups_interface.list(params)
|
47
|
+
backups = json_response['backups']
|
48
|
+
render_response(json_response, options, 'backups') do
|
49
|
+
print_h1 "Morpheus Backups", parse_list_subtitles(options), options
|
50
|
+
if backups.empty?
|
51
|
+
print cyan,"No backups found.",reset,"\n"
|
52
|
+
else
|
53
|
+
print as_pretty_table(backups, backup_column_definitions.upcase_keys!, options)
|
54
|
+
print_results_pagination(json_response)
|
55
|
+
end
|
56
|
+
print reset,"\n"
|
57
|
+
end
|
58
|
+
if backups.empty?
|
59
|
+
return 1, "no backups found"
|
60
|
+
else
|
61
|
+
return 0, nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(args)
|
66
|
+
params = {}
|
67
|
+
options = {}
|
68
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
69
|
+
opts.banner = subcommand_usage("[backup]")
|
70
|
+
build_standard_get_options(opts, options)
|
71
|
+
opts.footer = <<-EOT
|
72
|
+
Get details about a specific backup.
|
73
|
+
[backup] is required. This is the name or id of a backup.
|
74
|
+
EOT
|
75
|
+
end
|
76
|
+
optparse.parse!(args)
|
77
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
78
|
+
connect(options)
|
79
|
+
id_list = parse_id_list(args)
|
80
|
+
return run_command_for_each_arg(id_list) do |arg|
|
81
|
+
_get(arg, params, options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def _get(id, options)
|
86
|
+
params = {}
|
87
|
+
@backups_interface.setopts(options)
|
88
|
+
if options[:dry_run]
|
89
|
+
print_dry_run @backups_interface.dry.get(id, params)
|
90
|
+
return
|
91
|
+
end
|
92
|
+
json_response = @backups_interface.get(id, params)
|
93
|
+
backup = json_response['backup']
|
94
|
+
render_response(json_response, options, 'backup') do
|
95
|
+
print_h1 "Backup Details", [], options
|
96
|
+
print cyan
|
97
|
+
print_description_list(backup_column_definitions, backup)
|
98
|
+
print reset,"\n"
|
99
|
+
end
|
100
|
+
return 0, nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def add(args)
|
104
|
+
options = {}
|
105
|
+
params = {}
|
106
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
107
|
+
opts.banner = subcommand_usage("[name] [options]")
|
108
|
+
build_option_type_options(opts, options, add_backup_option_types)
|
109
|
+
build_option_type_options(opts, options, add_backup_advanced_option_types)
|
110
|
+
build_standard_add_options(opts, options)
|
111
|
+
opts.footer = <<-EOT
|
112
|
+
Create a new backup.
|
113
|
+
EOT
|
114
|
+
end
|
115
|
+
optparse.parse!(args)
|
116
|
+
verify_args!(args:args, optparse:optparse, min:0, max:1)
|
117
|
+
options[:options]['name'] = args[0] if args[0]
|
118
|
+
connect(options)
|
119
|
+
payload = {}
|
120
|
+
if options[:payload]
|
121
|
+
payload = options[:payload]
|
122
|
+
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
123
|
+
else
|
124
|
+
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
125
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(add_backup_option_types(), options[:options], @api_client, options[:params])
|
126
|
+
params.deep_merge!(v_prompt)
|
127
|
+
advanced_config = Morpheus::Cli::OptionTypes.no_prompt(add_backup_advanced_option_types, options[:options], @api_client, options[:params])
|
128
|
+
advanced_config.deep_compact!
|
129
|
+
params.deep_merge!(advanced_config)
|
130
|
+
payload['backup'].deep_merge!(params)
|
131
|
+
end
|
132
|
+
@backups_interface.setopts(options)
|
133
|
+
if options[:dry_run]
|
134
|
+
print_dry_run @backups_interface.dry.create(payload)
|
135
|
+
return 0, nil
|
136
|
+
end
|
137
|
+
json_response = @backups_interface.create(payload)
|
138
|
+
backup = json_response['backup']
|
139
|
+
render_response(json_response, options, 'backup') do
|
140
|
+
print_green_success "Added backup #{backup['name']}"
|
141
|
+
return _get(backup["id"], {}, options)
|
142
|
+
end
|
143
|
+
return 0, nil
|
144
|
+
end
|
145
|
+
|
146
|
+
def update(args)
|
147
|
+
options = {}
|
148
|
+
params = {}
|
149
|
+
payload = {}
|
150
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
151
|
+
opts.banner = subcommand_usage("[backup] [options]")
|
152
|
+
build_option_type_options(opts, options, update_backup_option_types)
|
153
|
+
build_option_type_options(opts, options, update_backup_advanced_option_types)
|
154
|
+
build_standard_update_options(opts, options)
|
155
|
+
opts.footer = <<-EOT
|
156
|
+
Update a backup.
|
157
|
+
[backup] is required. This is the name or id of a backup.
|
158
|
+
EOT
|
159
|
+
end
|
160
|
+
optparse.parse!(args)
|
161
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
162
|
+
connect(options)
|
163
|
+
backup = find_backup_by_name_or_id(args[0])
|
164
|
+
return 1 if backup.nil?
|
165
|
+
payload = {}
|
166
|
+
if options[:payload]
|
167
|
+
payload = options[:payload]
|
168
|
+
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
169
|
+
else
|
170
|
+
payload.deep_merge!({'backup' => parse_passed_options(options)})
|
171
|
+
# do not prompt on update
|
172
|
+
v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_backup_option_types, options[:options], @api_client, options[:params])
|
173
|
+
v_prompt.deep_compact!
|
174
|
+
params.deep_merge!(v_prompt)
|
175
|
+
advanced_config = Morpheus::Cli::OptionTypes.no_prompt(update_backup_advanced_option_types, options[:options], @api_client, options[:params])
|
176
|
+
advanced_config.deep_compact!
|
177
|
+
params.deep_merge!(advanced_config)
|
178
|
+
payload.deep_merge!({'backup' => params})
|
179
|
+
if payload['backup'].empty? # || options[:no_prompt]
|
180
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
@backups_interface.setopts(options)
|
184
|
+
if options[:dry_run]
|
185
|
+
print_dry_run @backups_interface.dry.update(backup['id'], payload)
|
186
|
+
return
|
187
|
+
end
|
188
|
+
json_response = @backups_interface.update(backup['id'], payload)
|
189
|
+
backup = json_response['backup']
|
190
|
+
render_response(json_response, options, 'backup') do
|
191
|
+
print_green_success "Updated backup #{backup['name']}"
|
192
|
+
return _get(backup["id"], {}, options)
|
193
|
+
end
|
194
|
+
return 0, nil
|
195
|
+
end
|
196
|
+
|
197
|
+
def remove(args)
|
198
|
+
options = {}
|
199
|
+
params = {}
|
200
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
201
|
+
opts.banner = subcommand_usage("[backup] [options]")
|
202
|
+
build_standard_remove_options(opts, options)
|
203
|
+
opts.footer = <<-EOT
|
204
|
+
Delete a backup.
|
205
|
+
[backup] is required. This is the name or id of a backup.
|
206
|
+
EOT
|
207
|
+
end
|
208
|
+
optparse.parse!(args)
|
209
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
210
|
+
connect(options)
|
211
|
+
backup = find_backup_by_name_or_id(args[0])
|
212
|
+
return 1 if backup.nil?
|
213
|
+
@backups_interface.setopts(options)
|
214
|
+
if options[:dry_run]
|
215
|
+
print_dry_run @backups_interface.dry.destroy(backup['id'], params)
|
216
|
+
return
|
217
|
+
end
|
218
|
+
json_response = @backups_interface.destroy(backup['id'], params)
|
219
|
+
render_response(json_response, options) do
|
220
|
+
print_green_success "Removed backup #{backup['name']}"
|
221
|
+
end
|
222
|
+
return 0, nil
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def backup_column_definitions()
|
228
|
+
{
|
229
|
+
"ID" => 'id',
|
230
|
+
"Name" => 'name',
|
231
|
+
"Schedule" => lambda {|it| it['schedule']['name'] rescue '' },
|
232
|
+
"Backup Job" => lambda {|it| it['job']['name'] rescue '' },
|
233
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
234
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
# this is not so simple, need to first choose select instance, host or provider
|
239
|
+
def add_backup_option_types
|
240
|
+
[
|
241
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
242
|
+
{'fieldName' => 'jobId', 'fieldLabel' => 'Backup Job', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
243
|
+
# @options_interface.options_for_source("licenseTypes", {})['data']
|
244
|
+
@backup_jobs_interface.list({max:10000})['backupJobs'].collect {|backup_job|
|
245
|
+
{'name' => backup_job['name'], 'value' => backup_job['id'], 'id' => backup_job['id']}
|
246
|
+
}
|
247
|
+
}, 'required' => true, 'displayOrder' => 3},
|
248
|
+
]
|
249
|
+
end
|
250
|
+
|
251
|
+
def add_backup_advanced_option_types
|
252
|
+
[]
|
253
|
+
end
|
254
|
+
|
255
|
+
def update_backup_option_types
|
256
|
+
add_backup_option_types.collect {|it|
|
257
|
+
it.delete('required')
|
258
|
+
it.delete('defaultValue')
|
259
|
+
it
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
def update_backup_advanced_option_types
|
264
|
+
add_backup_advanced_option_types.collect {|it|
|
265
|
+
it.delete('required')
|
266
|
+
it.delete('defaultValue')
|
267
|
+
it
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
@@ -279,7 +279,7 @@ class Morpheus::Cli::BootScriptsCommand
|
|
279
279
|
options = {}
|
280
280
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
281
281
|
opts.banner = subcommand_usage("[boot-script]")
|
282
|
-
build_common_options(opts, options, [:
|
282
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
283
283
|
end
|
284
284
|
optparse.parse!(args)
|
285
285
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'json'
|
3
|
+
require 'fileutils'
|
3
4
|
require 'morpheus/logging'
|
4
5
|
require 'morpheus/benchmarking'
|
5
6
|
require 'morpheus/cli/option_parser'
|
@@ -169,14 +170,18 @@ module Morpheus
|
|
169
170
|
full_field_name = "#{field_namespace.join('.')}.#{field_name}"
|
170
171
|
end
|
171
172
|
|
172
|
-
description = "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}
|
173
|
+
description = "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}"
|
173
174
|
if option_type['description']
|
174
175
|
# description << "\n #{option_type['description']}"
|
175
176
|
description << " - #{option_type['description']}"
|
176
177
|
end
|
178
|
+
if option_type['defaultValue']
|
179
|
+
description << ". Default: #{option_type['defaultValue']}"
|
180
|
+
end
|
177
181
|
if option_type['helpBlock']
|
178
182
|
description << "\n #{option_type['helpBlock']}"
|
179
183
|
end
|
184
|
+
|
180
185
|
# description = option_type['description'].to_s
|
181
186
|
# if option_type['defaultValue']
|
182
187
|
# description = "#{description} Default: #{option_type['defaultValue']}"
|
@@ -228,11 +233,11 @@ module Morpheus
|
|
228
233
|
## the standard options for a command that makes api requests (most of them)
|
229
234
|
|
230
235
|
def build_standard_get_options(opts, options, includes=[], excludes=[])
|
231
|
-
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :quiet, :dry_run, :remote]
|
236
|
+
build_common_options(opts, options, includes + [:query, :json, :yaml, :csv, :fields, :quiet, :dry_run, :remote], excludes)
|
232
237
|
end
|
233
238
|
|
234
239
|
def build_standard_post_options(opts, options, includes=[], excludes=[])
|
235
|
-
build_common_options(opts, options, [:options, :payload, :json, :quiet, :dry_run, :remote]
|
240
|
+
build_common_options(opts, options, includes + [:options, :payload, :json, :quiet, :dry_run, :remote], excludes)
|
236
241
|
end
|
237
242
|
|
238
243
|
def build_standard_put_options(opts, options, includes=[], excludes=[])
|
@@ -240,7 +245,7 @@ module Morpheus
|
|
240
245
|
end
|
241
246
|
|
242
247
|
def build_standard_delete_options(opts, options, includes=[], excludes=[])
|
243
|
-
build_common_options(opts, options, [:auto_confirm, :query, :json, :quiet, :dry_run, :remote]
|
248
|
+
build_common_options(opts, options, includes + [:auto_confirm, :query, :json, :quiet, :dry_run, :remote], excludes)
|
244
249
|
end
|
245
250
|
|
246
251
|
# list is GET that supports phrase,max,offset,sort,direction
|
@@ -281,13 +286,24 @@ module Morpheus
|
|
281
286
|
while (option_key = option_keys.shift) do
|
282
287
|
case option_key.to_sym
|
283
288
|
|
284
|
-
when :account
|
285
|
-
|
289
|
+
when :tenant, :account
|
290
|
+
# todo: let's deprecate this in favor of :tenant --tenant to keep -a reserved for --all perhaps?
|
291
|
+
opts.on('--tenant TENANT', String, "Tenant (Account) Name or ID") do |val|
|
292
|
+
options[:account] = val
|
293
|
+
end
|
294
|
+
opts.on('--tenant-id ID', String, "Tenant (Account) ID") do |val|
|
295
|
+
options[:account_id] = val
|
296
|
+
end
|
297
|
+
# todo: let's deprecate this in favor of :tenant --tenant to keep -a reserved for --all perhaps?
|
298
|
+
opts.on('-a','--account ACCOUNT', "Alias for --tenant") do |val|
|
286
299
|
options[:account] = val
|
287
300
|
end
|
288
|
-
opts.on('-A','--account-id ID', "Account ID") do |val|
|
301
|
+
opts.on('-A','--account-id ID', "Tenant (Account) ID") do |val|
|
289
302
|
options[:account_id] = val
|
290
303
|
end
|
304
|
+
opts.add_hidden_option('--tenant-id') if opts.is_a?(Morpheus::Cli::OptionParser)
|
305
|
+
opts.add_hidden_option('-a, --account') if opts.is_a?(Morpheus::Cli::OptionParser)
|
306
|
+
opts.add_hidden_option('-A, --account-id') if opts.is_a?(Morpheus::Cli::OptionParser)
|
291
307
|
|
292
308
|
when :options
|
293
309
|
options[:options] ||= {}
|
@@ -455,7 +471,7 @@ module Morpheus
|
|
455
471
|
end
|
456
472
|
|
457
473
|
# arbitrary query parameters in the format -Q "category=web&phrase=nginx"
|
458
|
-
# opts.on( '-Q', '--query PARAMS', "Query parameters. PARAMS format is '
|
474
|
+
# opts.on( '-Q', '--query PARAMS', "Query parameters. PARAMS format is 'foo=bar&category=web'" ) do |val|
|
459
475
|
# options[:query_filters_raw] = val
|
460
476
|
# options[:query_filters] = {}
|
461
477
|
# # todo: smarter parsing
|
@@ -473,7 +489,7 @@ module Morpheus
|
|
473
489
|
|
474
490
|
when :query, :query_filters
|
475
491
|
# arbitrary query parameters in the format -Q "category=web&phrase=nginx"
|
476
|
-
opts.on( '-Q', '--query PARAMS', "Query parameters. PARAMS format is '
|
492
|
+
opts.on( '-Q', '--query PARAMS', "Query parameters. PARAMS format is 'foo=bar&category=web'" ) do |val|
|
477
493
|
options[:query_filters_raw] = val
|
478
494
|
options[:query_filters] = {}
|
479
495
|
# todo: smarter parsing
|
@@ -599,9 +615,17 @@ module Morpheus
|
|
599
615
|
opts.add_hidden_option('json-raw') if opts.is_a?(Morpheus::Cli::OptionParser)
|
600
616
|
|
601
617
|
when :yaml
|
602
|
-
|
603
|
-
|
604
|
-
|
618
|
+
# -y for --yes and for --yaml
|
619
|
+
if includes.include?(:auto_confirm)
|
620
|
+
opts.on(nil, '--yaml', "YAML Output") do
|
621
|
+
options[:yaml] = true
|
622
|
+
options[:format] = :yaml
|
623
|
+
end
|
624
|
+
else
|
625
|
+
opts.on('-y', '--yaml', "YAML Output") do
|
626
|
+
options[:yaml] = true
|
627
|
+
options[:format] = :yaml
|
628
|
+
end
|
605
629
|
end
|
606
630
|
opts.on(nil, '--yml', "alias for --yaml") do
|
607
631
|
options[:yaml] = true
|
@@ -779,7 +803,11 @@ module Morpheus
|
|
779
803
|
end
|
780
804
|
opts.add_hidden_option('--no-debug') if opts.is_a?(Morpheus::Cli::OptionParser)
|
781
805
|
|
782
|
-
|
806
|
+
opts.on('--hidden-help', "Print help that includes all the hidden options, like this one." ) do
|
807
|
+
puts opts.full_help_message({show_hidden_options:true})
|
808
|
+
exit # return 0 maybe?
|
809
|
+
end
|
810
|
+
opts.add_hidden_option('--hidden-help') if opts.is_a?(Morpheus::Cli::OptionParser)
|
783
811
|
opts.on('-h', '--help', "Print this help" ) do
|
784
812
|
puts opts
|
785
813
|
exit # return 0 maybe?
|
@@ -804,6 +832,20 @@ module Morpheus
|
|
804
832
|
self.class.subcommand_aliases
|
805
833
|
end
|
806
834
|
|
835
|
+
# def subcommand_descriptions
|
836
|
+
# self.class.subcommand_descriptions
|
837
|
+
# end
|
838
|
+
|
839
|
+
def get_subcommand_description(subcmd)
|
840
|
+
self.class.get_subcommand_description(subcmd)
|
841
|
+
end
|
842
|
+
|
843
|
+
def subcommand_description()
|
844
|
+
calling_method = caller[0][/`([^']*)'/, 1].to_s.sub('block in ', '')
|
845
|
+
subcommand_name = subcommands.key(calling_method)
|
846
|
+
subcommand_name ? get_subcommand_description(subcommand_name) : nil
|
847
|
+
end
|
848
|
+
|
807
849
|
def default_subcommand
|
808
850
|
self.class.default_subcommand
|
809
851
|
end
|
@@ -845,8 +887,11 @@ module Morpheus
|
|
845
887
|
if !subcommands.empty?
|
846
888
|
out << "Commands:"
|
847
889
|
out << "\n"
|
848
|
-
subcommands.sort.each {|
|
849
|
-
|
890
|
+
subcommands.sort.each {|subcmd, method|
|
891
|
+
desc = get_subcommand_description(subcmd)
|
892
|
+
out << "\t#{subcmd.to_s}"
|
893
|
+
out << "\t#{desc}" if desc
|
894
|
+
out << "\n"
|
850
895
|
}
|
851
896
|
end
|
852
897
|
# out << "\n"
|
@@ -990,7 +1035,7 @@ module Morpheus
|
|
990
1035
|
# raise_command_error "Please specify a remote appliance with -r or see the command `remote use`"
|
991
1036
|
# end
|
992
1037
|
|
993
|
-
Morpheus::Logging::DarkPrinter.puts "establishing connection to remote #{display_appliance(@appliance_name, @appliance_url)}" if Morpheus::Logging.debug?
|
1038
|
+
Morpheus::Logging::DarkPrinter.puts "establishing connection to remote #{display_appliance(@appliance_name, @appliance_url)}" if Morpheus::Logging.debug? # && !options[:quiet]
|
994
1039
|
|
995
1040
|
if options[:no_authorization]
|
996
1041
|
# maybe handle this here..
|
@@ -1061,7 +1106,7 @@ module Morpheus
|
|
1061
1106
|
else
|
1062
1107
|
if opts[:min]
|
1063
1108
|
if args.count < opts[:min]
|
1064
|
-
raise_args_error("not
|
1109
|
+
raise_args_error("not enough arguments, expected #{opts[:min] || '0'}-#{opts[:max] || 'N'} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
|
1065
1110
|
end
|
1066
1111
|
end
|
1067
1112
|
if opts[:max]
|
@@ -1152,22 +1197,32 @@ module Morpheus
|
|
1152
1197
|
payload
|
1153
1198
|
end
|
1154
1199
|
|
1155
|
-
def
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1200
|
+
def validate_outfile(outfile, options)
|
1201
|
+
full_filename = File.expand_path(outfile)
|
1202
|
+
outdir = File.dirname(full_filename)
|
1203
|
+
if Dir.exists?(full_filename)
|
1204
|
+
print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
|
1205
|
+
return false
|
1206
|
+
end
|
1207
|
+
if !Dir.exists?(outdir)
|
1208
|
+
if options[:mkdir]
|
1209
|
+
print cyan,"Creating local directory #{outdir}",reset,"\n"
|
1210
|
+
FileUtils.mkdir_p(outdir)
|
1162
1211
|
else
|
1163
|
-
|
1212
|
+
print_red_alert "[local-file] is invalid. Directory not found: #{outdir}"
|
1213
|
+
return false
|
1164
1214
|
end
|
1165
1215
|
end
|
1216
|
+
if File.exists?(full_filename) && !options[:overwrite]
|
1217
|
+
print_red_alert "[local-file] is invalid. File already exists: #{outfile}\nUse -f to overwrite the existing file."
|
1218
|
+
return false
|
1219
|
+
end
|
1220
|
+
return true
|
1166
1221
|
end
|
1167
1222
|
|
1168
1223
|
# basic rendering for options :json, :yml, :csv, :quiet, and :outfile
|
1169
1224
|
# returns the string rendered, or nil if nothing was rendered.
|
1170
|
-
def
|
1225
|
+
def render_response(json_response, options, object_key=nil, &block)
|
1171
1226
|
output = nil
|
1172
1227
|
if options[:json]
|
1173
1228
|
output = as_json(json_response, options, object_key)
|
@@ -1180,32 +1235,49 @@ module Morpheus
|
|
1180
1235
|
else
|
1181
1236
|
output = records_as_csv([row], options)
|
1182
1237
|
end
|
1183
|
-
elsif options[:quiet]
|
1184
|
-
# note: returning non nil means the calling function knows to return rght away.. kinda weird..
|
1185
|
-
# but means we need less if options[:quiet] blocks in every action.
|
1186
|
-
return ""
|
1187
1238
|
end
|
1188
|
-
if
|
1189
|
-
if
|
1239
|
+
if options[:outfile]
|
1240
|
+
if output
|
1190
1241
|
print_to_file(output, options[:outfile], options[:overwrite])
|
1242
|
+
print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(options[:outfile])} B)\n" unless options[:quiet]
|
1191
1243
|
else
|
1192
|
-
|
1244
|
+
# uhhh ok lets try this
|
1245
|
+
Morpheus::Logging::DarkPrinter.puts "using experimental feature: --outfile without a common format like json, yml or csv" if Morpheus::Logging.debug?
|
1246
|
+
result = with_stdout_to_file(options[:outfile], options[:overwrite], 'w+', &block)
|
1247
|
+
print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(options[:outfile])} B)\n" unless options[:quiet]
|
1248
|
+
if result
|
1249
|
+
return result
|
1250
|
+
end
|
1251
|
+
return 0, nil
|
1193
1252
|
end
|
1194
1253
|
else
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1254
|
+
# --quiet means do not render, still want to print to outfile though
|
1255
|
+
if options[:quiet]
|
1256
|
+
return 0, nil
|
1257
|
+
end
|
1258
|
+
# render ouput generated above
|
1259
|
+
if output
|
1260
|
+
puts output
|
1261
|
+
return 0, nil
|
1262
|
+
else
|
1263
|
+
# no render happened, so calling the block if given
|
1264
|
+
if block_given?
|
1265
|
+
result = yield
|
1266
|
+
if result
|
1267
|
+
return result
|
1268
|
+
else
|
1269
|
+
return 0, nil
|
1270
|
+
end
|
1271
|
+
else
|
1272
|
+
# nil means nothing was rendered, some methods still using render_with_format() are relying on this
|
1273
|
+
return nil
|
1274
|
+
end
|
1275
|
+
end
|
1205
1276
|
end
|
1206
|
-
return output
|
1207
1277
|
end
|
1208
1278
|
|
1279
|
+
alias :render_with_format :render_response
|
1280
|
+
|
1209
1281
|
module ClassMethods
|
1210
1282
|
|
1211
1283
|
def set_command_name(cmd_name)
|
@@ -1277,12 +1349,46 @@ module Morpheus
|
|
1277
1349
|
v = cmd.to_s.gsub('-', '_')
|
1278
1350
|
register_subcommands({(k) => v})
|
1279
1351
|
else
|
1280
|
-
raise "Unable to register command of type: #{cmd.class} #{cmd}"
|
1352
|
+
raise Morpheus::Cli::CliRegistry::BadCommandDefinition.new("Unable to register command of type: #{cmd.class} #{cmd}")
|
1281
1353
|
end
|
1282
1354
|
}
|
1283
1355
|
return
|
1284
1356
|
end
|
1285
1357
|
|
1358
|
+
# this might be the new hotness
|
1359
|
+
# register_subcommand(:show) # do not do this, always define a description!
|
1360
|
+
# register_subcommand(:list, "List things")
|
1361
|
+
# register_subcommand("update-all", "update_all", "Update all things")
|
1362
|
+
# If the command name =~ method, no need to pass both
|
1363
|
+
# command names will have "-" swapped in for "_" and vice versa for method names.
|
1364
|
+
def register_subcommand(*args)
|
1365
|
+
args = args.flatten
|
1366
|
+
cmd_name = args[0]
|
1367
|
+
cmd_method = nil
|
1368
|
+
cmd_desc = nil
|
1369
|
+
if args.count == 1
|
1370
|
+
cmd_method = cmd_name
|
1371
|
+
elsif args.count == 2
|
1372
|
+
if args[1].is_a?(Symbol)
|
1373
|
+
cmd_method = args[1]
|
1374
|
+
else
|
1375
|
+
cmd_method = cmd_name
|
1376
|
+
cmd_desc = args[1]
|
1377
|
+
end
|
1378
|
+
elsif args.count == 3
|
1379
|
+
cmd_method = args[1]
|
1380
|
+
cmd_desc = args[2]
|
1381
|
+
else
|
1382
|
+
raise Morpheus::Cli::CliRegistry::BadCommandDefinition.new("register_subcommand expects 1-3 arguments, got #{args.size} #{args.inspect}")
|
1383
|
+
end
|
1384
|
+
cmd_name = cmd_name.to_s.gsub("_", "-").to_sym
|
1385
|
+
cmd_method = (cmd_method || cmd_name).to_s.gsub("-", "_").to_sym
|
1386
|
+
cmd_definition = {(cmd_name) => cmd_method}
|
1387
|
+
register_subcommands(cmd_definition)
|
1388
|
+
add_subcommand_description(cmd_name, cmd_desc)
|
1389
|
+
return
|
1390
|
+
end
|
1391
|
+
|
1286
1392
|
def set_default_subcommand(cmd)
|
1287
1393
|
@default_subcommand = cmd
|
1288
1394
|
end
|
@@ -1341,6 +1447,31 @@ module Morpheus
|
|
1341
1447
|
@subcommand_aliases.delete(alias_cmd_name.to_s)
|
1342
1448
|
end
|
1343
1449
|
|
1450
|
+
def subcommand_descriptions
|
1451
|
+
@subcommand_descriptions ||= {}
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
def add_subcommand_description(cmd_name, description)
|
1455
|
+
@subcommand_descriptions ||= {}
|
1456
|
+
@subcommand_descriptions[cmd_name.to_s.gsub('_', '-')] = description
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
def get_subcommand_description(cmd_name)
|
1460
|
+
desc = subcommand_descriptions[cmd_name.to_s.gsub('_', '-')]
|
1461
|
+
if desc
|
1462
|
+
return desc
|
1463
|
+
else
|
1464
|
+
cmd_method = subcommands.key(cmd_name)
|
1465
|
+
return cmd_method ? subcommand_descriptions[cmd_method.to_s.gsub('_', '-')] : nil
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
|
1469
|
+
def set_subcommand_descriptions(cmd_map)
|
1470
|
+
cmd_map.each do |cmd_name, description|
|
1471
|
+
add_subcommand_description(cmd_name, description)
|
1472
|
+
end
|
1473
|
+
end
|
1474
|
+
|
1344
1475
|
end
|
1345
1476
|
end
|
1346
1477
|
end
|