morpheus-cli 8.1.0 → 8.1.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/image_builder_image_builds_interface.rb +19 -8
- data/lib/morpheus/api/systems_interface.rb +14 -0
- data/lib/morpheus/cli/commands/image_builder_command.rb +18 -16
- data/lib/morpheus/cli/commands/systems.rb +334 -5
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c5ef57fd8e0005ce881d8a2e8de0d8100e965447dd5e66dfded62ab50229c8a
|
|
4
|
+
data.tar.gz: 64705b005697dbab2f6b0c15fa58400182a8a2a063c5a99d93cb0364b101227c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: feedeaf9d8c6241a0076af156b22182dc76ffb91feafbfd5fff9bae98016624f93400351d7fd37d4cb0fa16d42eb50444f5fd895d554bbb4214e6f8eb30c30e6
|
|
7
|
+
data.tar.gz: bbb9fb396e8b36bf35bf75a1afbcd08d3c0d000716486035c24d64a9540615a0de01e084ebce2dec11828d3345a5e9643b41c71b339bedbc92d17f8f79b10aa2
|
data/Dockerfile
CHANGED
|
@@ -2,58 +2,69 @@ require 'morpheus/api/api_client'
|
|
|
2
2
|
# this may change to just /api/image-builds
|
|
3
3
|
class Morpheus::ImageBuilderImageBuildsInterface < Morpheus::APIClient
|
|
4
4
|
|
|
5
|
+
def base_path
|
|
6
|
+
"#{@base_url}/api/image-builds"
|
|
7
|
+
end
|
|
8
|
+
|
|
5
9
|
def get(id, params={})
|
|
6
10
|
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
|
7
|
-
url = "#{
|
|
11
|
+
url = "#{base_path}/#{id}"
|
|
8
12
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
|
9
13
|
opts = {method: :get, url: url, headers: headers}
|
|
10
14
|
execute(opts)
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def list(params={})
|
|
14
|
-
url = "#{
|
|
18
|
+
url = "#{base_path}"
|
|
15
19
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
|
16
20
|
opts = {method: :get, url: url, headers: headers}
|
|
17
21
|
execute(opts)
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def create(payload)
|
|
21
|
-
url = "#{
|
|
25
|
+
url = "#{base_path}"
|
|
22
26
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
|
23
27
|
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
|
24
28
|
execute(opts)
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
# def validate_save(payload)
|
|
28
|
-
# url = "#{
|
|
32
|
+
# url = "#{base_path}/validate-save"
|
|
29
33
|
# headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
|
30
34
|
# opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
|
31
35
|
# execute(opts)
|
|
32
36
|
# end
|
|
33
37
|
|
|
34
38
|
def update(id, payload)
|
|
35
|
-
url = "#{
|
|
39
|
+
url = "#{base_path}/#{id}"
|
|
36
40
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
|
37
41
|
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
|
38
42
|
execute(opts)
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def destroy(id, params={})
|
|
42
|
-
url = "#{
|
|
46
|
+
url = "#{base_path}/#{id}"
|
|
43
47
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
|
44
48
|
opts = {method: :delete, url: url, headers: headers}
|
|
45
49
|
execute(opts)
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
def run(id, params={})
|
|
49
|
-
url = "#{
|
|
53
|
+
url = "#{base_path}/#{id}/run"
|
|
50
54
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
|
51
55
|
opts = {method: :post, url: url, headers: headers}
|
|
52
56
|
execute(opts)
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
def list_executions(id, params={})
|
|
56
|
-
url = "#{
|
|
60
|
+
url = "#{base_path}/#{id}/list-executions"
|
|
61
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
|
62
|
+
opts = {method: :get, url: url, headers: headers}
|
|
63
|
+
execute(opts)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def create_options(params={})
|
|
67
|
+
url = "#{base_path}/create-options"
|
|
57
68
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
|
58
69
|
opts = {method: :get, url: url, headers: headers}
|
|
59
70
|
execute(opts)
|
|
@@ -6,4 +6,18 @@ class Morpheus::SystemsInterface < Morpheus::RestInterface
|
|
|
6
6
|
"/api/infrastructure/systems"
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def save_uninitialized(payload, params={}, headers={})
|
|
10
|
+
execute(method: :post, url: "#{base_path}/uninitialized", params: params, payload: payload, headers: headers)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize_system(id, payload={}, params={}, headers={})
|
|
14
|
+
validate_id!(id)
|
|
15
|
+
execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}/initialize", params: params, payload: payload, headers: headers)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def validate_system(id, params={}, headers={})
|
|
19
|
+
validate_id!(id)
|
|
20
|
+
execute(method: :get, url: "#{base_path}/#{CGI::escape(id.to_s)}/validate", params: params, headers: headers)
|
|
21
|
+
end
|
|
22
|
+
|
|
9
23
|
end
|
|
@@ -651,9 +651,9 @@ class Morpheus::Cli::ImageBuilderCommand
|
|
|
651
651
|
private
|
|
652
652
|
|
|
653
653
|
def get_available_image_build_types()
|
|
654
|
-
# todo: api call
|
|
655
654
|
[
|
|
656
|
-
{'name' => 'VMware', 'code' => 'vmware', 'instanceType' => {'code' => 'vmware'}}
|
|
655
|
+
{'name' => 'VMware', 'code' => 'vmware', 'instanceType' => {'code' => 'vmware'}},
|
|
656
|
+
{'name' => 'KVM', 'code' => 'kvm', 'instanceType' => {'code' => 'kvm'}}
|
|
657
657
|
]
|
|
658
658
|
end
|
|
659
659
|
|
|
@@ -663,17 +663,6 @@ class Morpheus::Cli::ImageBuilderCommand
|
|
|
663
663
|
}
|
|
664
664
|
end
|
|
665
665
|
|
|
666
|
-
def find_image_build_type(val)
|
|
667
|
-
if val.nil? || val.to_s.empty?
|
|
668
|
-
return nil
|
|
669
|
-
else
|
|
670
|
-
return get_available_image_build_types().find { |it|
|
|
671
|
-
(it['code'].to_s.downcase == val.to_s.downcase) ||
|
|
672
|
-
(it['name'].to_s.downcase == val.to_s.downcase)
|
|
673
|
-
}
|
|
674
|
-
end
|
|
675
|
-
end
|
|
676
|
-
|
|
677
666
|
def add_image_build_option_types(connected=true)
|
|
678
667
|
[
|
|
679
668
|
{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_image_build_types_dropdown(), 'required' => true, 'description' => 'Choose the type of image build.'},
|
|
@@ -901,15 +890,28 @@ class Morpheus::Cli::ImageBuilderCommand
|
|
|
901
890
|
def prompt_new_image_build(options={}, default_values={}, do_require=true)
|
|
902
891
|
payload = {}
|
|
903
892
|
|
|
893
|
+
# load to create-options data for help with prompting
|
|
894
|
+
create_options = nil
|
|
895
|
+
begin
|
|
896
|
+
create_options = @image_builds_interface.create_options({})
|
|
897
|
+
rescue => ex
|
|
898
|
+
Morpheus::Logging::DarkPrinter.puts "Unable to load image build create options data" if Morpheus::Logging.debug?
|
|
899
|
+
end
|
|
904
900
|
# Summary / Settings Tab
|
|
905
901
|
|
|
906
902
|
# Image Build Type
|
|
903
|
+
image_build_types = nil
|
|
904
|
+
if create_options && create_options['imageBuildTypes']
|
|
905
|
+
image_build_types = create_options['imageBuildTypes'].collect {|it| it.merge({'value' => it['code']}) }
|
|
906
|
+
else
|
|
907
|
+
image_build_types = get_available_image_build_types_dropdown
|
|
908
|
+
end
|
|
907
909
|
image_build_type = nil
|
|
908
910
|
if options['type']
|
|
909
|
-
image_build_type =
|
|
911
|
+
image_build_type = image_build_types.find { |it| (it['code'].to_s.downcase == options['type'].to_s.downcase) || (it['name'].to_s.downcase == options['type'].to_s.downcase) }
|
|
910
912
|
else
|
|
911
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' =>
|
|
912
|
-
image_build_type =
|
|
913
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => image_build_types, 'required' => do_require, 'description' => 'Choose the type of image build.', 'defaultValue' => default_values['type'], :fmt=>:natural}], options, @api_client)
|
|
914
|
+
image_build_type = Morpheus::Cli::OptionTypes.get_last_select()
|
|
913
915
|
end
|
|
914
916
|
if !image_build_type
|
|
915
917
|
print_red_alert "Image Build Type not found!"
|
|
@@ -6,10 +6,8 @@ class Morpheus::Cli::Systems
|
|
|
6
6
|
|
|
7
7
|
set_command_name :systems
|
|
8
8
|
set_command_description "View and manage systems."
|
|
9
|
-
register_subcommands :list, :get, :add, :update, :remove
|
|
10
|
-
|
|
11
|
-
set_command_hidden
|
|
12
|
-
|
|
9
|
+
register_subcommands :list, :get, :add, :update, :remove, :'add-uninitialized', {:'initialize' => 'exec_initialize'}, {:'validate' => 'exec_validate'}
|
|
10
|
+
|
|
13
11
|
protected
|
|
14
12
|
|
|
15
13
|
# Systems API uses lowercase keys in payloads.
|
|
@@ -164,6 +162,337 @@ class Morpheus::Cli::Systems
|
|
|
164
162
|
end
|
|
165
163
|
end
|
|
166
164
|
|
|
165
|
+
def add_uninitialized(args)
|
|
166
|
+
options = {}
|
|
167
|
+
params = {}
|
|
168
|
+
components = []
|
|
169
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
170
|
+
opts.banner = subcommand_usage("[name]")
|
|
171
|
+
opts.on('--name NAME', String, "System Name") do |val|
|
|
172
|
+
params['name'] = val.to_s
|
|
173
|
+
end
|
|
174
|
+
opts.on('--description [TEXT]', String, "Description") do |val|
|
|
175
|
+
params['description'] = val.to_s
|
|
176
|
+
end
|
|
177
|
+
opts.on('--type TYPE', String, "System Type ID or name") do |val|
|
|
178
|
+
params['type'] = val
|
|
179
|
+
end
|
|
180
|
+
opts.on('--layout LAYOUT', String, "System Layout ID or name") do |val|
|
|
181
|
+
params['layout'] = val
|
|
182
|
+
end
|
|
183
|
+
opts.on('--config JSON', String, "System config JSON") do |val|
|
|
184
|
+
params['config'] = JSON.parse(val)
|
|
185
|
+
end
|
|
186
|
+
opts.on('--externalId ID', String, "External ID") do |val|
|
|
187
|
+
params['externalId'] = val.to_s
|
|
188
|
+
end
|
|
189
|
+
opts.on('--component JSON', String, "Component JSON (can be repeated). e.g. '{\"typeCode\":\"compute-node\",\"name\":\"CN-1\",\"config\":{\"ip\":\"10.0.0.1\"}}'") do |val|
|
|
190
|
+
components << JSON.parse(val)
|
|
191
|
+
end
|
|
192
|
+
opts.on('--components JSON', String, "Components JSON array") do |val|
|
|
193
|
+
components.concat(JSON.parse(val))
|
|
194
|
+
end
|
|
195
|
+
build_standard_add_options(opts, options)
|
|
196
|
+
opts.footer = <<-EOT
|
|
197
|
+
Create a new system in an uninitialized state.
|
|
198
|
+
This creates a skeleton system with components but does not invoke provider initialization.
|
|
199
|
+
[name] is optional and can be passed as the first argument.
|
|
200
|
+
EOT
|
|
201
|
+
end
|
|
202
|
+
optparse.parse!(args)
|
|
203
|
+
connect(options)
|
|
204
|
+
|
|
205
|
+
payload = nil
|
|
206
|
+
if options[:payload]
|
|
207
|
+
payload = options[:payload]
|
|
208
|
+
payload[rest_object_key] ||= {}
|
|
209
|
+
payload[rest_object_key].deep_merge!(params) unless params.empty?
|
|
210
|
+
payload[rest_object_key]['name'] ||= args[0] if args[0]
|
|
211
|
+
payload[rest_object_key]['components'] = components unless components.empty?
|
|
212
|
+
else
|
|
213
|
+
system_payload = {}
|
|
214
|
+
|
|
215
|
+
# Name
|
|
216
|
+
system_payload['name'] = params['name'] || args[0]
|
|
217
|
+
if !system_payload['name'] && !options[:no_prompt]
|
|
218
|
+
system_payload['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true}], options[:options], @api_client, {})['name']
|
|
219
|
+
end
|
|
220
|
+
raise_command_error "Name is required.\n#{optparse}" if system_payload['name'].to_s.empty?
|
|
221
|
+
|
|
222
|
+
# Description
|
|
223
|
+
if !params['description'] && !options[:no_prompt]
|
|
224
|
+
system_payload['description'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false}], options[:options], @api_client, {})['description']
|
|
225
|
+
else
|
|
226
|
+
system_payload['description'] = params['description']
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Type
|
|
230
|
+
available_types = system_types_for_dropdown
|
|
231
|
+
type_val = params['type']
|
|
232
|
+
if type_val
|
|
233
|
+
type_id = type_val =~ /\A\d+\Z/ ? type_val.to_i : available_types.find { |t| t['name'] == type_val || t['code'] == type_val }&.dig('id')
|
|
234
|
+
raise_command_error "System type not found: #{type_val}" unless type_id
|
|
235
|
+
system_payload['type'] = {'id' => type_id}
|
|
236
|
+
elsif !options[:no_prompt]
|
|
237
|
+
if available_types.empty?
|
|
238
|
+
raise_command_error "No system types found."
|
|
239
|
+
else
|
|
240
|
+
print cyan, "Available System Types\n", reset
|
|
241
|
+
available_types.each do |t|
|
|
242
|
+
print " #{t['id']}) #{t['name']}#{t['code'] ? " (#{t['code']})" : ''}\n"
|
|
243
|
+
end
|
|
244
|
+
selected = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'text', 'fieldLabel' => 'System Type ID', 'required' => true}], options[:options], @api_client, {})['type']
|
|
245
|
+
type_id = available_types.find { |t| t['id'].to_s == selected.to_s }&.dig('id')
|
|
246
|
+
raise_command_error "Invalid system type id: #{selected}" unless type_id
|
|
247
|
+
system_payload['type'] = {'id' => type_id}
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Layout
|
|
252
|
+
available_layouts = system_layouts_for_dropdown(system_payload.dig('type', 'id'))
|
|
253
|
+
selected_layout = nil
|
|
254
|
+
layout_val = params['layout']
|
|
255
|
+
if layout_val
|
|
256
|
+
layout_id = layout_val =~ /\A\d+\Z/ ? layout_val.to_i : available_layouts.find { |l| l['name'] == layout_val || l['code'] == layout_val }&.dig('id')
|
|
257
|
+
raise_command_error "System layout not found: #{layout_val}" unless layout_id
|
|
258
|
+
system_payload['layout'] = {'id' => layout_id}
|
|
259
|
+
selected_layout = available_layouts.find { |l| l['id'].to_i == layout_id.to_i }
|
|
260
|
+
elsif !options[:no_prompt]
|
|
261
|
+
if available_layouts.empty?
|
|
262
|
+
raise_command_error "No system layouts found for selected type."
|
|
263
|
+
else
|
|
264
|
+
print cyan, "Available System Layouts\n", reset
|
|
265
|
+
available_layouts.each do |l|
|
|
266
|
+
print " #{l['id']}) #{l['name']}#{l['code'] ? " (#{l['code']})" : ''}\n"
|
|
267
|
+
end
|
|
268
|
+
selected = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'text', 'fieldLabel' => 'System Layout ID', 'required' => true}], options[:options], @api_client, {})['layout']
|
|
269
|
+
selected_layout = available_layouts.find { |l| l['id'].to_s == selected.to_s }
|
|
270
|
+
raise_command_error "Invalid system layout id: #{selected}" unless selected_layout
|
|
271
|
+
system_payload['layout'] = {'id' => selected_layout['id']}
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Config
|
|
276
|
+
system_payload['config'] = params['config'] if params['config']
|
|
277
|
+
|
|
278
|
+
# External ID
|
|
279
|
+
system_payload['externalId'] = params['externalId'] if params['externalId']
|
|
280
|
+
|
|
281
|
+
# Components — prompt interactively if none provided via flags
|
|
282
|
+
if components.empty? && !options[:no_prompt] && selected_layout
|
|
283
|
+
available_component_types = selected_layout['componentTypes'] || []
|
|
284
|
+
if available_component_types.any?
|
|
285
|
+
print cyan, "\nAvailable Component Types for layout '#{selected_layout['name']}':\n", reset
|
|
286
|
+
available_component_types.each do |ct|
|
|
287
|
+
print " #{ct['code']}#{ct['category'] ? " [#{ct['category']}]" : ''} - #{ct['name']}\n"
|
|
288
|
+
end
|
|
289
|
+
print "\n"
|
|
290
|
+
add_more = true
|
|
291
|
+
while add_more
|
|
292
|
+
add_component = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'addComponent', 'type' => 'text', 'fieldLabel' => 'Add a component? (yes/no)', 'required' => true, 'defaultValue' => 'yes'}], options[:options], @api_client, {})['addComponent']
|
|
293
|
+
if add_component.to_s.downcase =~ /^(y|yes)$/
|
|
294
|
+
# Component type selection
|
|
295
|
+
print cyan, "Component Types:\n", reset
|
|
296
|
+
available_component_types.each_with_index do |ct, idx|
|
|
297
|
+
print " #{idx + 1}) #{ct['name']} (#{ct['code']})\n"
|
|
298
|
+
end
|
|
299
|
+
selected_ct = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'componentType', 'type' => 'text', 'fieldLabel' => 'Component Type (number or code)', 'required' => true}], options[:options], @api_client, {})['componentType']
|
|
300
|
+
component_type = nil
|
|
301
|
+
if selected_ct =~ /\A\d+\Z/
|
|
302
|
+
idx = selected_ct.to_i - 1
|
|
303
|
+
component_type = available_component_types[idx] if idx >= 0 && idx < available_component_types.size
|
|
304
|
+
else
|
|
305
|
+
component_type = available_component_types.find { |ct| ct['code'] == selected_ct }
|
|
306
|
+
end
|
|
307
|
+
if component_type.nil?
|
|
308
|
+
print_red_alert "Invalid component type: #{selected_ct}"
|
|
309
|
+
next
|
|
310
|
+
end
|
|
311
|
+
# Component name
|
|
312
|
+
default_name = component_type['name']
|
|
313
|
+
comp_name = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'componentName', 'type' => 'text', 'fieldLabel' => 'Component Name', 'required' => false, 'defaultValue' => default_name}], options[:options], @api_client, {})['componentName']
|
|
314
|
+
comp = {'typeCode' => component_type['code'], 'name' => comp_name || default_name}
|
|
315
|
+
# Component config
|
|
316
|
+
comp_config = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'componentConfig', 'type' => 'text', 'fieldLabel' => 'Component Config JSON (optional)', 'required' => false}], options[:options], @api_client, {})['componentConfig']
|
|
317
|
+
if comp_config && !comp_config.to_s.empty?
|
|
318
|
+
begin
|
|
319
|
+
comp['config'] = JSON.parse(comp_config)
|
|
320
|
+
rescue JSON::ParserError => e
|
|
321
|
+
print_red_alert "Invalid JSON: #{e.message}"
|
|
322
|
+
next
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
components << comp
|
|
326
|
+
print_green_success "Added component: #{comp['name']} (#{comp['typeCode']})"
|
|
327
|
+
else
|
|
328
|
+
add_more = false
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
system_payload['components'] = components unless components.empty?
|
|
334
|
+
|
|
335
|
+
payload = {rest_object_key => system_payload}
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
if options[:dry_run]
|
|
339
|
+
print_dry_run rest_interface.dry.save_uninitialized(payload)
|
|
340
|
+
return
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
rest_interface.setopts(options)
|
|
344
|
+
json_response = rest_interface.save_uninitialized(payload)
|
|
345
|
+
render_response(json_response, options, rest_object_key) do
|
|
346
|
+
system_id = json_response['id'] || json_response.dig(rest_object_key, 'id')
|
|
347
|
+
print_green_success "Uninitialized system created"
|
|
348
|
+
get([system_id.to_s] + (options[:remote] ? ['-r', options[:remote]] : [])) if system_id
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def exec_initialize(args)
|
|
353
|
+
options = {}
|
|
354
|
+
params = {}
|
|
355
|
+
components = []
|
|
356
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
357
|
+
opts.banner = subcommand_usage("[system]")
|
|
358
|
+
opts.on('--name NAME', String, "Update the system name before initializing") do |val|
|
|
359
|
+
params['name'] = val.to_s
|
|
360
|
+
end
|
|
361
|
+
opts.on('--description [TEXT]', String, "Update the description before initializing") do |val|
|
|
362
|
+
params['description'] = val.to_s
|
|
363
|
+
end
|
|
364
|
+
opts.on('--externalId ID', String, "Set the external ID before initializing") do |val|
|
|
365
|
+
params['externalId'] = val.to_s
|
|
366
|
+
end
|
|
367
|
+
opts.on('--config JSON', String, "Set config JSON before initializing") do |val|
|
|
368
|
+
params['config'] = JSON.parse(val)
|
|
369
|
+
end
|
|
370
|
+
opts.on('--component JSON', String, "Component update JSON (can be repeated). e.g. '{\"typeCode\":\"compute-node\",\"externalId\":\"ext-001\",\"config\":{\"ip\":\"10.0.0.1\"}}'") do |val|
|
|
371
|
+
components << JSON.parse(val)
|
|
372
|
+
end
|
|
373
|
+
opts.on('--components JSON', String, "Component updates JSON array") do |val|
|
|
374
|
+
components.concat(JSON.parse(val))
|
|
375
|
+
end
|
|
376
|
+
build_standard_update_options(opts, options)
|
|
377
|
+
opts.footer = <<-EOT
|
|
378
|
+
Initialize an existing system that is in an uninitialized state.
|
|
379
|
+
This invokes the provider's prepare and initialize lifecycle methods.
|
|
380
|
+
[system] is required. This is the name or id of a system.
|
|
381
|
+
EOT
|
|
382
|
+
end
|
|
383
|
+
optparse.parse!(args)
|
|
384
|
+
verify_args!(args: args, optparse: optparse, count: 1)
|
|
385
|
+
connect(options)
|
|
386
|
+
|
|
387
|
+
system_record = nil
|
|
388
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
|
389
|
+
json_response = rest_interface.get(args[0].to_i)
|
|
390
|
+
system_record = json_response[rest_object_key] || json_response
|
|
391
|
+
else
|
|
392
|
+
system_record = find_by_name(rest_key, args[0])
|
|
393
|
+
end
|
|
394
|
+
return 1, "System not found for '#{args[0]}'" if system_record.nil?
|
|
395
|
+
|
|
396
|
+
# Prompt for component updates if none provided via flags and not in no-prompt mode
|
|
397
|
+
if components.empty? && !options[:no_prompt] && !options[:payload]
|
|
398
|
+
existing_components = system_record['components'] || []
|
|
399
|
+
if existing_components.any?
|
|
400
|
+
print cyan, "\nExisting Components:\n", reset
|
|
401
|
+
existing_components.each_with_index do |c, idx|
|
|
402
|
+
type_code = c.dig('type', 'code') || 'unknown'
|
|
403
|
+
print " #{idx + 1}) #{c['name']} (#{type_code})#{c['externalId'] ? " externalId=#{c['externalId']}" : ''}\n"
|
|
404
|
+
end
|
|
405
|
+
print "\n"
|
|
406
|
+
update_components = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'updateComponents', 'type' => 'text', 'fieldLabel' => 'Update any components? (yes/no)', 'required' => true, 'defaultValue' => 'no'}], options[:options], @api_client, {})['updateComponents']
|
|
407
|
+
if update_components.to_s.downcase =~ /^(y|yes)$/
|
|
408
|
+
existing_components.each do |c|
|
|
409
|
+
type_code = c.dig('type', 'code') || 'unknown'
|
|
410
|
+
print cyan, "\nComponent: #{c['name']} (#{type_code})\n", reset
|
|
411
|
+
comp_update = {'typeCode' => type_code}
|
|
412
|
+
|
|
413
|
+
ext_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'externalId', 'type' => 'text', 'fieldLabel' => " External ID (current: #{c['externalId'] || 'none'})", 'required' => false}], options[:options], @api_client, {})['externalId']
|
|
414
|
+
comp_update['externalId'] = ext_id unless ext_id.to_s.empty?
|
|
415
|
+
|
|
416
|
+
comp_config = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'config', 'type' => 'text', 'fieldLabel' => ' Config JSON (optional)', 'required' => false}], options[:options], @api_client, {})['config']
|
|
417
|
+
if comp_config && !comp_config.to_s.empty?
|
|
418
|
+
begin
|
|
419
|
+
comp_update['config'] = JSON.parse(comp_config)
|
|
420
|
+
rescue JSON::ParserError => e
|
|
421
|
+
print_red_alert "Invalid JSON for component config: #{e.message}"
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
components << comp_update if comp_update.keys.length > 1
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
payload = {}
|
|
432
|
+
if options[:payload]
|
|
433
|
+
payload = options[:payload]
|
|
434
|
+
payload[rest_object_key] ||= {}
|
|
435
|
+
payload[rest_object_key].deep_merge!(params) unless params.empty?
|
|
436
|
+
payload[rest_object_key]['components'] = components unless components.empty?
|
|
437
|
+
else
|
|
438
|
+
params['components'] = components unless components.empty?
|
|
439
|
+
payload = {rest_object_key => params}
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
if options[:dry_run]
|
|
443
|
+
print_dry_run rest_interface.dry.initialize_system(system_record['id'], payload)
|
|
444
|
+
return
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
rest_interface.setopts(options)
|
|
448
|
+
json_response = rest_interface.initialize_system(system_record['id'], payload)
|
|
449
|
+
render_response(json_response, options, rest_object_key) do
|
|
450
|
+
print_green_success "System #{system_record['name']} initialized"
|
|
451
|
+
get([system_record['id'].to_s] + (options[:remote] ? ['-r', options[:remote]] : []))
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def exec_validate(args)
|
|
456
|
+
options = {}
|
|
457
|
+
params = {}
|
|
458
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
459
|
+
opts.banner = subcommand_usage("[system]")
|
|
460
|
+
build_standard_get_options(opts, options)
|
|
461
|
+
opts.footer = <<-EOT
|
|
462
|
+
Validate an existing system by running the provider's pre-check phase.
|
|
463
|
+
No state changes are made. Returns success if the provider's prepare check passes.
|
|
464
|
+
[system] is required. This is the name or id of a system.
|
|
465
|
+
EOT
|
|
466
|
+
end
|
|
467
|
+
optparse.parse!(args)
|
|
468
|
+
verify_args!(args: args, optparse: optparse, count: 1)
|
|
469
|
+
connect(options)
|
|
470
|
+
|
|
471
|
+
system = nil
|
|
472
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
|
473
|
+
json_response = rest_interface.get(args[0].to_i)
|
|
474
|
+
system = json_response[rest_object_key] || json_response
|
|
475
|
+
else
|
|
476
|
+
system = find_by_name(rest_key, args[0])
|
|
477
|
+
end
|
|
478
|
+
return 1, "System not found for '#{args[0]}'" if system.nil?
|
|
479
|
+
|
|
480
|
+
if options[:dry_run]
|
|
481
|
+
print_dry_run rest_interface.dry.validate_system(system['id'])
|
|
482
|
+
return
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
rest_interface.setopts(options)
|
|
486
|
+
json_response = rest_interface.validate_system(system['id'])
|
|
487
|
+
render_response(json_response, options) do
|
|
488
|
+
if json_response['success']
|
|
489
|
+
print_green_success "System #{system['name']} validated successfully"
|
|
490
|
+
else
|
|
491
|
+
print_red_alert "System #{system['name']} validation failed: #{json_response['msg']}"
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
167
496
|
def remove(args)
|
|
168
497
|
options = {}
|
|
169
498
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
@@ -265,6 +594,6 @@ EOT
|
|
|
265
594
|
return [] if type_id.nil?
|
|
266
595
|
result = @api_client.system_types.list_layouts(type_id, {'max' => 100})
|
|
267
596
|
items = result ? (result['systemTypeLayouts'] || result[:systemTypeLayouts] || result['layouts'] || result[:layouts] || []) : []
|
|
268
|
-
items.map { |l| {'id' => l['id'] || l[:id], 'name' => l['name'] || l[:name], 'value' => (l['id'] || l[:id]).to_s, 'code' => l['code'] || l[:code]} }
|
|
597
|
+
items.map { |l| {'id' => l['id'] || l[:id], 'name' => l['name'] || l[:name], 'value' => (l['id'] || l[:id]).to_s, 'code' => l['code'] || l[:code], 'componentTypes' => l['componentTypes'] || l[:componentTypes] || []} }
|
|
269
598
|
end
|
|
270
599
|
end
|
data/lib/morpheus/cli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: morpheus-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Estes
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2026-
|
|
14
|
+
date: 2026-04-06 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: tins
|