morpheus-cli 5.4.0 → 5.4.3.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/account_users_interface.rb +68 -0
- data/lib/morpheus/api/api_client.rb +55 -10
- data/lib/morpheus/api/audit_interface.rb +9 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +20 -0
- data/lib/morpheus/api/instances_interface.rb +49 -0
- data/lib/morpheus/api/load_balancer_monitors_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
- data/lib/morpheus/api/load_balancer_profiles_interface.rb +4 -5
- data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +13 -4
- data/lib/morpheus/api/load_balancers_interface.rb +5 -0
- data/lib/morpheus/api/network_routers_interface.rb +9 -0
- data/lib/morpheus/api/network_static_routes_interface.rb +36 -0
- data/lib/morpheus/api/ping_interface.rb +2 -0
- data/lib/morpheus/api/read_interface.rb +4 -3
- data/lib/morpheus/api/rest_interface.rb +3 -3
- data/lib/morpheus/api/secondary_read_interface.rb +1 -1
- data/lib/morpheus/api/secondary_rest_interface.rb +19 -19
- data/lib/morpheus/api/setup_interface.rb +4 -0
- data/lib/morpheus/api/snapshots_interface.rb +19 -0
- data/lib/morpheus/api/storage_server_types_interface.rb +14 -0
- data/lib/morpheus/api/storage_servers_interface.rb +9 -0
- data/lib/morpheus/api/storage_volume_types_interface.rb +9 -0
- data/lib/morpheus/api/storage_volumes_interface.rb +9 -0
- data/lib/morpheus/api/users_interface.rb +16 -63
- data/lib/morpheus/cli/cli_command.rb +253 -5
- data/lib/morpheus/cli/cli_registry.rb +1 -1
- data/lib/morpheus/cli/commands/alias_command.rb +1 -1
- data/lib/morpheus/cli/commands/apps.rb +14 -78
- data/lib/morpheus/cli/commands/audit.rb +188 -0
- data/lib/morpheus/cli/commands/blueprints_command.rb +1 -1
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +88 -0
- data/lib/morpheus/cli/commands/change_password_command.rb +4 -4
- data/lib/morpheus/cli/commands/clusters.rb +96 -58
- data/lib/morpheus/cli/commands/hosts.rb +27 -15
- data/lib/morpheus/cli/commands/image_builder_command.rb +4 -8
- data/lib/morpheus/cli/commands/instances.rb +359 -3
- data/lib/morpheus/cli/commands/integrations_command.rb +1 -12
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -0
- data/lib/morpheus/cli/commands/load_balancer_monitors.rb +70 -0
- data/lib/morpheus/cli/commands/load_balancer_pools.rb +29 -50
- data/lib/morpheus/cli/commands/load_balancer_profiles.rb +64 -0
- data/lib/morpheus/cli/commands/load_balancer_types.rb +9 -4
- data/lib/morpheus/cli/commands/load_balancer_virtual_servers.rb +69 -58
- data/lib/morpheus/cli/commands/load_balancers.rb +109 -6
- data/lib/morpheus/cli/commands/network_firewalls_command.rb +22 -5
- data/lib/morpheus/cli/commands/network_routers_command.rb +96 -45
- data/lib/morpheus/cli/commands/network_static_routes_command.rb +451 -0
- data/lib/morpheus/cli/commands/network_transport_zones_command.rb +4 -4
- data/lib/morpheus/cli/commands/networks_command.rb +2 -2
- data/lib/morpheus/cli/commands/open_command.rb +30 -0
- data/lib/morpheus/cli/commands/options.rb +98 -0
- data/lib/morpheus/cli/commands/ping.rb +3 -5
- data/lib/morpheus/cli/commands/policies_command.rb +2 -2
- data/lib/morpheus/cli/commands/prices_command.rb +7 -7
- data/lib/morpheus/cli/commands/provisioning_settings_command.rb +1 -0
- data/lib/morpheus/cli/commands/remote.rb +20 -12
- data/lib/morpheus/cli/commands/roles.rb +1 -1
- data/lib/morpheus/cli/commands/security_groups.rb +2 -2
- data/lib/morpheus/cli/commands/service_plans_command.rb +1 -1
- data/lib/morpheus/cli/commands/setup.rb +1 -1
- data/lib/morpheus/cli/commands/shell.rb +2 -2
- data/lib/morpheus/cli/commands/snapshots.rb +139 -0
- data/lib/morpheus/cli/commands/storage_server_types.rb +50 -0
- data/lib/morpheus/cli/commands/storage_servers.rb +122 -0
- data/lib/morpheus/cli/commands/storage_volume_types.rb +50 -0
- data/lib/morpheus/cli/commands/storage_volumes.rb +103 -0
- data/lib/morpheus/cli/commands/tasks.rb +5 -5
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_groups_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +3 -2
- data/lib/morpheus/cli/commands/user_sources_command.rb +1 -1
- data/lib/morpheus/cli/commands/users.rb +28 -28
- data/lib/morpheus/cli/commands/view.rb +102 -0
- data/lib/morpheus/cli/commands/virtual_images.rb +4 -1
- data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -5
- data/lib/morpheus/cli/mixins/load_balancers_helper.rb +24 -4
- data/lib/morpheus/cli/mixins/print_helper.rb +50 -18
- data/lib/morpheus/cli/mixins/processes_helper.rb +1 -2
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +96 -6
- data/lib/morpheus/cli/mixins/rest_command.rb +148 -74
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +174 -82
- data/lib/morpheus/cli/mixins/storage_servers_helper.rb +156 -0
- data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +119 -0
- data/lib/morpheus/cli/option_types.rb +95 -28
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/ext/string.rb +29 -6
- data/lib/morpheus/routes.rb +238 -0
- data/lib/morpheus/util.rb +6 -1
- metadata +26 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'morpheus/cli/mixins/print_helper'
|
|
2
|
+
require 'morpheus/cli/option_types'
|
|
3
|
+
require 'morpheus/rest_client'
|
|
4
|
+
# Mixin for Morpheus::Cli command classes
|
|
5
|
+
# Provides common methods for storage volume management
|
|
6
|
+
# including storage volumes and storage volume types
|
|
7
|
+
module Morpheus::Cli::StorageVolumesHelper
|
|
8
|
+
|
|
9
|
+
def self.included(klass)
|
|
10
|
+
klass.send :include, Morpheus::Cli::PrintHelper
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def storage_volumes_interface
|
|
14
|
+
# @api_client.storage_volumes
|
|
15
|
+
raise "#{self.class} has not defined @storage_volumes_interface" if @storage_volumes_interface.nil?
|
|
16
|
+
@storage_volumes_interface
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def storage_volume_types_interface
|
|
20
|
+
# @api_client.storage_volume_types
|
|
21
|
+
raise "#{self.class} has not defined @storage_volume_types_interface" if @storage_volume_types_interface.nil?
|
|
22
|
+
@storage_volume_types_interface
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def storage_volume_object_key
|
|
26
|
+
'storageVolume'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def storage_volume_list_key
|
|
30
|
+
'storageVolumes'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def storage_volume_label
|
|
34
|
+
'Storage Volume'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def storage_volume_label_plural
|
|
38
|
+
'Storage Volume'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def storage_volume_type_object_key
|
|
42
|
+
'storageVolumeType'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def storage_volume_type_list_key
|
|
46
|
+
'storageVolumeTypes'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def storage_volume_type_label
|
|
50
|
+
'Storage Volume Type'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def storage_volume_type_label_plural
|
|
54
|
+
'Storage Volume Types'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_available_storage_volume_types(refresh=false)
|
|
58
|
+
if !@available_storage_volume_types || refresh
|
|
59
|
+
@available_storage_volume_types = storage_volume_types_interface.list({max:1000})[storage_volume_type_list_key]
|
|
60
|
+
end
|
|
61
|
+
return @available_storage_volume_types
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def storage_volume_type_for_name_or_id(val)
|
|
65
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
66
|
+
return storage_volume_type_for_id(val)
|
|
67
|
+
else
|
|
68
|
+
return storage_volume_type_for_name(val)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def storage_volume_type_for_id(val)
|
|
73
|
+
record = get_available_storage_volume_types().find { |z| z['id'].to_i == val.to_i}
|
|
74
|
+
label = "Storage Volume Type"
|
|
75
|
+
if record.nil?
|
|
76
|
+
print_red_alert "#{label} not found by id #{val}"
|
|
77
|
+
return nil
|
|
78
|
+
end
|
|
79
|
+
return record
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def storage_volume_type_for_name(val)
|
|
83
|
+
records = get_available_storage_volume_types().select { |z| z['name'].downcase == val.downcase || z['code'].downcase == val.downcase}
|
|
84
|
+
label = "Storage Volume Type"
|
|
85
|
+
if records.empty?
|
|
86
|
+
print_red_alert "#{label} not found by name '#{val}'"
|
|
87
|
+
return nil
|
|
88
|
+
elsif records.size > 1
|
|
89
|
+
print_red_alert "More than one #{label.downcase} found by name '#{val}'"
|
|
90
|
+
print_error "\n"
|
|
91
|
+
puts_error as_pretty_table(records, [:id, :name], {color:red})
|
|
92
|
+
print_red_alert "Try using ID instead"
|
|
93
|
+
print_error reset,"\n"
|
|
94
|
+
return nil
|
|
95
|
+
else
|
|
96
|
+
return records[0]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def format_storage_volume_status(record, return_color=cyan)
|
|
101
|
+
out = ""
|
|
102
|
+
status_string = record['status']
|
|
103
|
+
if status_string.nil? || status_string.empty? || status_string == "unknown"
|
|
104
|
+
out << "#{white}UNKNOWN#{return_color}"
|
|
105
|
+
elsif status_string == 'provisioned' || status_string == 'unattached'
|
|
106
|
+
out << "#{cyan}#{status_string.capitalize}#{return_color}"
|
|
107
|
+
elsif status_string == 'syncing'
|
|
108
|
+
out << "#{yellow}#{status_string.capitalize}#{return_color}"
|
|
109
|
+
else
|
|
110
|
+
out << "#{red}#{status_string ? status_string.capitalize : 'N/A'}#{record['statusMessage'] ? "#{return_color} - #{record['statusMessage']}" : ''}#{return_color}"
|
|
111
|
+
end
|
|
112
|
+
out
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def format_storage_volume_source(storage_volume)
|
|
116
|
+
storage_volume['source']
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
@@ -42,7 +42,7 @@ module Morpheus
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def self.prompt(option_types, options={}, api_client=nil, api_params={}, no_prompt=false, paging_enabled=false)
|
|
45
|
+
def self.prompt(option_types, options={}, api_client=nil, api_params={}, no_prompt=false, paging_enabled=false, ignore_empty=false)
|
|
46
46
|
paging_enabled = false if Morpheus::Cli.windows?
|
|
47
47
|
no_prompt = no_prompt || options[:no_prompt]
|
|
48
48
|
results = {}
|
|
@@ -57,6 +57,10 @@ module Morpheus
|
|
|
57
57
|
if option_type['fieldGroup'].to_s.downcase == 'options'
|
|
58
58
|
option_type['fieldGroup'] = 'default'
|
|
59
59
|
end
|
|
60
|
+
# apply custom templates
|
|
61
|
+
if option_type['fieldName'] == 'sshHosts'
|
|
62
|
+
option_type['type'] = 'multiText'
|
|
63
|
+
end
|
|
60
64
|
end
|
|
61
65
|
# puts "Options Prompt #{options}"
|
|
62
66
|
# Sort options by default, group, advanced
|
|
@@ -128,7 +132,11 @@ module Morpheus
|
|
|
128
132
|
get_object_value(options, depends_on_field_key) ||
|
|
129
133
|
get_object_value(api_params, depends_on_field_key)
|
|
130
134
|
|
|
131
|
-
if
|
|
135
|
+
if field_value.nil? && !options['_object_key'].nil?
|
|
136
|
+
field_value = get_object_value({options['_object_key'] => results}, depends_on_field_key)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if !field_value.nil? && (depends_on_value.nil? || depends_on_value.empty? || field_value.to_s.match?(depends_on_value))
|
|
132
140
|
found_dep_value = true if match_type != 'all'
|
|
133
141
|
else
|
|
134
142
|
found_dep_value = false if match_type == 'all'
|
|
@@ -151,6 +159,10 @@ module Morpheus
|
|
|
151
159
|
context_map = context_map[ns.to_s]
|
|
152
160
|
end
|
|
153
161
|
|
|
162
|
+
# build parameters for option source api request
|
|
163
|
+
option_params = (option_type['noParams'] ? {} : (api_params || {}).deep_merge(results))
|
|
164
|
+
option_params.merge!(option_type['optionParams']) if option_type['optionParams']
|
|
165
|
+
|
|
154
166
|
# use the value passed in the options map
|
|
155
167
|
if cur_namespace.respond_to?('key?') && cur_namespace.key?(field_name)
|
|
156
168
|
value = cur_namespace[field_name]
|
|
@@ -161,25 +173,25 @@ module Morpheus
|
|
|
161
173
|
end
|
|
162
174
|
# these select prompts should just fall down through below, with the extra params no_prompt, use_value
|
|
163
175
|
elsif option_type['type'] == 'select'
|
|
164
|
-
value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client,
|
|
176
|
+
value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true, nil, false, ignore_empty)
|
|
165
177
|
elsif option_type['type'] == 'multiSelect'
|
|
166
178
|
# support value as csv like "thing1, thing2"
|
|
167
179
|
value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
|
|
168
180
|
input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
|
|
169
181
|
select_value_list = []
|
|
170
182
|
value_list.each_with_index do |v, i|
|
|
171
|
-
select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client,
|
|
183
|
+
select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true, nil, false, ignore_empty)
|
|
172
184
|
end
|
|
173
185
|
value = select_value_list
|
|
174
186
|
elsif option_type['type'] == 'typeahead'
|
|
175
|
-
value = typeahead_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client,
|
|
187
|
+
value = typeahead_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true)
|
|
176
188
|
elsif option_type['type'] == 'multiTypeahead'
|
|
177
189
|
# support value as csv like "thing1, thing2"
|
|
178
190
|
value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
|
|
179
191
|
input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
|
|
180
192
|
select_value_list = []
|
|
181
193
|
value_list.each_with_index do |v, i|
|
|
182
|
-
select_value_list << typeahead_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client,
|
|
194
|
+
select_value_list << typeahead_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true)
|
|
183
195
|
end
|
|
184
196
|
value = select_value_list
|
|
185
197
|
end
|
|
@@ -205,14 +217,14 @@ module Morpheus
|
|
|
205
217
|
# select type is special because it supports skipSingleOption
|
|
206
218
|
# and prints the available options on error
|
|
207
219
|
if ['select', 'multiSelect'].include?(option_type['type'])
|
|
208
|
-
value = select_prompt(option_type, api_client,
|
|
220
|
+
value = select_prompt(option_type, api_client, option_params, true, nil, false, ignore_empty)
|
|
209
221
|
value_found = !!value
|
|
210
222
|
end
|
|
211
223
|
if ['typeahead', 'multiTypeahead'].include?(option_type['type'])
|
|
212
|
-
value = typeahead_prompt(option_type, api_client,
|
|
224
|
+
value = typeahead_prompt(option_type, api_client, option_params, true)
|
|
213
225
|
value_found = !!value
|
|
214
226
|
end
|
|
215
|
-
if !value_found
|
|
227
|
+
if !value_found && !ignore_empty
|
|
216
228
|
if option_type['required']
|
|
217
229
|
print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
|
|
218
230
|
print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{help_field_key}=] - #{option_type['description']}\n", Term::ANSIColor.reset
|
|
@@ -246,11 +258,11 @@ module Morpheus
|
|
|
246
258
|
# I suppose the entered value should take precedence
|
|
247
259
|
# api_params = api_params.merge(options) # this might be good enough
|
|
248
260
|
# dup it
|
|
249
|
-
value = select_prompt(option_type, api_client,
|
|
261
|
+
value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
|
|
250
262
|
if value && option_type['type'] == 'multiSelect'
|
|
251
263
|
value = [value]
|
|
252
264
|
while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
|
|
253
|
-
if addn_value = select_prompt(option_type, api_client,
|
|
265
|
+
if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
|
|
254
266
|
value << addn_value
|
|
255
267
|
else
|
|
256
268
|
break
|
|
@@ -258,11 +270,11 @@ module Morpheus
|
|
|
258
270
|
end
|
|
259
271
|
end
|
|
260
272
|
elsif ['typeahead', 'multiTypeahead'].include?(option_type['type'])
|
|
261
|
-
value = typeahead_prompt(option_type, api_client,
|
|
273
|
+
value = typeahead_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
|
|
262
274
|
if value && option_type['type'] == 'multiTypeahead'
|
|
263
275
|
value = [value]
|
|
264
276
|
while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
|
|
265
|
-
if addn_value = typeahead_prompt(option_type, api_client,
|
|
277
|
+
if addn_value = typeahead_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
|
|
266
278
|
value << addn_value
|
|
267
279
|
else
|
|
268
280
|
break
|
|
@@ -279,6 +291,8 @@ module Morpheus
|
|
|
279
291
|
value = file_prompt(option_type)
|
|
280
292
|
elsif option_type['type'] == 'file-content'
|
|
281
293
|
value = file_content_prompt(option_type, options, api_client, {})
|
|
294
|
+
elsif option_type['type'] == 'multiText'
|
|
295
|
+
value = multitext_prompt(option_type)
|
|
282
296
|
else
|
|
283
297
|
value = generic_prompt(option_type)
|
|
284
298
|
end
|
|
@@ -286,7 +300,11 @@ module Morpheus
|
|
|
286
300
|
|
|
287
301
|
if option_type['type'] == 'multiSelect'
|
|
288
302
|
value = [value] if !value.nil? && !value.is_a?(Array)
|
|
289
|
-
|
|
303
|
+
elsif option_type['type'] == 'multiText'
|
|
304
|
+
# multiText expects csv value
|
|
305
|
+
if value && value.is_a?(String)
|
|
306
|
+
value = value.split(",").collect {|it| it.strip }
|
|
307
|
+
end
|
|
290
308
|
end
|
|
291
309
|
context_map[field_name] = value if !(value.nil? || (value.is_a?(Hash) && value.empty?))
|
|
292
310
|
parent_context_map.reject! {|k,v| k == parent_ns && (v.nil? || (v.is_a?(Hash) && v.empty?))}
|
|
@@ -305,7 +323,7 @@ module Morpheus
|
|
|
305
323
|
end
|
|
306
324
|
optionString = options.collect{ |b| b[:checked] ? "(#{b[:key]})" : b[:key]}.join(', ')
|
|
307
325
|
while !value_found do
|
|
308
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }[#{optionString}]: "
|
|
326
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }[#{optionString}]: "
|
|
309
327
|
input = $stdin.gets.chomp!
|
|
310
328
|
if input == '?'
|
|
311
329
|
help_prompt(option_type)
|
|
@@ -332,7 +350,7 @@ module Morpheus
|
|
|
332
350
|
value_found = false
|
|
333
351
|
value = nil
|
|
334
352
|
while !value_found do
|
|
335
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
353
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
336
354
|
input = $stdin.gets.chomp!
|
|
337
355
|
value = input.empty? ? option_type['defaultValue'] : input
|
|
338
356
|
if !value.to_s.empty?
|
|
@@ -356,7 +374,7 @@ module Morpheus
|
|
|
356
374
|
Thread.current[:_last_select]
|
|
357
375
|
end
|
|
358
376
|
|
|
359
|
-
def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false)
|
|
377
|
+
def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false)
|
|
360
378
|
paging_enabled = false if Morpheus::Cli.windows?
|
|
361
379
|
field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
|
|
362
380
|
help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
|
|
@@ -365,7 +383,12 @@ module Morpheus
|
|
|
365
383
|
value_field = (option_type['config'] ? option_type['config']['valueField'] : nil) || 'value'
|
|
366
384
|
default_value = option_type['defaultValue']
|
|
367
385
|
default_value = default_value['id'] if default_value && default_value.is_a?(Hash) && !default_value['id'].nil?
|
|
368
|
-
|
|
386
|
+
|
|
387
|
+
if !option_type['params'].nil?
|
|
388
|
+
api_params = (api_params || {}).select {|k,v| option_type['params'].key?(k) || option_type['params'].key?(k.to_s)}
|
|
389
|
+
option_type['params'].select {|k,v| !v.empty?}.each {|k,v| api_params[k] = v}
|
|
390
|
+
end
|
|
391
|
+
|
|
369
392
|
# local array of options
|
|
370
393
|
if option_type['selectOptions']
|
|
371
394
|
# calculate from inline lambda
|
|
@@ -387,9 +410,11 @@ module Morpheus
|
|
|
387
410
|
select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
|
|
388
411
|
end
|
|
389
412
|
else
|
|
390
|
-
raise "option '#{
|
|
413
|
+
raise "option '#{help_field_key}' is type: 'select' and missing selectOptions or optionSource!"
|
|
391
414
|
end
|
|
392
415
|
|
|
416
|
+
return nil if (select_options.nil? || select_options.count == 0) && ignore_empty
|
|
417
|
+
|
|
393
418
|
# ensure the preselected value (passed as an option) is in the dropdown
|
|
394
419
|
if !use_value.nil?
|
|
395
420
|
matched_option = select_options.find {|opt| opt[value_field].to_s == use_value.to_s || opt['name'].to_s == use_value.to_s }
|
|
@@ -424,7 +449,8 @@ module Morpheus
|
|
|
424
449
|
default_value = found_default_option['name'] # name is prettier than value
|
|
425
450
|
end
|
|
426
451
|
else
|
|
427
|
-
found_default_option
|
|
452
|
+
found_default_option = select_options.find {|opt| opt[value_field].to_s == default_value.to_s || opt['name'] == default_value.to_s}
|
|
453
|
+
found_default_option = select_options.find {|opt| opt[value_field].to_s.start_with?(default_value.to_s) || opt['name'].to_s.start_with?(default_value.to_s)} if !found_default_option
|
|
428
454
|
if found_default_option
|
|
429
455
|
default_value = found_default_option['name'] # name is prettier than value
|
|
430
456
|
end
|
|
@@ -483,7 +509,7 @@ module Morpheus
|
|
|
483
509
|
}
|
|
484
510
|
|
|
485
511
|
has_more_pages = paging && (paging[:cur_page] * paging[:page_size]) < paging[:total]
|
|
486
|
-
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages && paging[:cur_page] > 0 ? ' more ' : ' '}options]: ", false).to_s
|
|
512
|
+
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages && paging[:cur_page] > 0 ? ' more ' : ' '}options]: ", false).to_s
|
|
487
513
|
input = input.chomp.strip
|
|
488
514
|
if input.empty? && default_value
|
|
489
515
|
input = default_value.to_s
|
|
@@ -511,6 +537,10 @@ module Morpheus
|
|
|
511
537
|
if value && !option_type['fieldInput'].nil?
|
|
512
538
|
value = {option_type['fieldName'].split('.').last => value, option_type['fieldInput'] => (no_prompt ? option_type['defaultInputValue'] : field_input_prompt(option_type))}
|
|
513
539
|
end
|
|
540
|
+
|
|
541
|
+
if value && !option_type['resultValueField'].nil?
|
|
542
|
+
value = {option_type['resultValueField'] => value}
|
|
543
|
+
end
|
|
514
544
|
value
|
|
515
545
|
end
|
|
516
546
|
|
|
@@ -554,7 +584,7 @@ module Morpheus
|
|
|
554
584
|
}
|
|
555
585
|
# prompt for typeahead input value
|
|
556
586
|
has_more_pages = paging && ((paging[:cur_page] + 1) * paging[:page_size]) < paging[:total]
|
|
557
|
-
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages ? ' more ' : ' '}options]: ", false).to_s
|
|
587
|
+
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!default_value.to_s.empty? ? ' ['+default_value.to_s+']' : ''} ['?' for#{has_more_pages ? ' more ' : ' '}options]: ", false).to_s
|
|
558
588
|
input = input.chomp.strip
|
|
559
589
|
end
|
|
560
590
|
|
|
@@ -648,6 +678,10 @@ module Morpheus
|
|
|
648
678
|
if select_options.empty?
|
|
649
679
|
print "The value '#{input}' matched 0 options.\n"
|
|
650
680
|
# print "Please try again.\n"
|
|
681
|
+
elsif select_options.size() == 1
|
|
682
|
+
print "The value '#{input}' matched 1 option.\n"
|
|
683
|
+
print "Perhaps you meant '#{select_options[0]['name']}' instead?"
|
|
684
|
+
# print "Please try again.\n"
|
|
651
685
|
else
|
|
652
686
|
print "The value '#{input}' matched #{select_options.size()} options.\n"
|
|
653
687
|
print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
|
|
@@ -662,6 +696,9 @@ module Morpheus
|
|
|
662
696
|
if select_options.empty?
|
|
663
697
|
print "The value '#{input}' matched 0 options.\n"
|
|
664
698
|
print "Please try again.\n"
|
|
699
|
+
elsif select_options.size() == 1
|
|
700
|
+
print "The value '#{input}' matched 1 option.\n"
|
|
701
|
+
print "Perhaps you meant '#{select_options[0]['name']}' instead?"
|
|
665
702
|
else
|
|
666
703
|
print "The value '#{input}' matched #{select_options.size()} options.\n"
|
|
667
704
|
print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
|
|
@@ -743,7 +780,7 @@ module Morpheus
|
|
|
743
780
|
value_found = false
|
|
744
781
|
value = nil
|
|
745
782
|
while !value_found do
|
|
746
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
783
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
747
784
|
input = $stdin.gets.chomp!
|
|
748
785
|
value = input.empty? ? option_type['defaultValue'] : input
|
|
749
786
|
if input == '?'
|
|
@@ -760,7 +797,7 @@ module Morpheus
|
|
|
760
797
|
value = nil
|
|
761
798
|
while !value_found do
|
|
762
799
|
if value.nil?
|
|
763
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)} [Type 'EOF' to stop input]: \n"
|
|
800
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)} [Type 'EOF' to stop input]: \n"
|
|
764
801
|
end
|
|
765
802
|
input = $stdin.gets.chomp!
|
|
766
803
|
# value = input.empty? ? option_type['defaultValue'] : input
|
|
@@ -784,7 +821,7 @@ module Morpheus
|
|
|
784
821
|
def self.password_prompt(option_type)
|
|
785
822
|
value_found = false
|
|
786
823
|
while !value_found do
|
|
787
|
-
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
824
|
+
print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
788
825
|
input = $stdin.noecho(&:gets).chomp!
|
|
789
826
|
value = input
|
|
790
827
|
print "\n"
|
|
@@ -804,11 +841,11 @@ module Morpheus
|
|
|
804
841
|
value_found = false
|
|
805
842
|
value = nil
|
|
806
843
|
while !value_found do
|
|
807
|
-
#print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
844
|
+
#print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
|
|
808
845
|
Readline.completion_append_character = ""
|
|
809
846
|
Readline.basic_word_break_characters = ''
|
|
810
847
|
Readline.completion_proc = proc {|s| Readline::FILENAME_COMPLETION_PROC.call(s) }
|
|
811
|
-
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: ", false).to_s
|
|
848
|
+
input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? (' (' + option_type['fieldAddOn'] + ') ') : '' }#{optional_label(option_type)}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: ", false).to_s
|
|
812
849
|
input = input.chomp.strip
|
|
813
850
|
#input = $stdin.gets.chomp!
|
|
814
851
|
value = input.empty? ? option_type['defaultValue'] : input.to_s
|
|
@@ -907,7 +944,35 @@ module Morpheus
|
|
|
907
944
|
return file_params
|
|
908
945
|
end
|
|
909
946
|
|
|
947
|
+
def self.multitext_prompt(option_type)
|
|
948
|
+
rtn = nil
|
|
949
|
+
|
|
950
|
+
# supports multi-part fields via config.fields
|
|
951
|
+
# {"fields": [{"name":"tag", "required":true, "label": "Tag"}, {"name":"value", "required":false, "label": "Scope"}]}
|
|
952
|
+
if option_type['config']['fields']
|
|
953
|
+
while (option_type['required'] && rtn.empty?) || self.confirm("Add#{rtn.empty? ? '': ' more'} #{option_type['fieldLabel']}?", {:default => false})
|
|
954
|
+
rtn ||= []
|
|
955
|
+
value = {}
|
|
956
|
+
option_type['config']['fields'].each do |field|
|
|
957
|
+
field_label = field['label'] || field['name'].capitalize
|
|
958
|
+
value[field['name']] = generic_prompt(option_type.merge({'fieldLabel' => field_label, 'required' => field['required'], 'description' => "#{option_type['fieldLabel']} #{field_label}"}))
|
|
959
|
+
end
|
|
960
|
+
rtn << value
|
|
961
|
+
end
|
|
962
|
+
else
|
|
963
|
+
if rtn = generic_prompt(option_type)
|
|
964
|
+
rtn = [rtn]
|
|
965
|
+
while self.confirm("Add more #{option_type['fieldLabel']}?", {:default => false}) do
|
|
966
|
+
rtn << generic_prompt(option_type)
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
end
|
|
970
|
+
rtn
|
|
971
|
+
end
|
|
972
|
+
|
|
910
973
|
def self.load_options(option_type, api_client, api_params, query_value=nil)
|
|
974
|
+
field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
|
|
975
|
+
help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
|
|
911
976
|
select_options = []
|
|
912
977
|
# local array of options
|
|
913
978
|
if option_type['selectOptions']
|
|
@@ -926,6 +991,8 @@ module Morpheus
|
|
|
926
991
|
select_options = filtered_options
|
|
927
992
|
end
|
|
928
993
|
elsif option_type['optionSource']
|
|
994
|
+
api_params = api_params.select {|k,v| option_type['params'].include(k)} if !option_type['params'].nil? && api_params
|
|
995
|
+
|
|
929
996
|
# calculate from inline lambda
|
|
930
997
|
if option_type['optionSource'].is_a?(Proc)
|
|
931
998
|
select_options = option_type['optionSource'].call(api_client, api_params || {})
|
|
@@ -937,7 +1004,7 @@ module Morpheus
|
|
|
937
1004
|
select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
|
|
938
1005
|
end
|
|
939
1006
|
else
|
|
940
|
-
raise "option '#{
|
|
1007
|
+
raise "option '#{help_field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
|
|
941
1008
|
end
|
|
942
1009
|
|
|
943
1010
|
return select_options
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/cli.rb
CHANGED
data/lib/morpheus/ext/string.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class String
|
|
2
2
|
|
|
3
3
|
def pluralize
|
|
4
|
-
value = self
|
|
4
|
+
value = self.dup
|
|
5
5
|
if value == ""
|
|
6
6
|
value
|
|
7
7
|
elsif value[-1].chr == "y"
|
|
@@ -18,7 +18,7 @@ class String
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def singularize
|
|
21
|
-
value = self
|
|
21
|
+
value = self.dup
|
|
22
22
|
if value == ""
|
|
23
23
|
value
|
|
24
24
|
elsif value.size > 3 && value[-3..-1] == "ies"
|
|
@@ -27,15 +27,38 @@ class String
|
|
|
27
27
|
value[0..-3]
|
|
28
28
|
elsif value[-1] == "s"
|
|
29
29
|
value[0..-2]
|
|
30
|
+
else
|
|
31
|
+
value
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
+
def underscore
|
|
36
|
+
value = self.dup
|
|
37
|
+
value.gsub!(/::/, '/')
|
|
38
|
+
value.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
|
39
|
+
value.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
|
40
|
+
value.tr!("-", "_")
|
|
41
|
+
value.tr!(" ", "_")
|
|
42
|
+
value.downcase!
|
|
43
|
+
value
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def camelcase
|
|
47
|
+
value = self.underscore.gsub(/\_([a-z])/) do $1.upcase end
|
|
48
|
+
value = value[0, 1].downcase + value[1..-1]
|
|
49
|
+
value
|
|
35
50
|
end
|
|
36
51
|
|
|
37
|
-
def
|
|
38
|
-
self.
|
|
52
|
+
def upcamelcase
|
|
53
|
+
self.camelcase.capitalize
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def titleize
|
|
57
|
+
self.underscore.split("_").map(&:capitalize).join(" ")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dasherize
|
|
61
|
+
self.underscore.gsub("_", "-")
|
|
39
62
|
end
|
|
40
63
|
|
|
41
64
|
end
|