morpheus-cli 5.3.2 → 5.3.2.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/accounts_interface.rb +4 -30
- data/lib/morpheus/api/api_client.rb +12 -0
- data/lib/morpheus/api/load_balancer_pools_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_types_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +9 -0
- data/lib/morpheus/api/load_balancers_interface.rb +4 -53
- data/lib/morpheus/api/network_routers_interface.rb +56 -0
- data/lib/morpheus/api/secondary_read_interface.rb +25 -0
- data/lib/morpheus/api/secondary_rest_interface.rb +42 -0
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/cli/cli_command.rb +9 -9
- data/lib/morpheus/cli/instances.rb +33 -33
- data/lib/morpheus/cli/load_balancer_types.rb +37 -0
- data/lib/morpheus/cli/load_balancers.rb +149 -314
- data/lib/morpheus/cli/log_settings_command.rb +7 -3
- data/lib/morpheus/cli/mixins/load_balancers_helper.rb +156 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +11 -0
- data/lib/morpheus/cli/mixins/rest_command.rb +657 -0
- data/lib/morpheus/cli/network_routers_command.rb +1183 -185
- data/lib/morpheus/cli/networks_command.rb +194 -101
- data/lib/morpheus/cli/option_types.rb +29 -39
- data/lib/morpheus/cli/tenants_command.rb +18 -20
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/ext/string.rb +41 -0
- data/lib/morpheus/formatters.rb +4 -0
- metadata +11 -2
@@ -6,11 +6,12 @@ class Morpheus::Cli::LogSettingsCommand
|
|
6
6
|
|
7
7
|
set_command_name :'log-settings'
|
8
8
|
|
9
|
-
register_subcommands :get, :update
|
10
|
-
register_subcommands :enable_integration, :disable_integration, :remove_integration
|
9
|
+
register_subcommands :get, :update
|
11
10
|
register_subcommands :add_syslog_rule, :remove_syslog_rule
|
12
11
|
|
13
|
-
|
12
|
+
# these command are deprecated in 5.3.3 and can be removed
|
13
|
+
register_subcommands :enable_integration, :disable_integration, :remove_integration
|
14
|
+
set_subcommands_hidden :enable_integration, :disable_integration, :remove_integration
|
14
15
|
|
15
16
|
def connect(opts)
|
16
17
|
@api_client = establish_remote_appliance_connection(opts)
|
@@ -186,6 +187,7 @@ class Morpheus::Cli::LogSettingsCommand
|
|
186
187
|
end
|
187
188
|
|
188
189
|
def enable_integration(args)
|
190
|
+
print_error yellow,"[DEPRECATED] The command `#{command_name} enable-integration` is deprecated.",reset,"\n"
|
189
191
|
options = {}
|
190
192
|
|
191
193
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
@@ -240,6 +242,7 @@ class Morpheus::Cli::LogSettingsCommand
|
|
240
242
|
end
|
241
243
|
|
242
244
|
def disable_integration(args)
|
245
|
+
print_error yellow,"[DEPRECATED] The command `#{command_name} disable-integration` is deprecated.",reset,"\n"
|
243
246
|
options = {}
|
244
247
|
|
245
248
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
@@ -285,6 +288,7 @@ class Morpheus::Cli::LogSettingsCommand
|
|
285
288
|
end
|
286
289
|
|
287
290
|
def remove_integration(args)
|
291
|
+
print_error yellow,"[DEPRECATED] The command `#{command_name } remove-integration` is deprecated.",reset,"\n"
|
288
292
|
options = {}
|
289
293
|
|
290
294
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
@@ -0,0 +1,156 @@
|
|
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 load balancer management
|
6
|
+
# including load balancers, load balancer types, virtual servers, etc.
|
7
|
+
module Morpheus::Cli::LoadBalancersHelper
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
klass.send :include, Morpheus::Cli::PrintHelper
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_balancers_interface
|
14
|
+
# @api_client.load_balancers
|
15
|
+
raise "#{self.class} has not defined @load_balancers_interface" if @load_balancers_interface.nil?
|
16
|
+
@load_balancers_interface
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_balancer_types_interface
|
20
|
+
# @api_client.load_balancer_types
|
21
|
+
raise "#{self.class} has not defined @load_balancer_types_interface" if @load_balancer_types_interface.nil?
|
22
|
+
@load_balancer_types_interface
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_balancer_object_key
|
26
|
+
'loadBalancer'
|
27
|
+
end
|
28
|
+
|
29
|
+
def load_balancer_list_key
|
30
|
+
'loadBalancers'
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_balancer_label
|
34
|
+
'Load Balancer'
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_balancer_plural_label
|
38
|
+
'Load Balancers'
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_balancer_type_object_key
|
42
|
+
'loadBalancerType'
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_balancer_type_list_key
|
46
|
+
'loadBalancerTypes'
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_balancer_type_label
|
50
|
+
'Load Balancer Type'
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_balancer_type_plural_label
|
54
|
+
'Load Balancer Types'
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_load_balancer_by_name_or_id(val)
|
58
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
59
|
+
return find_load_balancer_by_id(val)
|
60
|
+
else
|
61
|
+
return find_load_balancer_by_name(val)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_load_balancer_by_id(id)
|
66
|
+
begin
|
67
|
+
json_response = load_balancers_interface.get(id.to_i)
|
68
|
+
return json_response[load_balancer_object_key]
|
69
|
+
rescue RestClient::Exception => e
|
70
|
+
if e.response && e.response.code == 404
|
71
|
+
print_red_alert "Load Balancer not found by id #{id}"
|
72
|
+
else
|
73
|
+
raise e
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_load_balancer_by_name(name)
|
79
|
+
lbs = load_balancers_interface.list({name: name.to_s})[load_balancer_list_key]
|
80
|
+
if lbs.empty?
|
81
|
+
print_red_alert "Load Balancer not found by name #{name}"
|
82
|
+
return nil
|
83
|
+
elsif lbs.size > 1
|
84
|
+
print_red_alert "#{lbs.size} load balancers found by name #{name}"
|
85
|
+
#print_lbs_table(lbs, {color: red})
|
86
|
+
print reset,"\n\n"
|
87
|
+
return nil
|
88
|
+
else
|
89
|
+
return lbs[0]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_available_load_balancer_types(refresh=false)
|
94
|
+
if !@available_load_balancer_types || refresh
|
95
|
+
@available_load_balancer_types = load_balancer_types_interface.list({max:1000})[load_balancer_type_list_key]
|
96
|
+
end
|
97
|
+
return @available_load_balancer_types
|
98
|
+
end
|
99
|
+
|
100
|
+
def load_balancer_type_for_name_or_id(val)
|
101
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
102
|
+
return load_balancer_type_for_id(val)
|
103
|
+
else
|
104
|
+
return load_balancer_type_for_name(val)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def load_balancer_type_for_id(id)
|
109
|
+
return get_available_load_balancer_types().find { |z| z['id'].to_i == id.to_i}
|
110
|
+
end
|
111
|
+
|
112
|
+
def load_balancer_type_for_name(name)
|
113
|
+
return get_available_load_balancer_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
|
114
|
+
end
|
115
|
+
|
116
|
+
def find_load_balancer_type_by_name_or_id(val)
|
117
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
118
|
+
return find_load_balancer_type_by_id(val)
|
119
|
+
else
|
120
|
+
return find_load_balancer_type_by_name(val)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_load_balancer_type_by_id(id)
|
125
|
+
begin
|
126
|
+
json_response = load_balancer_types_interface.get(id.to_i)
|
127
|
+
return json_response[load_balancer_type_object_key]
|
128
|
+
rescue RestClient::Exception => e
|
129
|
+
if e.response && e.response.code == 404
|
130
|
+
print_red_alert "Load Balancer Type not found by id #{id}"
|
131
|
+
return nil
|
132
|
+
else
|
133
|
+
raise e
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def find_load_balancer_type_by_name(name)
|
139
|
+
json_response = load_balancer_types_interface.list({name: name.to_s})
|
140
|
+
load_balancer_types = json_response[load_balancer_type_list_key]
|
141
|
+
if load_balancer_types.empty?
|
142
|
+
print_red_alert "Load Balancer Type not found by name #{name}"
|
143
|
+
return load_balancer_types
|
144
|
+
elsif load_balancer_types.size > 1
|
145
|
+
print_red_alert "#{load_balancer_types.size} load balancer types found by name #{name}"
|
146
|
+
rows = load_balancer_types.collect do |it|
|
147
|
+
{id: it['id'], name: it['name']}
|
148
|
+
end
|
149
|
+
puts as_pretty_table(rows, [:id, :name], {color:red})
|
150
|
+
return nil
|
151
|
+
else
|
152
|
+
return load_balancer_types[0]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -1335,4 +1335,15 @@ module Morpheus::Cli::PrintHelper
|
|
1335
1335
|
parse_json_or_yaml(config, parsers)
|
1336
1336
|
end
|
1337
1337
|
|
1338
|
+
def format_option_types_table(option_types, options={}, domain_name=nil)
|
1339
|
+
columns = [
|
1340
|
+
{"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
|
1341
|
+
{"FIELD NAME" => lambda {|it| [it['fieldContext'] == domain_name ? nil : it['fieldContext'], it['fieldName']].select {|it| !it.to_s.empty? }.join('.') } },
|
1342
|
+
{"TYPE" => lambda {|it| it['type'] } },
|
1343
|
+
{"DEFAULT" => lambda {|it| it['defaultValue'] } },
|
1344
|
+
{"REQUIRED" => lambda {|it| format_boolean it['required'] } },
|
1345
|
+
]
|
1346
|
+
as_pretty_table(option_types, columns, options)
|
1347
|
+
end
|
1348
|
+
|
1338
1349
|
end
|
@@ -0,0 +1,657 @@
|
|
1
|
+
# RestCommand is a mixin for Morpheus::Cli command classes.
|
2
|
+
# Provides basic CRUD commands: list, get, add, update, remove
|
3
|
+
# Currently the command class must also include Morpheus::Cli::CliCommand
|
4
|
+
# The command class can define a few variables to dictate what the resource
|
5
|
+
# is called and the the api interface used to fetch the records. The command class
|
6
|
+
# or helper must also provide several methods to provide the default behavior.
|
7
|
+
# In the example below, the command (helper) defines the following methods:
|
8
|
+
# * load_balancer_object_key() - Key name of object returned by the "get" api endpoint.
|
9
|
+
# * load_balancer_list_key() - Key name of array of records returned by the "list" api endpoint.
|
10
|
+
# * load_balancer_column_definitions() - Column definitions for the "get" command display output.
|
11
|
+
# * load_balancer_list_column_definitions() - Column definitions for the "list" command display output.
|
12
|
+
#
|
13
|
+
# # An example of a RestCommand for `morpheus load-balancers`.
|
14
|
+
# class Morpheus::Cli::LoadBalancers
|
15
|
+
#
|
16
|
+
# include Morpheus::Cli::CliCommand
|
17
|
+
# include Morpheus::Cli::RestCommand
|
18
|
+
# include Morpheus::Cli::LoadBalancersHelper
|
19
|
+
#
|
20
|
+
# # All of the example settings below are redundant
|
21
|
+
# # and would be the default values if not set.
|
22
|
+
# set_rest_name :load_balancers
|
23
|
+
# set_rest_label "Load Balancer"
|
24
|
+
# set_rest_plural_label "Load Balancers"
|
25
|
+
# set_rest_object_key "load_balancer"
|
26
|
+
# set_rest_has_type true
|
27
|
+
# set_rest_type "load_balancer_types"
|
28
|
+
# register_interfaces :load_balancers, :load_balancer_types
|
29
|
+
#
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
module Morpheus::Cli::RestCommand
|
33
|
+
def self.included(base)
|
34
|
+
#puts "including RestCommand for #{base}"
|
35
|
+
#base.send :include, Morpheus::Cli::CliCommand
|
36
|
+
base.extend ClassMethods
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
|
41
|
+
# rest_name is the plural name of the rest command resource eg. NeatThingsCommand would be "neat_things"
|
42
|
+
# It is used to derive all other default rest settings key, label, etc.
|
43
|
+
# The default name the command name with underscores `_` instead of dashes `-`.
|
44
|
+
def rest_name
|
45
|
+
@rest_name || default_rest_name
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_rest_name
|
49
|
+
self.command_name.to_s.gsub("-", "_")
|
50
|
+
end
|
51
|
+
|
52
|
+
def rest_name=(v)
|
53
|
+
@rest_name = v
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :set_rest_name :rest_name=
|
57
|
+
|
58
|
+
# rest_key is the singular name of the resource eg. "neat_thing"
|
59
|
+
def rest_key
|
60
|
+
@rest_key || default_rest_key
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_rest_key
|
64
|
+
rest_name.chomp("s")
|
65
|
+
end
|
66
|
+
|
67
|
+
def rest_key=(v)
|
68
|
+
@rest_key = v
|
69
|
+
end
|
70
|
+
|
71
|
+
alias :set_rest_key :rest_key=
|
72
|
+
|
73
|
+
# rest_arg is a label for the arg in the command usage eg. "thing" gets displayed as [thing]
|
74
|
+
def rest_arg
|
75
|
+
@rest_arg || default_rest_arg
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_rest_arg
|
79
|
+
rest_key.gsub("_", " ") # "[" + rest_key.gsub("_", " ") + "]"
|
80
|
+
end
|
81
|
+
|
82
|
+
def rest_arg=(v)
|
83
|
+
@rest_arg = v
|
84
|
+
end
|
85
|
+
|
86
|
+
alias :set_rest_arg :rest_arg=
|
87
|
+
|
88
|
+
# rest_label is the capitalized resource label eg. "Neat Thing"
|
89
|
+
def rest_label
|
90
|
+
@rest_label || default_rest_label
|
91
|
+
end
|
92
|
+
|
93
|
+
def default_rest_label
|
94
|
+
rest_key.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
|
95
|
+
end
|
96
|
+
|
97
|
+
def rest_label=(v)
|
98
|
+
@rest_label = v
|
99
|
+
end
|
100
|
+
|
101
|
+
alias :set_rest_label :rest_label=
|
102
|
+
|
103
|
+
# the plural version of the label eg. "Neat Things"
|
104
|
+
def rest_plural_label
|
105
|
+
@rest_plural_label || default_rest_plural_label
|
106
|
+
end
|
107
|
+
|
108
|
+
def default_rest_plural_label
|
109
|
+
#rest_name.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
|
110
|
+
rest_label.to_s.pluralize
|
111
|
+
end
|
112
|
+
|
113
|
+
def rest_plural_label=(v)
|
114
|
+
@rest_plural_label = v
|
115
|
+
end
|
116
|
+
|
117
|
+
alias :set_rest_plural_label :rest_plural_label=
|
118
|
+
|
119
|
+
# rest_interface_name is the interface name for the resource. eg. "neat_things"
|
120
|
+
def rest_interface_name
|
121
|
+
@rest_interface_name || default_rest_interface_name
|
122
|
+
end
|
123
|
+
|
124
|
+
def default_rest_interface_name
|
125
|
+
rest_name
|
126
|
+
end
|
127
|
+
|
128
|
+
def rest_interface_name=(v)
|
129
|
+
@rest_interface_name = v
|
130
|
+
end
|
131
|
+
|
132
|
+
alias :set_rest_interface_name :rest_interface_name=
|
133
|
+
|
134
|
+
# rest_has_type indicates a resource has a type. default is false
|
135
|
+
def rest_has_type
|
136
|
+
@rest_has_type == true
|
137
|
+
end
|
138
|
+
|
139
|
+
def default_rest_has_type
|
140
|
+
false
|
141
|
+
end
|
142
|
+
|
143
|
+
def rest_has_type=(v)
|
144
|
+
@rest_has_type = !!v
|
145
|
+
end
|
146
|
+
|
147
|
+
alias :set_rest_has_type :rest_has_type=
|
148
|
+
|
149
|
+
## duplicated the rest_* settings with rest_type, for the types resource
|
150
|
+
|
151
|
+
# rest_type_name is the rest_name for the type, only applicable if rest_has_type
|
152
|
+
def rest_type_name
|
153
|
+
@rest_type_name || default_rest_type_name
|
154
|
+
end
|
155
|
+
|
156
|
+
def default_rest_type_name
|
157
|
+
rest_key + "_types"
|
158
|
+
end
|
159
|
+
|
160
|
+
def rest_type_name=(v)
|
161
|
+
@rest_type_name = v
|
162
|
+
end
|
163
|
+
|
164
|
+
alias :set_rest_type_name :rest_type_name=
|
165
|
+
alias :set_rest_type :rest_type_name=
|
166
|
+
#alias :rest_type= :rest_type_name=
|
167
|
+
|
168
|
+
# rest_type_key is the singular name of the resource eg. "neat_thing"
|
169
|
+
def rest_type_key
|
170
|
+
@rest_type_key || default_rest_type_key
|
171
|
+
end
|
172
|
+
|
173
|
+
def default_rest_type_key
|
174
|
+
rest_type_name.chomp("s")
|
175
|
+
end
|
176
|
+
|
177
|
+
def rest_type_key=(v)
|
178
|
+
@rest_type_key = v
|
179
|
+
end
|
180
|
+
|
181
|
+
alias :set_rest_type_key :rest_type_key=
|
182
|
+
|
183
|
+
def rest_type_arg
|
184
|
+
@rest_type_arg || default_rest_type_arg
|
185
|
+
end
|
186
|
+
|
187
|
+
def default_rest_type_arg
|
188
|
+
# rest_type_key.gsub("_", " ") # "[" + rest_key.gsub("_", " ") + "]"
|
189
|
+
"type" # [type]
|
190
|
+
end
|
191
|
+
|
192
|
+
def rest_type_arg=(v)
|
193
|
+
@rest_type_arg = v
|
194
|
+
end
|
195
|
+
|
196
|
+
alias :set_rest_type_arg :rest_type_arg=
|
197
|
+
|
198
|
+
# rest_type_label is the capitalized resource label eg. "Neat Thing"
|
199
|
+
def rest_type_label
|
200
|
+
@rest_type_label || default_rest_type_label
|
201
|
+
end
|
202
|
+
|
203
|
+
def default_rest_type_label
|
204
|
+
rest_type_key.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
|
205
|
+
end
|
206
|
+
|
207
|
+
def rest_type_label=(v)
|
208
|
+
@rest_type_label = v
|
209
|
+
end
|
210
|
+
|
211
|
+
alias :set_rest_type_label :rest_type_label=
|
212
|
+
|
213
|
+
# the plural version of the label eg. "Neat Things"
|
214
|
+
def rest_type_plural_label
|
215
|
+
@rest_type_plural_label || default_rest_type_plural_label
|
216
|
+
end
|
217
|
+
|
218
|
+
def default_rest_type_plural_label
|
219
|
+
#rest_type_name.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
|
220
|
+
rest_type_label.to_s.pluralize
|
221
|
+
end
|
222
|
+
|
223
|
+
def rest_type_plural_label=(v)
|
224
|
+
@rest_type_plural_label = v
|
225
|
+
end
|
226
|
+
|
227
|
+
alias :set_rest_type_plural_label :rest_type_plural_label=
|
228
|
+
|
229
|
+
# the name of the default interface, matches the rest name eg. "neat_things"
|
230
|
+
def rest_type_interface_name
|
231
|
+
@rest_type_interface_name || default_rest_type_interface_name
|
232
|
+
end
|
233
|
+
|
234
|
+
def default_rest_type_interface_name
|
235
|
+
rest_type_name
|
236
|
+
end
|
237
|
+
|
238
|
+
def rest_type_interface_name=(v)
|
239
|
+
@rest_type_interface_name = v
|
240
|
+
end
|
241
|
+
|
242
|
+
alias :set_rest_type_interface_name :rest_type_interface_name=
|
243
|
+
|
244
|
+
# set or append to the list of interface names to register for this command
|
245
|
+
# The registered interfaces will be pre-loaded into instance variables
|
246
|
+
# eg. [:neat_things, :neat_thing_types] will instantiate @neat_things_interface and @neat_thing_types_interface
|
247
|
+
def register_interfaces(*interfaces)
|
248
|
+
@registered_interfaces ||= []
|
249
|
+
interfaces.flatten.each do |it|
|
250
|
+
@registered_interfaces << it.to_sym
|
251
|
+
end
|
252
|
+
# put the default rest_interface first
|
253
|
+
if rest_interface_name && !@registered_interfaces.include?(rest_interface_name)
|
254
|
+
@registered_interfaces.unshift(rest_interface_name)
|
255
|
+
end
|
256
|
+
# and also the rest_type_interface
|
257
|
+
if rest_has_type && !@registered_interfaces.include?(rest_type_interface_name)
|
258
|
+
@registered_interfaces.unshift(rest_type_interface_name)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
alias :register_interface :register_interfaces
|
263
|
+
|
264
|
+
# get list of interface names that are registered for this command
|
265
|
+
# automatically includes the interface for the rest_name and rest_type_name if has_type
|
266
|
+
def registered_interfaces
|
267
|
+
@registered_interfaces ||= []
|
268
|
+
# put the default rest_interface first
|
269
|
+
if @registered_interfaces.empty?
|
270
|
+
if rest_interface_name
|
271
|
+
@registered_interfaces.unshift(rest_interface_name)
|
272
|
+
end
|
273
|
+
if rest_has_type
|
274
|
+
@registered_interfaces.unshift(rest_type_interface_name)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
@registered_interfaces
|
278
|
+
end
|
279
|
+
|
280
|
+
# clear the list of registered interfaces, perhaps useful in a command subclass
|
281
|
+
def clear_registered_interfaces()
|
282
|
+
@registered_interfaces = []
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
def rest_name
|
288
|
+
self.class.rest_name
|
289
|
+
end
|
290
|
+
|
291
|
+
def rest_key
|
292
|
+
self.class.rest_key
|
293
|
+
end
|
294
|
+
|
295
|
+
def rest_arg
|
296
|
+
self.class.rest_arg
|
297
|
+
end
|
298
|
+
|
299
|
+
def rest_label
|
300
|
+
self.class.rest_label
|
301
|
+
end
|
302
|
+
|
303
|
+
def rest_plural_label
|
304
|
+
self.class.rest_plural_label
|
305
|
+
end
|
306
|
+
|
307
|
+
def rest_interface_name
|
308
|
+
self.class.rest_interface_name
|
309
|
+
end
|
310
|
+
|
311
|
+
# returns the default rest interface, allows using rest_interface_name = "your"
|
312
|
+
# or override this method to return @your_interface if needed
|
313
|
+
def rest_interface
|
314
|
+
instance_variable_get("@#{rest_interface_name}_interface")
|
315
|
+
end
|
316
|
+
|
317
|
+
def rest_object_key
|
318
|
+
self.send("#{rest_key}_object_key")
|
319
|
+
end
|
320
|
+
|
321
|
+
def rest_list_key
|
322
|
+
self.send("#{rest_key}_list_key")
|
323
|
+
end
|
324
|
+
|
325
|
+
def rest_column_definitions
|
326
|
+
self.send("#{rest_key}_column_definitions")
|
327
|
+
end
|
328
|
+
|
329
|
+
def rest_list_column_definitions
|
330
|
+
self.send("#{rest_key}_list_column_definitions")
|
331
|
+
end
|
332
|
+
|
333
|
+
def rest_find_by_name_or_id(name)
|
334
|
+
return self.send("find_#{rest_key}_by_name_or_id", name)
|
335
|
+
end
|
336
|
+
|
337
|
+
def rest_has_type
|
338
|
+
self.class.rest_has_type
|
339
|
+
end
|
340
|
+
|
341
|
+
## duplicated the rest_* settings with rest_type, for the types resource
|
342
|
+
|
343
|
+
def rest_type_name
|
344
|
+
self.class.rest_type_name
|
345
|
+
end
|
346
|
+
|
347
|
+
def rest_type_key
|
348
|
+
self.class.rest_type_key
|
349
|
+
end
|
350
|
+
|
351
|
+
def rest_type_arg
|
352
|
+
self.class.rest_type_arg
|
353
|
+
end
|
354
|
+
|
355
|
+
def rest_type_label
|
356
|
+
self.class.rest_type_label
|
357
|
+
end
|
358
|
+
|
359
|
+
def rest_type_plural_label
|
360
|
+
self.class.rest_type_plural_label
|
361
|
+
end
|
362
|
+
|
363
|
+
def rest_type_interface_name
|
364
|
+
self.class.rest_type_interface_name # || "@#{rest_type_name}_interface"
|
365
|
+
end
|
366
|
+
|
367
|
+
def rest_type_interface
|
368
|
+
instance_variable_get("@#{rest_type_interface_name}_interface")
|
369
|
+
end
|
370
|
+
|
371
|
+
def rest_type_object_key
|
372
|
+
self.send("#{rest_type_key}_object_key")
|
373
|
+
end
|
374
|
+
|
375
|
+
def rest_type_list_key
|
376
|
+
self.send("#{rest_type_key}_list_key")
|
377
|
+
end
|
378
|
+
|
379
|
+
def rest_type_column_definitions
|
380
|
+
self.send("#{rest_type_key}_column_definitions")
|
381
|
+
end
|
382
|
+
|
383
|
+
def rest_type_list_column_definitions
|
384
|
+
self.send("#{rest_type_key}_list_column_definitions")
|
385
|
+
end
|
386
|
+
|
387
|
+
def rest_type_find_by_name_or_id(name)
|
388
|
+
return self.send("find_#{rest_type_key}_by_name_or_id", name)
|
389
|
+
end
|
390
|
+
|
391
|
+
def registered_interfaces
|
392
|
+
self.class.registered_interfaces
|
393
|
+
end
|
394
|
+
|
395
|
+
# standard connect method to establish @api_client
|
396
|
+
# and @{name}_interface variables for each registered interface.
|
397
|
+
def connect(options)
|
398
|
+
@api_client = establish_remote_appliance_connection(options)
|
399
|
+
self.class.registered_interfaces.each do |interface_name|
|
400
|
+
if interface_name.is_a?(String) || interface_name.is_a?(Symbol)
|
401
|
+
instance_variable_set("@#{interface_name}_interface", @api_client.send(interface_name))
|
402
|
+
elsif interface_name.is_a?(Hash)
|
403
|
+
interface_name.each do |k,v|
|
404
|
+
instance_variable_set("#{k}_interface", @api_client.send(v))
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def handle(args)
|
411
|
+
handle_subcommand(args)
|
412
|
+
end
|
413
|
+
|
414
|
+
def list(args)
|
415
|
+
params = {}
|
416
|
+
options = {}
|
417
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
418
|
+
opts.banner = subcommand_usage()
|
419
|
+
build_standard_list_options(opts, options)
|
420
|
+
opts.footer = "List #{rest_plural_label.downcase}."
|
421
|
+
end
|
422
|
+
optparse.parse!(args)
|
423
|
+
connect(options)
|
424
|
+
if args.count > 0
|
425
|
+
options[:phrase] = args.join(" ")
|
426
|
+
end
|
427
|
+
params.merge!(parse_list_options(options))
|
428
|
+
rest_interface.setopts(options)
|
429
|
+
if options[:dry_run]
|
430
|
+
print_dry_run rest_interface.dry.list(params)
|
431
|
+
return
|
432
|
+
end
|
433
|
+
json_response = rest_interface.list(params)
|
434
|
+
render_response(json_response, options, rest_list_key) do
|
435
|
+
records = json_response[rest_list_key]
|
436
|
+
print_h1 "Morpheus #{rest_plural_label}"
|
437
|
+
if records.nil? || records.empty?
|
438
|
+
print cyan,"No #{rest_plural_label.downcase} found.",reset,"\n"
|
439
|
+
else
|
440
|
+
print as_pretty_table(records, rest_list_column_definitions.upcase_keys!, options)
|
441
|
+
print_results_pagination(json_response) if json_response['meta']
|
442
|
+
end
|
443
|
+
print reset,"\n"
|
444
|
+
end
|
445
|
+
return 0, nil
|
446
|
+
end
|
447
|
+
|
448
|
+
def get(args)
|
449
|
+
params = {}
|
450
|
+
options = {}
|
451
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
452
|
+
opts.banner = subcommand_usage("[type]")
|
453
|
+
build_standard_get_options(opts, options)
|
454
|
+
opts.footer = <<-EOT
|
455
|
+
Get details about #{a_or_an(rest_label)} #{rest_label.downcase}.
|
456
|
+
[#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
|
457
|
+
EOT
|
458
|
+
end
|
459
|
+
optparse.parse!(args)
|
460
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
461
|
+
connect(options)
|
462
|
+
params.merge!(parse_query_options(options))
|
463
|
+
id_list = parse_id_list(args)
|
464
|
+
return run_command_for_each_arg(id_list) do |arg|
|
465
|
+
_get(arg, params, options)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def _get(id, params, options)
|
470
|
+
if id !~ /\A\d{1,}\Z/
|
471
|
+
record = rest_find_by_name_or_id(id)
|
472
|
+
if record.nil?
|
473
|
+
raise_command_error "#{rest_label} not found for name '#{id}'"
|
474
|
+
end
|
475
|
+
id = record['id']
|
476
|
+
end
|
477
|
+
rest_interface.setopts(options)
|
478
|
+
if options[:dry_run]
|
479
|
+
print_dry_run rest_interface.dry.get(id, params)
|
480
|
+
return
|
481
|
+
end
|
482
|
+
json_response = rest_interface.get(id, params)
|
483
|
+
render_response_for_get(json_response, options)
|
484
|
+
return 0, nil
|
485
|
+
end
|
486
|
+
|
487
|
+
def render_response_for_get(json_response, options)
|
488
|
+
render_response(json_response, options, rest_object_key) do
|
489
|
+
record = json_response[rest_object_key]
|
490
|
+
print_h1 rest_label, [], options
|
491
|
+
print cyan
|
492
|
+
print_description_list(rest_column_definitions, record, options)
|
493
|
+
# show config settings...
|
494
|
+
if record['optionTypes'] && record['optionTypes'].size > 0
|
495
|
+
print_h2 "Option Types", options
|
496
|
+
print format_option_types_table(record['optionTypes'], options, rest_object_key)
|
497
|
+
end
|
498
|
+
print reset,"\n"
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def add(args)
|
503
|
+
record_type_id = nil
|
504
|
+
options = {}
|
505
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
506
|
+
if rest_has_type
|
507
|
+
opts.banner = subcommand_usage("[name] -t TYPE")
|
508
|
+
opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val|
|
509
|
+
record_type_id = val
|
510
|
+
end
|
511
|
+
else
|
512
|
+
opts.banner = subcommand_usage("[name]")
|
513
|
+
end
|
514
|
+
# if defined?(add_#{rest_key}_option_types)
|
515
|
+
# build_option_type_options(opts, options, add_#{rest_key}_option_types)
|
516
|
+
# end
|
517
|
+
build_standard_add_options(opts, options)
|
518
|
+
end
|
519
|
+
optparse.parse!(args)
|
520
|
+
# todo: make supporting args[0] optional and more flexible
|
521
|
+
# for now args[0] is assumed to be the 'name'
|
522
|
+
record_name = nil
|
523
|
+
if args[0] # && rest_has_name
|
524
|
+
record_name = args[0]
|
525
|
+
end
|
526
|
+
verify_args!(args:args, optparse:optparse, min:0, max: 1)
|
527
|
+
# todo: maybe need a flag to make this required, it could be an option type too, so
|
528
|
+
if rest_has_type
|
529
|
+
if record_type_id.nil?
|
530
|
+
raise_command_error "#{rest_type_label} is required.\n#{optparse}"
|
531
|
+
end
|
532
|
+
end
|
533
|
+
connect(options)
|
534
|
+
if rest_has_type
|
535
|
+
record_type = rest_type_find_by_name_or_id(record_type_id)
|
536
|
+
if record_type.nil?
|
537
|
+
raise_command_error "#{rest_type_label} not found for '#{record_type_id}'.\n#{optparse}"
|
538
|
+
end
|
539
|
+
end
|
540
|
+
passed_options = parse_passed_options(options)
|
541
|
+
payload = {}
|
542
|
+
if options[:payload]
|
543
|
+
payload = options[:payload]
|
544
|
+
payload.deep_merge!({rest_object_key => passed_options})
|
545
|
+
else
|
546
|
+
record_payload = {}
|
547
|
+
if record_name
|
548
|
+
record_payload['name'] = record_name
|
549
|
+
options[:options]['name'] = record_name # injected for prompt
|
550
|
+
end
|
551
|
+
if rest_has_type && record_type
|
552
|
+
# record_payload['type'] = {'code' => record_type['code']}
|
553
|
+
record_payload['type'] = record_type['code']
|
554
|
+
options[:options]['type'] = record_type['code'] # injected for prompt
|
555
|
+
end
|
556
|
+
record_payload.deep_merge!(passed_options)
|
557
|
+
# options by type
|
558
|
+
my_option_types = record_type ? record_type['optionTypes'] : nil
|
559
|
+
if my_option_types && !my_option_types.empty?
|
560
|
+
# remove redundant fieldContext
|
561
|
+
my_option_types.each do |option_type|
|
562
|
+
if option_type['fieldContext'] == rest_object_key
|
563
|
+
option_type['fieldContext'] = nil
|
564
|
+
end
|
565
|
+
end
|
566
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, options[:params])
|
567
|
+
v_prompt.deep_compact!
|
568
|
+
v_prompt.booleanize! # 'on' => true
|
569
|
+
record_payload.deep_merge!(v_prompt)
|
570
|
+
end
|
571
|
+
payload[rest_object_key] = record_payload
|
572
|
+
end
|
573
|
+
rest_interface.setopts(options)
|
574
|
+
if options[:dry_run]
|
575
|
+
print_dry_run rest_interface.dry.create(payload)
|
576
|
+
return
|
577
|
+
end
|
578
|
+
json_response = rest_interface.create(payload)
|
579
|
+
render_response(json_response, options, rest_object_key) do
|
580
|
+
record = json_response[rest_object_key]
|
581
|
+
print_green_success "Added #{rest_label.downcase} #{record['name'] || record['id']}"
|
582
|
+
return _get(record["id"], {}, options)
|
583
|
+
end
|
584
|
+
return 0, nil
|
585
|
+
end
|
586
|
+
|
587
|
+
def update(args)
|
588
|
+
id = args[0]
|
589
|
+
options = {}
|
590
|
+
params = {}
|
591
|
+
account_name = nil
|
592
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
593
|
+
opts.banner = subcommand_usage("[#{rest_arg}] [options]")
|
594
|
+
build_standard_update_options(opts, options)
|
595
|
+
end
|
596
|
+
optparse.parse!(args)
|
597
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
598
|
+
connect(options)
|
599
|
+
record = rest_find_by_name_or_id(id)
|
600
|
+
passed_options = parse_passed_options(options)
|
601
|
+
payload = nil
|
602
|
+
if options[:payload]
|
603
|
+
payload = options[:payload]
|
604
|
+
payload.deep_merge!({rest_object_key => passed_options}) unless passed_options.empty?
|
605
|
+
else
|
606
|
+
record_payload = passed_options
|
607
|
+
if record_payload.empty?
|
608
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
609
|
+
end
|
610
|
+
payload[rest_object_key] = record_payload
|
611
|
+
end
|
612
|
+
rest_interface.setopts(options)
|
613
|
+
if options[:dry_run]
|
614
|
+
print_dry_run rest_interface.dry.update(record['id'], payload)
|
615
|
+
return
|
616
|
+
end
|
617
|
+
json_response = rest_interface.update(record['id'], payload)
|
618
|
+
render_response(json_response, options, rest_object_key) do
|
619
|
+
print_green_success "Updated #{rest_label.downcase} #{record['name'] || record['id']}"
|
620
|
+
_get(record["id"], {}, options)
|
621
|
+
end
|
622
|
+
return 0, nil
|
623
|
+
end
|
624
|
+
|
625
|
+
def remove(args)
|
626
|
+
id = args[0]
|
627
|
+
params = {}
|
628
|
+
options = {}
|
629
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
630
|
+
opts.banner = subcommand_usage("[#{rest_arg}]")
|
631
|
+
build_standard_remove_options(opts, options)
|
632
|
+
end
|
633
|
+
optparse.parse!(args)
|
634
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
635
|
+
connect(options)
|
636
|
+
params.merge!(parse_query_options(options))
|
637
|
+
record = rest_find_by_name_or_id(id)
|
638
|
+
if record.nil?
|
639
|
+
return 1, "#{rest_name} not found for '#{id}'"
|
640
|
+
end
|
641
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the #{rest_label.downcase} #{record['name'] || record['id']}?")
|
642
|
+
return 9, "aborted"
|
643
|
+
end
|
644
|
+
rest_interface.setopts(options)
|
645
|
+
if options[:dry_run]
|
646
|
+
print_dry_run rest_interface.dry.destroy(record['id'])
|
647
|
+
return 0, nil
|
648
|
+
end
|
649
|
+
json_response = rest_interface.destroy(record['id'], params)
|
650
|
+
render_response(json_response, options) do
|
651
|
+
print_green_success "Removed #{rest_label.downcase} #{record['name'] || record['id']}"
|
652
|
+
end
|
653
|
+
return 0, nil
|
654
|
+
end
|
655
|
+
|
656
|
+
end
|
657
|
+
|