morpheus-cli 4.2.8 → 4.2.10
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.rb +1 -1
- data/lib/morpheus/api/activity_interface.rb +9 -0
- data/lib/morpheus/api/api_client.rb +83 -27
- data/lib/morpheus/api/apps_interface.rb +21 -0
- data/lib/morpheus/api/dashboard_interface.rb +5 -21
- data/lib/morpheus/api/instances_interface.rb +3 -10
- data/lib/morpheus/api/invoice_line_items_interface.rb +14 -0
- data/lib/morpheus/api/invoices_interface.rb +7 -12
- data/lib/morpheus/api/library_layouts_interface.rb +8 -0
- data/lib/morpheus/api/ping_interface.rb +20 -0
- data/lib/morpheus/api/projects_interface.rb +33 -0
- data/lib/morpheus/api/setup_interface.rb +19 -36
- data/lib/morpheus/api/user_settings_interface.rb +0 -6
- data/lib/morpheus/api/whoami_interface.rb +4 -8
- data/lib/morpheus/benchmarking.rb +16 -26
- data/lib/morpheus/cli.rb +10 -5
- data/lib/morpheus/cli/access_token_command.rb +5 -8
- data/lib/morpheus/cli/activity_command.rb +146 -0
- data/lib/morpheus/cli/apps.rb +312 -121
- data/lib/morpheus/cli/archives_command.rb +1 -1
- data/lib/morpheus/cli/auth_command.rb +4 -11
- data/lib/morpheus/cli/blueprints_command.rb +196 -137
- data/lib/morpheus/cli/change_password_command.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +225 -72
- data/lib/morpheus/cli/cli_registry.rb +2 -2
- data/lib/morpheus/cli/cloud_datastores_command.rb +1 -1
- data/lib/morpheus/cli/clouds.rb +5 -20
- data/lib/morpheus/cli/clusters.rb +4 -28
- data/lib/morpheus/cli/commands/standard/alias_command.rb +2 -9
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +2 -0
- data/lib/morpheus/cli/commands/standard/curl_command.rb +2 -3
- data/lib/morpheus/cli/commands/standard/history_command.rb +3 -6
- data/lib/morpheus/cli/commands/standard/man_command.rb +10 -7
- data/lib/morpheus/cli/commands/standard/ssl_verification_command.rb +10 -9
- data/lib/morpheus/cli/containers_command.rb +3 -3
- data/lib/morpheus/cli/credentials.rb +13 -16
- data/lib/morpheus/cli/error_handler.rb +18 -12
- data/lib/morpheus/cli/errors.rb +45 -0
- data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
- data/lib/morpheus/cli/execution_request_command.rb +4 -4
- data/lib/morpheus/cli/groups.rb +84 -132
- data/lib/morpheus/cli/hosts.rb +6 -16
- data/lib/morpheus/cli/instances.rb +100 -183
- data/lib/morpheus/cli/invoices_command.rb +505 -71
- data/lib/morpheus/cli/library_layouts_command.rb +254 -166
- data/lib/morpheus/cli/library_option_lists_command.rb +0 -87
- data/lib/morpheus/cli/library_option_types_command.rb +0 -96
- data/lib/morpheus/cli/license.rb +3 -0
- data/lib/morpheus/cli/login.rb +17 -37
- data/lib/morpheus/cli/logout.rb +9 -5
- data/lib/morpheus/cli/mixins/accounts_helper.rb +83 -7
- data/lib/morpheus/cli/mixins/operations_helper.rb +41 -0
- data/lib/morpheus/cli/mixins/option_source_helper.rb +255 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +18 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +222 -13
- data/lib/morpheus/cli/mixins/remote_helper.rb +139 -0
- data/lib/morpheus/cli/monitoring_checks_command.rb +11 -3
- data/lib/morpheus/cli/network_groups_command.rb +8 -2
- data/lib/morpheus/cli/option_types.rb +1 -1
- data/lib/morpheus/cli/ping.rb +252 -0
- data/lib/morpheus/cli/price_sets_command.rb +16 -27
- data/lib/morpheus/cli/prices_command.rb +34 -27
- data/lib/morpheus/cli/processes_command.rb +81 -7
- data/lib/morpheus/cli/projects_command.rb +607 -0
- data/lib/morpheus/cli/recent_activity_command.rb +87 -65
- data/lib/morpheus/cli/remote.rb +965 -974
- data/lib/morpheus/cli/reports_command.rb +3 -15
- data/lib/morpheus/cli/roles.rb +8 -31
- data/lib/morpheus/cli/service_plans_command.rb +25 -31
- data/lib/morpheus/cli/setup.rb +392 -0
- data/lib/morpheus/cli/shell.rb +144 -56
- data/lib/morpheus/cli/subnets_command.rb +71 -11
- data/lib/morpheus/cli/tasks.rb +3 -3
- data/lib/morpheus/cli/user_sources_command.rb +4 -4
- data/lib/morpheus/cli/users.rb +135 -109
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +7 -7
- data/lib/morpheus/cli/whoami.rb +90 -129
- data/lib/morpheus/cli/wiki_command.rb +2 -14
- data/lib/morpheus/ext/rest_client.rb +36 -0
- data/lib/morpheus/formatters.rb +42 -5
- data/lib/morpheus/rest_client.rb +0 -10
- data/lib/morpheus/terminal.rb +41 -1
- data/lib/morpheus/util.rb +24 -0
- metadata +16 -3
- data/lib/morpheus/cli/command_error.rb +0 -22
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
require 'morpheus/cli/mixins/print_helper'
|
|
2
|
+
|
|
3
|
+
# Mixin for Morpheus::Cli command classes
|
|
4
|
+
# Provides common methods for fetching objects via /api/options/:optionSource
|
|
5
|
+
# The including class must establish @options_interface or @api_client
|
|
6
|
+
# This is useful for when the user does not need to have permission to other endpoints
|
|
7
|
+
module Morpheus::Cli::OptionSourceHelper
|
|
8
|
+
|
|
9
|
+
def self.included(klass)
|
|
10
|
+
klass.send :include, Morpheus::Cli::PrintHelper
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def options_interface
|
|
14
|
+
api_interface = @options_interface
|
|
15
|
+
api_interface = @api_client.options if api_interface.nil? && @api_client
|
|
16
|
+
# @api_client.options
|
|
17
|
+
raise "#{self.class} has not defined @options_interface or @api_client" if api_interface.nil?
|
|
18
|
+
api_interface
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# todo: rewrite these specific methods to use the generic one.
|
|
22
|
+
|
|
23
|
+
def get_available_user_options(refresh=false)
|
|
24
|
+
if !@available_user_options || refresh
|
|
25
|
+
option_results = options_interface.options_for_source('users',{})
|
|
26
|
+
@available_user_options = option_results['data'].collect {|it|
|
|
27
|
+
{"name" => it["name"], "value" => it["value"],
|
|
28
|
+
"username" => it["name"], "id" => it["value"]}
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
return @available_user_options
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def find_available_user_option(name)
|
|
35
|
+
users = get_available_user_options().select {|it|
|
|
36
|
+
name && (it['name'].to_s.downcase == name.to_s.downcase || it['value'].to_s == name.to_s) }
|
|
37
|
+
if users.empty?
|
|
38
|
+
print_red_alert "User not found by username or id '#{name}'"
|
|
39
|
+
return nil
|
|
40
|
+
elsif users.size > 1
|
|
41
|
+
print_red_alert "#{users.size} users found by username or id '#{name}'"
|
|
42
|
+
return nil
|
|
43
|
+
else
|
|
44
|
+
return users[0]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_group_options(refresh=false, api_params={})
|
|
50
|
+
if !@available_group_options || refresh
|
|
51
|
+
option_results = options_interface.options_for_source('groups', api_params)
|
|
52
|
+
@available_group_options = option_results['data'].collect {|it|
|
|
53
|
+
{"name" => it["name"], "value" => it["value"], "id" => it["value"]}
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
return @available_group_options
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def find_group_option(group_id, refresh=false, api_params={})
|
|
60
|
+
if group_id.to_s.strip == ""
|
|
61
|
+
print_red_alert "Group not found by for blank id"
|
|
62
|
+
return nil
|
|
63
|
+
end
|
|
64
|
+
groups = get_group_options(refresh, api_params).select {|it| (it['name'].to_s == group_id.to_s || it['id'].to_s == group_id.to_s) }
|
|
65
|
+
if groups.empty?
|
|
66
|
+
print_red_alert "Group not found by '#{group_id}'"
|
|
67
|
+
return nil
|
|
68
|
+
elsif groups.size > 1
|
|
69
|
+
print_red_alert "#{groups.size} groups found by '#{group_id}'"
|
|
70
|
+
return nil
|
|
71
|
+
else
|
|
72
|
+
return groups[0]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def get_cloud_options(refresh=false, api_params={})
|
|
77
|
+
if !@available_cloud_options || refresh
|
|
78
|
+
option_results = options_interface.options_for_source('clouds', api_params)
|
|
79
|
+
@available_cloud_options = option_results['data'].collect {|it|
|
|
80
|
+
{"name" => it["name"], "value" => it["value"], "id" => it["value"]}
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
return @available_cloud_options
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def find_cloud_option(cloud_id, refresh=false, api_params={})
|
|
87
|
+
if cloud_id.to_s.strip == ""
|
|
88
|
+
print_red_alert "Cloud not found by for blank id"
|
|
89
|
+
return nil
|
|
90
|
+
end
|
|
91
|
+
clouds = get_cloud_options(refresh, api_params).select {|it| (it['name'].to_s == cloud_id.to_s || it['id'].to_s == cloud_id.to_s) }
|
|
92
|
+
if clouds.empty?
|
|
93
|
+
print_red_alert "Cloud not found by '#{cloud_id}'"
|
|
94
|
+
return nil
|
|
95
|
+
elsif clouds.size > 1
|
|
96
|
+
print_red_alert "#{clouds.size} clouds found by '#{cloud_id}'"
|
|
97
|
+
return nil
|
|
98
|
+
else
|
|
99
|
+
return clouds[0]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def get_tenant_options(refresh=false, api_params={})
|
|
104
|
+
if !@available_tenant_options || refresh
|
|
105
|
+
# source should be 'tenants' or 'allTenants'
|
|
106
|
+
# allTenants includes the current tenant
|
|
107
|
+
# I think we should always use that so you can use your own id
|
|
108
|
+
option_source = 'allTenants'
|
|
109
|
+
option_results = options_interface.options_for_source(option_source, api_params)
|
|
110
|
+
@available_tenant_options = option_results['data'].collect {|it|
|
|
111
|
+
{"name" => it["name"], "value" => it["value"], "id" => it["value"]}
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
return @available_tenant_options
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def find_tenant_option(tenant_id, refresh=false, api_params={})
|
|
118
|
+
tenant_id = tenant_id.to_s.strip
|
|
119
|
+
tenant_id_downcase = tenant_id.to_s.downcase
|
|
120
|
+
if tenant_id == ""
|
|
121
|
+
print_red_alert "Tenant not found by for blank id"
|
|
122
|
+
return nil
|
|
123
|
+
end
|
|
124
|
+
tenants = get_tenant_options(refresh, api_params).select {|it| (it['name'].to_s.downcase == tenant_id_downcase || it['id'].to_s == tenant_id.to_s) }
|
|
125
|
+
if tenants.empty?
|
|
126
|
+
print_red_alert "Tenant not found by '#{tenant_id}'"
|
|
127
|
+
return nil
|
|
128
|
+
elsif tenants.size > 1
|
|
129
|
+
print_red_alert "#{tenants.size} tenants found by '#{tenant_id}'"
|
|
130
|
+
return nil
|
|
131
|
+
else
|
|
132
|
+
return tenants[0]
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# parse cloud names or IDs into a name, works with array or csv
|
|
137
|
+
# skips validation of ID for now, just worry about translating names/codes
|
|
138
|
+
# def parse_cloud_id_list(id_list)
|
|
139
|
+
# cloud_ids = parse_id_list(id_list).collect {|cloud_id|
|
|
140
|
+
# if cloud_id.to_s =~ /\A\d{1,}\Z/
|
|
141
|
+
# cloud_id
|
|
142
|
+
# else
|
|
143
|
+
# cloud = find_cloud_option(cloud_id)
|
|
144
|
+
# if cloud.nil?
|
|
145
|
+
# # exit 1
|
|
146
|
+
# return nil
|
|
147
|
+
# end
|
|
148
|
+
# cloud['id']
|
|
149
|
+
# end
|
|
150
|
+
# }
|
|
151
|
+
# return cloud_ids
|
|
152
|
+
# end
|
|
153
|
+
|
|
154
|
+
# todo: some other common ones, accounts (tenants), etc.
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# a generic set of parse and find methods for any option source data
|
|
159
|
+
def load_option_source_data(option_source, api_params={}, refresh=false, &block)
|
|
160
|
+
@_option_source_cache ||= {}
|
|
161
|
+
option_source_hash = "#{option_source}#{api_params.empty? ? '' : api_params.to_s}"
|
|
162
|
+
data = @_option_source_cache[option_source_hash]
|
|
163
|
+
if data.nil? || refresh
|
|
164
|
+
json_response = options_interface.options_for_source(option_source, api_params)
|
|
165
|
+
data = json_response['data'].collect {|it|
|
|
166
|
+
{
|
|
167
|
+
"name" => it["name"],
|
|
168
|
+
"value" => it["value"],
|
|
169
|
+
"id" => (it["id"] || it["value"]),
|
|
170
|
+
"code" => it["code"]
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
@_option_source_cache[option_source_hash] = data
|
|
174
|
+
end
|
|
175
|
+
return data
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# todo: some other common ones, accounts (tenants), etc.
|
|
179
|
+
# todo: a generic set of parse and find methods like
|
|
180
|
+
# like this:
|
|
181
|
+
def parse_option_source_id_list(option_source, id_list, api_params={}, refresh=false)
|
|
182
|
+
option_source_label = option_source.to_s # .capitalize
|
|
183
|
+
option_data = load_option_source_data(option_source, api_params, refresh)
|
|
184
|
+
found_ids = []
|
|
185
|
+
parse_id_list(id_list).each {|record_id|
|
|
186
|
+
lowercase_id = record_id.to_s.downcase
|
|
187
|
+
# need to parameterize this behavior
|
|
188
|
+
if record_id.to_s.empty?
|
|
189
|
+
# never match blank nil or empty strings
|
|
190
|
+
print_red_alert "#{option_source_label} cannot be not found by with a blank id!"
|
|
191
|
+
return nil
|
|
192
|
+
# elsif record_id.to_s =~ /\A\d{1,}\Z/
|
|
193
|
+
# # always allow any ID for now..
|
|
194
|
+
# found_ids << record_id
|
|
195
|
+
else
|
|
196
|
+
# search with in a presedence by value, then name, then id (usually same as value)
|
|
197
|
+
# exact match on value first.
|
|
198
|
+
matching_results = []
|
|
199
|
+
matching_results = option_data.select {|it| it['value'] && it['value'] == record_id }
|
|
200
|
+
# match on value case /i
|
|
201
|
+
if matching_results.empty?
|
|
202
|
+
matching_results = option_data.select {|it| it['value'] && it['value'].to_s.downcase == lowercase_id }
|
|
203
|
+
end
|
|
204
|
+
# match on name case /i
|
|
205
|
+
if matching_results.empty?
|
|
206
|
+
matching_results = option_data.select {|it| it['name'] && it['name'].to_s.downcase == lowercase_id }
|
|
207
|
+
end
|
|
208
|
+
# match on id too, in case it is returned and different from value?
|
|
209
|
+
if matching_results.empty?
|
|
210
|
+
matching_results = option_data.select {|it| it['id'] && it['id'].to_s.downcase == lowercase_id }
|
|
211
|
+
end
|
|
212
|
+
if matching_results.empty?
|
|
213
|
+
print_red_alert "No #{option_source_label} found matching name or id '#{record_id}'"
|
|
214
|
+
return nil
|
|
215
|
+
elsif matching_results.size > 1
|
|
216
|
+
print_red_alert "#{matching_results.size} #{option_source_label} found matching name '#{record_id}'. Try specifying the id instead."
|
|
217
|
+
return nil
|
|
218
|
+
else
|
|
219
|
+
matching_result = matching_results[0]
|
|
220
|
+
if matching_result['value']
|
|
221
|
+
found_ids << matching_result['value']
|
|
222
|
+
else
|
|
223
|
+
found_ids << matching_result['id']
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
}
|
|
228
|
+
return found_ids
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def parse_cloud_id_list(id_list, api_params={}, refresh=false)
|
|
232
|
+
parse_option_source_id_list('clouds', id_list, api_params, refresh)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def parse_group_id_list(id_list, api_params={}, refresh=false)
|
|
236
|
+
parse_option_source_id_list('groups', id_list, api_params, refresh)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def parse_user_id_list(id_list, api_params={}, refresh=false)
|
|
240
|
+
parse_option_source_id_list('users', id_list, api_params, refresh)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def parse_tenant_id_list(id_list, api_params={}, refresh=false)
|
|
244
|
+
parse_option_source_id_list('allTenants', id_list, api_params, refresh)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# def parse_blueprints_id_list(id_list)
|
|
248
|
+
# parse_option_source_id_list('blueprints', id_list, api_params, refresh)
|
|
249
|
+
# end
|
|
250
|
+
|
|
251
|
+
def parse_project_id_list(id_list, api_params={}, refresh=false)
|
|
252
|
+
parse_option_source_id_list('projects', id_list, api_params, refresh)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
end
|
|
@@ -38,13 +38,16 @@ module Morpheus::Cli::PrintHelper
|
|
|
38
38
|
# puts red message to stderr
|
|
39
39
|
# why this not stderr yet? use print_error or if respond_to?(:my_terminal)
|
|
40
40
|
def print_red_alert(msg)
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
$stderr.print "#{red}#{msg}#{reset}\n"
|
|
42
|
+
#print_error "#{red}#{msg}#{reset}\n"
|
|
43
43
|
#puts_error "#{red}#{msg}#{reset}"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# puts green message to stdout
|
|
47
|
-
def print_green_success(msg)
|
|
47
|
+
def print_green_success(msg=nil)
|
|
48
|
+
if msg.nil?
|
|
49
|
+
msg = "success"
|
|
50
|
+
end
|
|
48
51
|
print "#{green}#{msg}#{reset}\n"
|
|
49
52
|
end
|
|
50
53
|
|
|
@@ -59,10 +62,21 @@ module Morpheus::Cli::PrintHelper
|
|
|
59
62
|
# print_h1(title, options={})
|
|
60
63
|
# print_h1(title, subtitles, options={})
|
|
61
64
|
# this can go away when we have a dirty @current_options
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# auto include remote name in h1 titles
|
|
68
|
+
# eg. Morpheus Instances [dev]
|
|
69
|
+
# if title && @appliance_name
|
|
70
|
+
# title = "#{title} [#{@appliance_name}]"
|
|
71
|
+
# end
|
|
72
|
+
|
|
62
73
|
if subtitles.is_a?(Hash)
|
|
63
74
|
options = subtitles
|
|
64
75
|
subtitles = (options[:subtitles] || []).flatten
|
|
65
76
|
end
|
|
77
|
+
if subtitles.is_a?(String)
|
|
78
|
+
subtitles = [subtitles]
|
|
79
|
+
end
|
|
66
80
|
subtitles = (subtitles || []).flatten
|
|
67
81
|
options ||= {}
|
|
68
82
|
color = options[:color] || cyan
|
|
@@ -728,7 +742,7 @@ module Morpheus::Cli::PrintHelper
|
|
|
728
742
|
# could use some options[:preferred_columns] logic here to throw away in some specified order
|
|
729
743
|
# --all fields disables this
|
|
730
744
|
trimmed_columns = []
|
|
731
|
-
if options[:
|
|
745
|
+
if options[:wrap] != true # && options[:include_fields].nil? && options[:all_fields] != true
|
|
732
746
|
|
|
733
747
|
begin
|
|
734
748
|
term_width = current_terminal_width()
|
|
@@ -17,6 +17,14 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
17
17
|
@api_client.instances
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def apps_interface
|
|
21
|
+
@api_client.apps
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def servers_interface
|
|
25
|
+
@api_client.servers
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
def options_interface
|
|
21
29
|
@api_client.options
|
|
22
30
|
end
|
|
@@ -82,7 +90,10 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
82
90
|
|
|
83
91
|
def get_available_accounts(refresh=false)
|
|
84
92
|
if !@available_accounts || refresh
|
|
85
|
-
@available_accounts = accounts_interface.list()['accounts']
|
|
93
|
+
# @available_accounts = accounts_interface.list()['accounts']
|
|
94
|
+
@available_accounts = options_interface.options_for_source("allTenants", {})['data'].collect {|it|
|
|
95
|
+
{"name" => it["name"], "value" => it["value"], "id" => it["value"]}
|
|
96
|
+
}
|
|
86
97
|
end
|
|
87
98
|
@available_accounts
|
|
88
99
|
end
|
|
@@ -242,6 +253,129 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
242
253
|
return instance
|
|
243
254
|
end
|
|
244
255
|
|
|
256
|
+
def parse_instance_id_list(id_list)
|
|
257
|
+
parse_id_list(id_list).collect do |instance_id|
|
|
258
|
+
find_instance_by_name_or_id(instance_id)['id']
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
## apps
|
|
263
|
+
|
|
264
|
+
def find_app_by_id(id)
|
|
265
|
+
app_results = apps_interface.get(id.to_i)
|
|
266
|
+
if app_results['app'].empty?
|
|
267
|
+
print_red_alert "App not found by id #{id}"
|
|
268
|
+
exit 1
|
|
269
|
+
end
|
|
270
|
+
return app_results['app']
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def find_app_by_name(name)
|
|
274
|
+
app_results = apps_interface.list({name: name})
|
|
275
|
+
apps = app_results['apps']
|
|
276
|
+
if apps.empty?
|
|
277
|
+
print_red_alert "App not found by name #{name}"
|
|
278
|
+
exit 1
|
|
279
|
+
elsif apps.size > 1
|
|
280
|
+
print_red_alert "#{apps.size} apps exist with the name #{name}. Try using id instead"
|
|
281
|
+
exit 1
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
return app_results['apps'][0]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def find_app_by_name_or_id(val)
|
|
288
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
289
|
+
return find_app_by_id(val)
|
|
290
|
+
else
|
|
291
|
+
return find_app_by_name(val)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
## servers
|
|
296
|
+
|
|
297
|
+
def find_server_by_id(id)
|
|
298
|
+
begin
|
|
299
|
+
json_response = servers_interface.get(id.to_i)
|
|
300
|
+
return json_response['server']
|
|
301
|
+
rescue RestClient::Exception => e
|
|
302
|
+
if e.response && e.response.code == 404
|
|
303
|
+
print_red_alert "Server not found by id #{id}"
|
|
304
|
+
exit 1
|
|
305
|
+
else
|
|
306
|
+
raise e
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def find_server_by_name(name)
|
|
312
|
+
results = servers_interface.list({name: name})
|
|
313
|
+
if results['servers'].empty?
|
|
314
|
+
print_red_alert "Server not found by name #{name}"
|
|
315
|
+
exit 1
|
|
316
|
+
elsif results['servers'].size > 1
|
|
317
|
+
print_red_alert "Multiple servers exist with the name #{name}. Try using id instead"
|
|
318
|
+
exit 1
|
|
319
|
+
end
|
|
320
|
+
return results['servers'][0]
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def find_server_by_name_or_id(val)
|
|
324
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
325
|
+
return find_server_by_id(val)
|
|
326
|
+
else
|
|
327
|
+
return find_server_by_name(val)
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def parse_server_id_list(id_list)
|
|
332
|
+
parse_id_list(id_list).collect do |server_id|
|
|
333
|
+
find_server_by_name_or_id(server_id)['id']
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
## hosts is the same as servers, just says 'Host' instead of 'Server'
|
|
338
|
+
|
|
339
|
+
def find_host_by_id(id)
|
|
340
|
+
begin
|
|
341
|
+
json_response = servers_interface.get(id.to_i)
|
|
342
|
+
return json_response['server']
|
|
343
|
+
rescue RestClient::Exception => e
|
|
344
|
+
if e.response && e.response.code == 404
|
|
345
|
+
print_red_alert "Host not found by id #{id}"
|
|
346
|
+
exit 1
|
|
347
|
+
else
|
|
348
|
+
raise e
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def find_host_by_name(name)
|
|
354
|
+
results = servers_interface.list({name: name})
|
|
355
|
+
if results['servers'].empty?
|
|
356
|
+
print_red_alert "Host not found by name #{name}"
|
|
357
|
+
exit 1
|
|
358
|
+
elsif results['servers'].size > 1
|
|
359
|
+
print_red_alert "#{results['servers'].size} hosts exist with the name #{name}. Try using id instead"
|
|
360
|
+
exit 1
|
|
361
|
+
end
|
|
362
|
+
return results['servers'][0]
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def find_host_by_name_or_id(val)
|
|
366
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
|
367
|
+
return find_host_by_id(val)
|
|
368
|
+
else
|
|
369
|
+
return find_host_by_name(val)
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def parse_host_id_list(id_list)
|
|
374
|
+
parse_id_list(id_list).collect do |host_id|
|
|
375
|
+
find_host_by_name_or_id(host_id)['id']
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
245
379
|
def find_instance_type_layout_by_id(layout_id, id)
|
|
246
380
|
json_results = instance_type_layouts_interface.get(layout_id, id)
|
|
247
381
|
if json_results['instanceTypeLayout'].empty?
|
|
@@ -416,7 +550,7 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
416
550
|
arbitrary_options.delete('cloud')
|
|
417
551
|
arbitrary_options.delete('type')
|
|
418
552
|
arbitrary_options.delete('name')
|
|
419
|
-
arbitrary_options.delete('version')
|
|
553
|
+
#arbitrary_options.delete('version')
|
|
420
554
|
arbitrary_options.delete('layout')
|
|
421
555
|
arbitrary_options.delete('servicePlan')
|
|
422
556
|
arbitrary_options.delete('description')
|
|
@@ -470,6 +604,8 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
470
604
|
layout_id = nil
|
|
471
605
|
if options[:layout]
|
|
472
606
|
layout_id = options[:layout]
|
|
607
|
+
# elsif options[:options]['layout']
|
|
608
|
+
# layout_id = options[:options]['layout']
|
|
473
609
|
end
|
|
474
610
|
if layout_id.is_a?(Hash)
|
|
475
611
|
layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
|
|
@@ -479,6 +615,8 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
479
615
|
default_layout_value = nil
|
|
480
616
|
if options[:version]
|
|
481
617
|
version_value = options[:version]
|
|
618
|
+
elsif options[:options]['version']
|
|
619
|
+
version_value = options[:options]['version']
|
|
482
620
|
else
|
|
483
621
|
available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
|
|
484
622
|
default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
|
|
@@ -581,8 +719,11 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
581
719
|
end
|
|
582
720
|
end
|
|
583
721
|
#todo: consolidate these, instances api looks for instance.plan.id and apps looks for plan.id
|
|
584
|
-
|
|
585
|
-
|
|
722
|
+
if options[:for_app]
|
|
723
|
+
payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
|
|
724
|
+
else
|
|
725
|
+
payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
|
|
726
|
+
end
|
|
586
727
|
end
|
|
587
728
|
|
|
588
729
|
# build config option types
|
|
@@ -964,7 +1105,6 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
964
1105
|
default_datastore = datastore_options.find {|ds| ds['value'].to_s == volume['datastoreId'].to_s}
|
|
965
1106
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => default_datastore ? default_datastore['name'] : volume['datastoreId']}], options[:options])
|
|
966
1107
|
volume['datastoreId'] = v_prompt[field_context]['datastoreId']
|
|
967
|
-
volume['hasDatastore'] = true
|
|
968
1108
|
end
|
|
969
1109
|
|
|
970
1110
|
volumes << volume
|
|
@@ -1029,7 +1169,6 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
1029
1169
|
if !datastore_options.empty?
|
|
1030
1170
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => volume['datastoreId']}], options[:options])
|
|
1031
1171
|
volume['datastoreId'] = v_prompt[field_context]['datastoreId']
|
|
1032
|
-
volume['hasDatastore'] = true
|
|
1033
1172
|
end
|
|
1034
1173
|
|
|
1035
1174
|
volumes << volume
|
|
@@ -1507,6 +1646,45 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
1507
1646
|
return evars
|
|
1508
1647
|
end
|
|
1509
1648
|
|
|
1649
|
+
# Converts metadata tags array into a string like "name:value, foo:bar"
|
|
1650
|
+
def format_metadata(tags)
|
|
1651
|
+
if tags.nil? || tags.empty?
|
|
1652
|
+
""
|
|
1653
|
+
elsif tags.instance_of?(Array)
|
|
1654
|
+
tags.collect {|tag| "#{tag['name']}: #{tag['value']}" }.sort.join(", ")
|
|
1655
|
+
elsif tags.instance_of?(Hash)
|
|
1656
|
+
tags.collect {|k,v| "#{k}: #{v}" }.sort.join(", ")
|
|
1657
|
+
else
|
|
1658
|
+
tags.to_s
|
|
1659
|
+
end
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
# Parses metadata tags object (string) into an array
|
|
1663
|
+
def parse_metadata(val)
|
|
1664
|
+
metadata = nil
|
|
1665
|
+
if val
|
|
1666
|
+
if val == "[]" || val == "null"
|
|
1667
|
+
metadata = []
|
|
1668
|
+
elsif val.is_a?(Array)
|
|
1669
|
+
metadata = val
|
|
1670
|
+
else
|
|
1671
|
+
# parse string into format name:value, name:value
|
|
1672
|
+
# merge IDs from current metadata
|
|
1673
|
+
# todo: should allow quoted semicolons..
|
|
1674
|
+
metadata_list = val.to_s.split(",").select {|it| !it.to_s.empty? }
|
|
1675
|
+
metadata_list = metadata_list.collect do |it|
|
|
1676
|
+
metadata_pair = it.include?(":") ? it.split(":") : it.split("=")
|
|
1677
|
+
row = {}
|
|
1678
|
+
row['name'] = metadata_pair[0].to_s.strip
|
|
1679
|
+
row['value'] = metadata_pair[1].to_s.strip
|
|
1680
|
+
row
|
|
1681
|
+
end
|
|
1682
|
+
metadata = metadata_list
|
|
1683
|
+
end
|
|
1684
|
+
end
|
|
1685
|
+
return metadata
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1510
1688
|
# Prompts user for environment variables for new instance
|
|
1511
1689
|
# returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
|
|
1512
1690
|
def prompt_metadata(options={})
|
|
@@ -1712,8 +1890,12 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
1712
1890
|
end
|
|
1713
1891
|
|
|
1714
1892
|
if !options[:groupAccessList].empty?
|
|
1715
|
-
group_access = options[:groupAccessList].collect {|site_id|
|
|
1716
|
-
|
|
1893
|
+
group_access = options[:groupAccessList].collect {|site_id|
|
|
1894
|
+
found_group = find_group_by_name_or_id_for_provisioning(site_id)
|
|
1895
|
+
return 1, "group not found by #{site_id}" if found_group.nil?
|
|
1896
|
+
{'id' => found_group['id']}
|
|
1897
|
+
} || []
|
|
1898
|
+
elsif !options[:no_prompt] && !all_groups
|
|
1717
1899
|
available_groups = options[:available_groups] || get_available_groups
|
|
1718
1900
|
|
|
1719
1901
|
if available_groups.empty?
|
|
@@ -1806,19 +1988,20 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
1806
1988
|
|
|
1807
1989
|
permissions = {'resourcePermissions' => resource_perms}
|
|
1808
1990
|
|
|
1809
|
-
available_accounts = get_available_accounts
|
|
1991
|
+
available_accounts = get_available_accounts() #.collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
|
1810
1992
|
accounts = []
|
|
1811
1993
|
|
|
1812
1994
|
# Prompts for multi tenant
|
|
1813
1995
|
if available_accounts.count > 1
|
|
1814
1996
|
visibility = options[:visibility]
|
|
1997
|
+
if !excludes.include?('visibility')
|
|
1998
|
+
if !visibility && !options[:no_prompt]
|
|
1999
|
+
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']
|
|
2000
|
+
end
|
|
1815
2001
|
|
|
1816
|
-
|
|
1817
|
-
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']
|
|
2002
|
+
permissions['resourcePool'] = {'visibility' => visibility} if visibility
|
|
1818
2003
|
end
|
|
1819
2004
|
|
|
1820
|
-
permissions['resourcePool'] = {'visibility' => visibility} if visibility
|
|
1821
|
-
|
|
1822
2005
|
# Tenants
|
|
1823
2006
|
if !excludes.include?('tenants')
|
|
1824
2007
|
if !options[:tenants].nil?
|
|
@@ -1980,4 +2163,30 @@ module Morpheus::Cli::ProvisioningHelper
|
|
|
1980
2163
|
|
|
1981
2164
|
return ports
|
|
1982
2165
|
end
|
|
2166
|
+
|
|
2167
|
+
def format_blueprint_type(type_code)
|
|
2168
|
+
return type_code.to_s # just show it as is
|
|
2169
|
+
if type_code.to_s.empty?
|
|
2170
|
+
type_code = "morpheus"
|
|
2171
|
+
end
|
|
2172
|
+
if type_code.to_s.downcase == "arm"
|
|
2173
|
+
"ARM"
|
|
2174
|
+
else
|
|
2175
|
+
return type_code.to_s.capitalize
|
|
2176
|
+
end
|
|
2177
|
+
end
|
|
2178
|
+
|
|
2179
|
+
def parse_blueprint_type(type_code)
|
|
2180
|
+
return type_code.to_s # just use it as is
|
|
2181
|
+
# if type_code.to_s.empty?
|
|
2182
|
+
# type_code = "morpheus"
|
|
2183
|
+
# end
|
|
2184
|
+
if type_code.to_s.downcase == "arm"
|
|
2185
|
+
"arm"
|
|
2186
|
+
elsif type_code.to_s.downcase == "cloudformation"
|
|
2187
|
+
type_code = "cloudFormation"
|
|
2188
|
+
else
|
|
2189
|
+
return type_code.to_s.downcase
|
|
2190
|
+
end
|
|
2191
|
+
end
|
|
1983
2192
|
end
|