morpheus-cli 5.5.0 → 5.5.1
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/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +12 -0
- data/lib/morpheus/api/network_pool_servers_interface.rb +7 -0
- data/lib/morpheus/api/scale_thresholds_interface.rb +9 -0
- data/lib/morpheus/cli/cli_command.rb +39 -20
- data/lib/morpheus/cli/commands/apps.rb +1 -1
- data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
- data/lib/morpheus/cli/commands/clouds.rb +12 -6
- data/lib/morpheus/cli/commands/clusters.rb +66 -5
- data/lib/morpheus/cli/commands/hosts.rb +5 -1
- data/lib/morpheus/cli/commands/instances.rb +1 -1
- data/lib/morpheus/cli/commands/integrations_command.rb +1 -1
- data/lib/morpheus/cli/commands/invoices_command.rb +8 -1
- data/lib/morpheus/cli/commands/jobs_command.rb +45 -225
- data/lib/morpheus/cli/commands/library_container_types_command.rb +52 -3
- data/lib/morpheus/cli/commands/library_option_types_command.rb +56 -62
- data/lib/morpheus/cli/commands/load_balancers.rb +11 -19
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +5 -2
- data/lib/morpheus/cli/commands/roles.rb +475 -70
- data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
- data/lib/morpheus/cli/commands/tasks.rb +19 -12
- data/lib/morpheus/cli/commands/user_sources_command.rb +107 -39
- data/lib/morpheus/cli/commands/users.rb +10 -10
- data/lib/morpheus/cli/commands/view.rb +1 -0
- data/lib/morpheus/cli/commands/workflows.rb +21 -14
- data/lib/morpheus/cli/error_handler.rb +13 -4
- data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/execution_request_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +3 -3
- data/lib/morpheus/cli/mixins/jobs_helper.rb +173 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +120 -38
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -3
- data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
- data/lib/morpheus/cli/option_types.rb +68 -37
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/logging.rb +6 -8
- metadata +6 -4
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'morpheus/cli/mixins/print_helper'
|
2
|
+
# Mixin for Morpheus::Cli command classes
|
3
|
+
# Provides refreshing job execution records by id
|
4
|
+
module Morpheus::Cli::JobsHelper
|
5
|
+
|
6
|
+
def self.included(klass)
|
7
|
+
klass.send :include, Morpheus::Cli::PrintHelper
|
8
|
+
# klass.send :include, Morpheus::Cli::ProcessesHelper
|
9
|
+
end
|
10
|
+
|
11
|
+
def api_client
|
12
|
+
raise "#{self.class} has not defined @api_client" if @api_client.nil?
|
13
|
+
@api_client
|
14
|
+
end
|
15
|
+
|
16
|
+
def jobs_interface
|
17
|
+
# get_interface('jobs')
|
18
|
+
api_client.jobs
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_process_event_data(process_or_event)
|
22
|
+
{
|
23
|
+
id: process_or_event['id'],
|
24
|
+
description: process_or_event['description'] || (process_or_event['refType'] == 'instance' ? process_or_event['displayName'] : (process_or_event['processTypeName'] || '').capitalize),
|
25
|
+
start_date: format_local_dt(process_or_event['startDate']),
|
26
|
+
created_by: process_or_event['createdBy'] ? process_or_event['createdBy']['displayName'] : '',
|
27
|
+
duration: format_human_duration((process_or_event['duration'] || process_or_event['statusEta'] || 0) / 1000.0),
|
28
|
+
status: format_job_status(process_or_event['status']),
|
29
|
+
error: process_or_event['message'] || process_or_event['error'],
|
30
|
+
output: process_or_event['output'],
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# both process and process events
|
35
|
+
def print_process_events(events, options={})
|
36
|
+
# event_columns = [:id, :description, :start_date, :created_by, :duration, :status, :error, :output]
|
37
|
+
event_columns = {
|
38
|
+
"ID" => lambda {|it| it[:id]},
|
39
|
+
"Description" => lambda {|it| it[:description]},
|
40
|
+
"Start Date" => lambda {|it| it[:start_date]},
|
41
|
+
"Created By" => lambda {|it| it[:created_by]},
|
42
|
+
"Duration" => lambda {|it| it[:duration]},
|
43
|
+
"Status" => lambda {|it| it[:status]},
|
44
|
+
"Error" => lambda {|it| options[:details] ? it[:error] : truncate_string(it[:error], 32) },
|
45
|
+
"Output" => lambda {|it| options[:details] ? it[:output] : truncate_string(it[:output], 32) }
|
46
|
+
}
|
47
|
+
print as_pretty_table(events.collect {|it| get_process_event_data(it)}, event_columns.upcase_keys!, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def print_job_execution(job_execution, options)
|
51
|
+
process = job_execution['process']
|
52
|
+
print cyan
|
53
|
+
description_cols = {
|
54
|
+
"ID" => lambda {|it| it['id'] },
|
55
|
+
"Job" => lambda {|it| it['job'] ? it['job']['name'] : ''},
|
56
|
+
"Job Type" => lambda {|it| it['job'] && it['job']['type'] ? (it['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : ''},
|
57
|
+
# "Description" => lambda {|it| it['description'] || (it['job'] ? it['job']['description'] : '') },
|
58
|
+
"Start Date" => lambda {|it| format_local_dt(it['startDate'])},
|
59
|
+
"ETA/Time" => lambda {|it| it['duration'] ? format_human_duration(it['duration'] / 1000.0) : ''},
|
60
|
+
"Status" => lambda {|it| format_job_status(it['status'])},
|
61
|
+
#"Output" => lambda {|it| it['process'] && (it['process']['output']) ?(it['process']['output']).to_s.strip : ''},
|
62
|
+
#"Error" => lambda {|it| it['process'] && (it['process']['message'] || it['process']['error']) ? red + (it['process']['message'] || it['process']['error']).to_s.strip + cyan : ''},
|
63
|
+
"Created By" => lambda {|it| it['createdBy'].nil? ? '' : it['createdBy']['displayName'] || it['createdBy']['username']}
|
64
|
+
}
|
65
|
+
print_description_list(description_cols, job_execution, options)
|
66
|
+
|
67
|
+
if process
|
68
|
+
process_data = get_process_event_data(process)
|
69
|
+
print_h2 "Process Details"
|
70
|
+
process_description_cols = {
|
71
|
+
"Process ID" => lambda {|it| it[:id]},
|
72
|
+
"Description" => lambda {|it| it[:description]},
|
73
|
+
"Start Date" => lambda {|it| it[:start_date]},
|
74
|
+
"Created By" => lambda {|it| it[:created_by]},
|
75
|
+
"Duration" => lambda {|it| it[:duration]},
|
76
|
+
"Status" => lambda {|it| it[:status]}
|
77
|
+
}
|
78
|
+
print_description_list(process_description_cols, process_data, options)
|
79
|
+
|
80
|
+
if process_data[:output] && process_data[:output].strip.length > 0
|
81
|
+
print_h2 "Output"
|
82
|
+
print process['output']
|
83
|
+
end
|
84
|
+
if process_data[:error] && process_data[:error].strip.length > 0
|
85
|
+
print_h2 "Error"
|
86
|
+
print process['message'] || process['error']
|
87
|
+
print reset,"\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
if process['events'] && !process['events'].empty?
|
92
|
+
print_h2 "Process Events", options
|
93
|
+
print_process_events(process['events'], options)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
print reset,"\n"
|
97
|
+
end
|
98
|
+
return 0, nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_job_executions(execs, options={})
|
102
|
+
if execs.empty?
|
103
|
+
print cyan,"No job executions found.",reset,"\n"
|
104
|
+
else
|
105
|
+
rows = execs.collect do |ex|
|
106
|
+
{
|
107
|
+
id: ex['id'],
|
108
|
+
job: ex['job'] ? ex['job']['name'] : '',
|
109
|
+
description: ex['description'] || ex['job'] ? ex['job']['description'] : '',
|
110
|
+
type: ex['job'] && ex['job']['type'] ? (ex['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : '',
|
111
|
+
start: format_local_dt(ex['startDate']),
|
112
|
+
duration: ex['duration'] ? format_human_duration(ex['duration'] / 1000.0) : '',
|
113
|
+
status: format_job_status(ex['status']),
|
114
|
+
error: truncate_string(ex['process'] && (ex['process']['message'] || ex['process']['error']) ? ex['process']['message'] || ex['process']['error'] : '', options[:details] ? nil : 32),
|
115
|
+
output: truncate_string(ex['process'] && ex['process']['output'] ? ex['process']['output'] : '', options[:details] ? nil : 32),
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
columns = [
|
120
|
+
:id, :job, :type, {'START DATE' => :start}, {'ETA/TIME' => :duration}, :status, :error, :output
|
121
|
+
]
|
122
|
+
print as_pretty_table(rows, columns, options)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def format_job_status(status_string, return_color=cyan)
|
127
|
+
out = ""
|
128
|
+
if status_string
|
129
|
+
if ['complete','success', 'successful', 'ok'].include?(status_string)
|
130
|
+
out << "#{green}#{status_string.upcase}"
|
131
|
+
elsif ['error', 'offline', 'failed', 'failure'].include?(status_string)
|
132
|
+
out << "#{red}#{status_string.upcase}"
|
133
|
+
else
|
134
|
+
out << "#{yellow}#{status_string.upcase}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
out + return_color
|
138
|
+
end
|
139
|
+
|
140
|
+
# this is built into CliCommand now...
|
141
|
+
# def find_by_name_or_id(type, val)
|
142
|
+
# interface = instance_variable_get "@#{type}s_interface"
|
143
|
+
# typeCamelCase = type.gsub(/(?:^|_)([a-z])/) do $1.upcase end
|
144
|
+
# typeCamelCase = typeCamelCase[0, 1].downcase + typeCamelCase[1..-1]
|
145
|
+
# (val.to_s =~ /\A\d{1,}\Z/) ? interface.get(val.to_i)[typeCamelCase] : interface.list({'name' => val})["#{typeCamelCase}s"].first
|
146
|
+
# end
|
147
|
+
|
148
|
+
# refresh execution request until it is finished
|
149
|
+
# returns json response data of the last execution request when status reached 'completed' or 'failed'
|
150
|
+
def wait_for_job_execution(job_execution_id, options={}, print_output = true)
|
151
|
+
refresh_interval = 10
|
152
|
+
if options[:refresh_interval].to_i > 0
|
153
|
+
refresh_interval = options[:refresh_interval]
|
154
|
+
end
|
155
|
+
refresh_display_seconds = refresh_interval % 1.0 == 0 ? refresh_interval.to_i : refresh_interval
|
156
|
+
unless options[:quiet]
|
157
|
+
print cyan, "Refreshing every #{refresh_display_seconds} seconds until execution is complete...", "\n", reset
|
158
|
+
end
|
159
|
+
job_execution = jobs_interface.get_execution(job_execution_id)['jobExecution']
|
160
|
+
while ['new','queued','pending','running'].include?(job_execution['status']) do
|
161
|
+
sleep(refresh_interval)
|
162
|
+
job_execution = jobs_interface.get_execution(job_execution_id)['jobExecution']
|
163
|
+
end
|
164
|
+
if print_output && options[:quiet] != true
|
165
|
+
print_h1 "Morpheus Job Execution", [], options
|
166
|
+
print_job_execution(job_execution, options)
|
167
|
+
end
|
168
|
+
return job_execution
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
end
|
@@ -608,6 +608,9 @@ module Morpheus::Cli::PrintHelper
|
|
608
608
|
# @param suffix [String] the character to pad right side with. Default is '...'
|
609
609
|
def truncate_string(value, width, suffix="...")
|
610
610
|
value = value.to_s
|
611
|
+
if !width
|
612
|
+
return value
|
613
|
+
end
|
611
614
|
# JD: hack alerty.. this sux, but it's a best effort to preserve values containing ascii coloring codes
|
612
615
|
# it stops working when there are words separated by ascii codes, eg. two diff colors
|
613
616
|
# plus this is probably pretty slow...
|
@@ -714,7 +717,11 @@ module Morpheus::Cli::PrintHelper
|
|
714
717
|
|
715
718
|
# support --fields x,y,z and --all-fields or --fields all
|
716
719
|
all_fields = data.first ? data.first.keys : []
|
717
|
-
|
720
|
+
#todo: support --raw-fields meh, not really needed..
|
721
|
+
# if options[:include_fields] && options[:raw_fields]
|
722
|
+
# data = transform_data_for_field_options(data, options)
|
723
|
+
# data = data[options[:include_fields_context]] if options[:include_fields_context]
|
724
|
+
# elsif options[:include_fields]
|
718
725
|
if options[:include_fields]
|
719
726
|
if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
|
720
727
|
columns = all_fields
|
@@ -1093,16 +1100,78 @@ module Morpheus::Cli::PrintHelper
|
|
1093
1100
|
'"' + v.to_s.gsub('"', '""') + '"'
|
1094
1101
|
end
|
1095
1102
|
|
1096
|
-
def as_csv(data,
|
1103
|
+
def as_csv(data, default_columns=nil, options={}, object_key=nil)
|
1097
1104
|
out = ""
|
1098
1105
|
delim = options[:csv_delim] || options[:delim] || ","
|
1099
1106
|
newline = options[:csv_newline] || options[:newline] || "\n"
|
1100
1107
|
include_header = options[:csv_no_header] ? false : true
|
1101
1108
|
do_quotes = options[:csv_quotes] || options[:quotes]
|
1102
1109
|
|
1110
|
+
if options[:include_fields]
|
1111
|
+
data = transform_data_for_field_options(data, options, object_key)
|
1112
|
+
if data.is_a?(Hash)
|
1113
|
+
if options[:raw_fields]
|
1114
|
+
if options[:include_fields_context]
|
1115
|
+
data = data[options[:include_fields_context]]
|
1116
|
+
else
|
1117
|
+
# todo: could use a dynamic object_key, first Array or Hash in the data, can probably always do this...
|
1118
|
+
# if object_key.nil?
|
1119
|
+
# object_key = data.keys.find {|k| data[k].is_a?(Array) || data[k].is_a?(Hash) }
|
1120
|
+
# end
|
1121
|
+
# if object_key && data[object_key]
|
1122
|
+
# data = data[object_key]
|
1123
|
+
# end
|
1124
|
+
end
|
1125
|
+
else
|
1126
|
+
if object_key
|
1127
|
+
data = data[object_key]
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
else
|
1132
|
+
# need array of records, so always select the object/array here
|
1133
|
+
if object_key
|
1134
|
+
if data.is_a?(Hash)
|
1135
|
+
data = data[object_key]
|
1136
|
+
else
|
1137
|
+
Morpheus::Logging::DarkPrinter.puts "as_csv() expects data as an to fetch object key '#{object_key}' from, #{records.class}." if Morpheus::Logging.debug?
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
|
1143
|
+
records = data
|
1144
|
+
|
1145
|
+
# allow records as Array and Hash only..
|
1146
|
+
if records.is_a?(Array)
|
1147
|
+
# records = records
|
1148
|
+
elsif records.is_a?(Hash)
|
1149
|
+
records = [records]
|
1150
|
+
else
|
1151
|
+
#raise "records_as_csv expects records as an Array of objects to render"
|
1152
|
+
Morpheus::Logging::DarkPrinter.puts "as_csv() expects data as an Array of objects to render, got a #{records.class} instead" if Morpheus::Logging.debug?
|
1153
|
+
# return ""
|
1154
|
+
return out
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# build column definitions, by default use all properties for the first record (Hash) in the array
|
1158
|
+
columns = []
|
1159
|
+
all_fields = records.first.is_a?(Hash) ? records.first.keys : []
|
1160
|
+
if options[:include_fields]
|
1161
|
+
if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
|
1162
|
+
columns = all_fields
|
1163
|
+
else
|
1164
|
+
columns = options[:include_fields]
|
1165
|
+
end
|
1166
|
+
elsif options[:all_fields]
|
1167
|
+
columns = all_fields
|
1168
|
+
elsif default_columns
|
1169
|
+
columns = default_columns
|
1170
|
+
else
|
1171
|
+
columns = all_fields
|
1172
|
+
end
|
1173
|
+
|
1103
1174
|
column_defs = build_column_definitions(columns)
|
1104
|
-
#columns = columns.flatten.compact
|
1105
|
-
data_array = [data].flatten.compact
|
1106
1175
|
|
1107
1176
|
if include_header
|
1108
1177
|
headers = column_defs.collect {|column_def| column_def.label }
|
@@ -1113,7 +1182,7 @@ module Morpheus::Cli::PrintHelper
|
|
1113
1182
|
out << newline
|
1114
1183
|
end
|
1115
1184
|
lines = []
|
1116
|
-
|
1185
|
+
records.each do |obj|
|
1117
1186
|
if obj
|
1118
1187
|
cells = []
|
1119
1188
|
column_defs.each do |column_def|
|
@@ -1137,29 +1206,9 @@ module Morpheus::Cli::PrintHelper
|
|
1137
1206
|
|
1138
1207
|
end
|
1139
1208
|
|
1209
|
+
# deprecated, replaced by as_csv(records, columns, options, object_key)
|
1140
1210
|
def records_as_csv(records, options={}, default_columns=nil)
|
1141
|
-
|
1142
|
-
if !records
|
1143
|
-
#raise "records_as_csv expects records as an Array of objects to render"
|
1144
|
-
return out
|
1145
|
-
end
|
1146
|
-
cols = []
|
1147
|
-
all_fields = records.first ? records.first.keys : []
|
1148
|
-
if options[:include_fields]
|
1149
|
-
if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
|
1150
|
-
cols = all_fields
|
1151
|
-
else
|
1152
|
-
cols = options[:include_fields]
|
1153
|
-
end
|
1154
|
-
elsif options[:all_fields]
|
1155
|
-
cols = all_fields
|
1156
|
-
elsif default_columns
|
1157
|
-
cols = default_columns
|
1158
|
-
else
|
1159
|
-
cols = all_fields
|
1160
|
-
end
|
1161
|
-
out << as_csv(records, cols, options)
|
1162
|
-
out
|
1211
|
+
as_csv(records, default_columns, options)
|
1163
1212
|
end
|
1164
1213
|
|
1165
1214
|
def as_json(data, options={}, object_key=nil)
|
@@ -1169,12 +1218,7 @@ module Morpheus::Cli::PrintHelper
|
|
1169
1218
|
end
|
1170
1219
|
|
1171
1220
|
if options[:include_fields]
|
1172
|
-
|
1173
|
-
# data[object_key] = filter_data(data[object_key], options[:include_fields])
|
1174
|
-
data = {(object_key) => filter_data(data[object_key], options[:include_fields]) }
|
1175
|
-
else
|
1176
|
-
data = filter_data(data, options[:include_fields])
|
1177
|
-
end
|
1221
|
+
data = transform_data_for_field_options(data, options, object_key)
|
1178
1222
|
end
|
1179
1223
|
|
1180
1224
|
do_pretty = options.key?(:pretty_json) ? options[:pretty_json] : true
|
@@ -1193,11 +1237,7 @@ module Morpheus::Cli::PrintHelper
|
|
1193
1237
|
return "null" # "No data"
|
1194
1238
|
end
|
1195
1239
|
if options[:include_fields]
|
1196
|
-
|
1197
|
-
data[object_key] = filter_data(data[object_key], options[:include_fields])
|
1198
|
-
else
|
1199
|
-
data = filter_data(data, options[:include_fields])
|
1200
|
-
end
|
1240
|
+
data = transform_data_for_field_options(data, options, object_key)
|
1201
1241
|
end
|
1202
1242
|
begin
|
1203
1243
|
out << data.to_yaml
|
@@ -1209,6 +1249,48 @@ module Morpheus::Cli::PrintHelper
|
|
1209
1249
|
out
|
1210
1250
|
end
|
1211
1251
|
|
1252
|
+
|
1253
|
+
# transform data for options --fields id,authority and --select id,authority
|
1254
|
+
# support traversing records with --raw-fields and the list command. Example: roles list --raw-fields roles.id,roles.authority
|
1255
|
+
def transform_data_for_field_options(data, options, object_key=nil)
|
1256
|
+
if options[:include_fields] && data.is_a?(Hash)
|
1257
|
+
if options[:raw_fields]
|
1258
|
+
row = (object_key && !options[:raw_fields]) ? data[object_key] : data
|
1259
|
+
records = [row].flatten()
|
1260
|
+
# look for an array in the first field only now...
|
1261
|
+
field_parts = options[:include_fields][0].to_s.split(".")
|
1262
|
+
field_context = field_parts[0]
|
1263
|
+
context_data = data[field_context]
|
1264
|
+
if field_parts.size > 1 && context_data.is_a?(Array)
|
1265
|
+
# inject all the root level properties to be selectable too..
|
1266
|
+
context_data = data.delete(field_context)
|
1267
|
+
# records = context_data
|
1268
|
+
records = context_data.collect {|it| it.is_a?(Hash) ? data.merge(it) : data }
|
1269
|
+
# hacky modifying options in place
|
1270
|
+
options[:include_fields_context] = field_context
|
1271
|
+
options[:include_fields] = options[:include_fields].collect {|it| it.sub(field_context+'.', '')}
|
1272
|
+
# data = filter_data(records, options[:include_fields])
|
1273
|
+
# data[field_context] = filter_data(records, options[:include_fields])
|
1274
|
+
data = {(field_context) => filter_data(records, options[:include_fields])}
|
1275
|
+
else
|
1276
|
+
data = filter_data(data, options[:include_fields])
|
1277
|
+
end
|
1278
|
+
else
|
1279
|
+
# By default, fields are relative to the object_key, so you can use -F id instead of requiring -F instance.id
|
1280
|
+
# So ironically it is the 'raw' options (:raw_fields == true) that has to do all this funny stuff to filter intuitively.
|
1281
|
+
if object_key
|
1282
|
+
# this removes everything but the object, makes sense when using --fields
|
1283
|
+
data = {(object_key) => filter_data(data[object_key], options[:include_fields])}
|
1284
|
+
# this preserves other fields eg. meta...
|
1285
|
+
# data[object_key] = filter_data(data[object_key], options[:include_fields])
|
1286
|
+
else
|
1287
|
+
data = filter_data(data, options[:include_fields])
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
end
|
1291
|
+
return data
|
1292
|
+
end
|
1293
|
+
|
1212
1294
|
def sleep_with_dots(sleep_seconds, dots=3, dot_chr=".")
|
1213
1295
|
dot_interval = (sleep_seconds.to_f / dots.to_i)
|
1214
1296
|
dots.to_i.times do |dot_index|
|
@@ -2082,7 +2082,6 @@ module Morpheus::Cli::ProvisioningHelper
|
|
2082
2082
|
if !visibility && !options[:no_prompt]
|
2083
2083
|
visibility = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
|
2084
2084
|
end
|
2085
|
-
|
2086
2085
|
permissions['resourcePool'] = {'visibility' => visibility} if visibility
|
2087
2086
|
end
|
2088
2087
|
|
@@ -2114,8 +2113,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
2114
2113
|
def prompt_permissions_v2(options, excludes = [])
|
2115
2114
|
perms = prompt_permissions(options, excludes)
|
2116
2115
|
rtn = {}
|
2117
|
-
|
2118
|
-
rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil?
|
2116
|
+
rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !excludes.include?('visibility')
|
2119
2117
|
rtn['tenants'] = ((perms['tenantPermissions'] || {})['accounts'] || []).collect {|it| {'id' => it}}
|
2120
2118
|
rtn
|
2121
2119
|
end
|
@@ -148,6 +148,17 @@ module Morpheus::Cli::RestCommand
|
|
148
148
|
|
149
149
|
alias :set_rest_interface_name :rest_interface_name=
|
150
150
|
|
151
|
+
# rest_perms_config enables and configures permissions prompt
|
152
|
+
def rest_perms_config
|
153
|
+
@rest_perms_config || {}
|
154
|
+
end
|
155
|
+
|
156
|
+
def rest_perms_config=(v)
|
157
|
+
@rest_perms_config = v
|
158
|
+
end
|
159
|
+
|
160
|
+
alias :set_rest_perms_config :rest_perms_config=
|
161
|
+
|
151
162
|
# rest_has_type indicates a resource has a type. default is false
|
152
163
|
def rest_has_type
|
153
164
|
@rest_has_type == true
|
@@ -329,6 +340,10 @@ module Morpheus::Cli::RestCommand
|
|
329
340
|
self.class.rest_interface_name
|
330
341
|
end
|
331
342
|
|
343
|
+
def rest_perms_config
|
344
|
+
self.class.rest_perms_config
|
345
|
+
end
|
346
|
+
|
332
347
|
# returns the default rest interface, allows using rest_interface_name = "your"
|
333
348
|
# or override this method to return @your_interface if needed
|
334
349
|
def rest_interface
|
@@ -600,25 +615,25 @@ EOT
|
|
600
615
|
verify_args!(args:args, optparse:optparse, count: 0)
|
601
616
|
end
|
602
617
|
connect(options)
|
603
|
-
# load or prompt for type
|
604
|
-
if rest_has_type && type_option_type.nil?
|
605
|
-
if record_type_id.nil?
|
606
|
-
#raise_command_error "#{rest_type_label} is required.\n#{optparse}"
|
607
|
-
type_list = rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key]
|
608
|
-
type_dropdown_options = respond_to?("#{rest_key}_type_list_to_options", true) ? send("#{rest_key}_type_list_to_options", type_list) : type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} }
|
609
|
-
record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type']
|
610
|
-
end
|
611
|
-
record_type = rest_type_find_by_name_or_id(record_type_id)
|
612
|
-
if record_type.nil?
|
613
|
-
return 1, "#{rest_type_label} not found for '#{record_type_id}"
|
614
|
-
end
|
615
|
-
end
|
616
618
|
passed_options = parse_passed_options(options)
|
617
619
|
payload = {}
|
618
620
|
if options[:payload]
|
619
621
|
payload = options[:payload]
|
620
622
|
payload.deep_merge!({rest_object_key => passed_options})
|
621
623
|
else
|
624
|
+
# load or prompt for type
|
625
|
+
if rest_has_type && type_option_type.nil?
|
626
|
+
if record_type_id.nil?
|
627
|
+
#raise_command_error "#{rest_type_label} is required.\n#{optparse}"
|
628
|
+
type_list = rest_type_interface.list({max:10000, creatable:true})[rest_type_list_key]
|
629
|
+
type_dropdown_options = respond_to?("#{rest_key}_type_list_to_options", true) ? send("#{rest_key}_type_list_to_options", type_list) : type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} }
|
630
|
+
record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type']
|
631
|
+
end
|
632
|
+
record_type = rest_type_find_by_name_or_id(record_type_id)
|
633
|
+
if record_type.nil?
|
634
|
+
return 1, "#{rest_type_label} not found for '#{record_type_id}"
|
635
|
+
end
|
636
|
+
end
|
622
637
|
record_payload = {}
|
623
638
|
if record_name
|
624
639
|
record_payload['name'] = record_name
|
@@ -667,7 +682,7 @@ EOT
|
|
667
682
|
end
|
668
683
|
end
|
669
684
|
api_params = (options[:params] || {}).merge(record_payload)
|
670
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params)
|
685
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params, false, true)
|
671
686
|
v_prompt.deep_compact!
|
672
687
|
v_prompt.booleanize! # 'on' => true
|
673
688
|
record_payload.deep_merge!(v_prompt)
|
@@ -679,6 +694,18 @@ EOT
|
|
679
694
|
v_prompt.booleanize! # 'on' => true
|
680
695
|
record_payload.deep_merge!(v_prompt)
|
681
696
|
end
|
697
|
+
# permissions
|
698
|
+
if rest_perms_config[:enabled]
|
699
|
+
if rest_perms_config[:version] == 2
|
700
|
+
perms = prompt_permissions_v2(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || [])
|
701
|
+
else
|
702
|
+
perms = prompt_permissions(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || [])
|
703
|
+
end
|
704
|
+
if !rest_perms_config[:name].nil?
|
705
|
+
perms.transform_keys! {|k| k == 'resourcePermissions' ? rest_perms_config[:name] : k}
|
706
|
+
end
|
707
|
+
record_payload.merge!(perms)
|
708
|
+
end
|
682
709
|
payload[rest_object_key] = record_payload
|
683
710
|
end
|
684
711
|
rest_interface.setopts(options)
|