morpheus-cli 8.0.11.1 → 8.0.12.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.
@@ -147,4 +147,140 @@ module Morpheus::Cli::ProcessesHelper
147
147
  return process
148
148
  end
149
149
 
150
+ def handle_history_command(args, arg_name, label, ref_type, &block)
151
+ raw_args = args.dup
152
+ options = {}
153
+ #options[:show_output] = true
154
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
155
+ opts.banner = "Usage: #{prog_name} #{command_name} history [#{arg_name}]"
156
+ opts.on( nil, '--events', "Display sub processes (events)." ) do
157
+ options[:show_events] = true
158
+ end
159
+ opts.on( nil, '--output', "Display process output." ) do
160
+ options[:show_output] = true
161
+ end
162
+ opts.on('--details', "Display more details: memory and storage usage used / max values." ) do
163
+ options[:show_events] = true
164
+ options[:show_output] = true
165
+ options[:details] = true
166
+ end
167
+ # opts.on('--process-id ID', String, "Display details about a specfic process only." ) do |val|
168
+ # options[:process_id] = val
169
+ # end
170
+ # opts.on('--event-id ID', String, "Display details about a specfic process event only." ) do |val|
171
+ # options[:event_id] = val
172
+ # end
173
+ build_standard_list_options(opts, options)
174
+ opts.footer = "List historical processes for a specific #{label}.\n" +
175
+ "[#{arg_name}] is required. This is the name or id of an #{label}."
176
+ end
177
+ optparse.parse!(args)
178
+
179
+ # shortcut to other actions
180
+ # if options[:process_id]
181
+ # return history_details(raw_args)
182
+ # elsif options[:event_id]
183
+ # return history_event_details(raw_args)
184
+ # end
185
+
186
+ verify_args!(args:args, optparse:optparse, count:1)
187
+ connect(options)
188
+
189
+ record = block.call(args[0])
190
+ # block should raise_command_error if not found
191
+ if record.nil?
192
+ raise_command_error "#{label} not found for name or id '#{args[0]}'"
193
+ end
194
+ params = {}
195
+ params.merge!(parse_list_options(options))
196
+ # params['query'] = params.delete('phrase') if params['phrase']
197
+ params['refType'] = ref_type
198
+ params['refId'] = record['id']
199
+ @processes_interface.setopts(options)
200
+ if options[:dry_run]
201
+ print_dry_run @processes_interface.dry.list(params)
202
+ return
203
+ end
204
+ json_response = @processes_interface.list(params)
205
+ render_response(json_response, options, "processes") do
206
+ title = "#{label} History: #{record['name'] || record['id']}"
207
+ subtitles = parse_list_subtitles(options)
208
+ print_h1 title, subtitles, options
209
+ processes = json_response['processes']
210
+ if processes.empty?
211
+ print "#{cyan}No process history found.#{reset}\n\n"
212
+ else
213
+ history_records = []
214
+ processes.each do |process|
215
+ row = {
216
+ id: process['id'],
217
+ eventId: nil,
218
+ uniqueId: process['uniqueId'],
219
+ name: process['displayName'],
220
+ description: process['description'],
221
+ processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
222
+ createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
223
+ startDate: format_local_dt(process['startDate']),
224
+ duration: format_process_duration(process),
225
+ status: format_process_status(process),
226
+ error: format_process_error(process, options[:details] ? nil : 20),
227
+ output: format_process_output(process, options[:details] ? nil : 20)
228
+ }
229
+ history_records << row
230
+ process_events = process['events'] || process['processEvents']
231
+ if options[:show_events]
232
+ if process_events
233
+ process_events.each do |process_event|
234
+ event_row = {
235
+ id: process['id'],
236
+ eventId: process_event['id'],
237
+ uniqueId: process_event['uniqueId'],
238
+ name: process_event['displayName'], # blank like the UI
239
+ description: process_event['description'],
240
+ processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
241
+ createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
242
+ startDate: format_local_dt(process_event['startDate']),
243
+ duration: format_process_duration(process_event),
244
+ status: format_process_status(process_event),
245
+ error: format_process_error(process_event, options[:details] ? nil : 20),
246
+ output: format_process_output(process_event, options[:details] ? nil : 20)
247
+ }
248
+ history_records << event_row
249
+ end
250
+ else
251
+
252
+ end
253
+ end
254
+ end
255
+ columns = [
256
+ {:id => {:display_name => "PROCESS ID"} },
257
+ :name,
258
+ :description,
259
+ {:processType => {:display_name => "PROCESS TYPE"} },
260
+ {:createdBy => {:display_name => "CREATED BY"} },
261
+ {:startDate => {:display_name => "START DATE"} },
262
+ {:duration => {:display_name => "ETA/DURATION"} },
263
+ :status,
264
+ :error
265
+ ]
266
+ if options[:show_events]
267
+ columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
268
+ end
269
+ if options[:show_output]
270
+ columns << :output
271
+ end
272
+ # custom pretty table columns ...
273
+ if options[:include_fields]
274
+ columns = options[:include_fields]
275
+ end
276
+ print cyan
277
+ print as_pretty_table(history_records, columns, options)
278
+ #print_results_pagination(json_response)
279
+ print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
280
+ print reset, "\n"
281
+ end
282
+ end
283
+ return 0, nil
284
+ end
285
+
150
286
  end
@@ -555,7 +555,7 @@ EOT
555
555
  end
556
556
 
557
557
  def _get(id, params, options)
558
- if id !~ /\A\d{1,}\Z/ && rest_has_name
558
+ if id.to_s !~ /\A\d{1,}\Z/ && rest_has_name
559
559
  record = rest_find_by_name_or_id(id)
560
560
  if record.nil?
561
561
  return 1, "#{rest_label} not found for '#{id}'"
@@ -952,7 +952,7 @@ EOT
952
952
  end
953
953
 
954
954
  def _get_type(id, params, options)
955
- if id !~ /\A\d{1,}\Z/ # && rest_type_has_name
955
+ if id.to_s !~ /\A\d{1,}\Z/ # && rest_type_has_name
956
956
  record = rest_type_find_by_name_or_id(id)
957
957
  if record.nil?
958
958
  return 1, "#{rest_type_label} not found for '#{id}'"
@@ -724,10 +724,11 @@ module Morpheus
724
724
  end
725
725
  end
726
726
  # default to the first option
727
- if !value_found && default_value.nil? && option_type['defaultFirstOption'] && select_options && select_options[0]
728
- # default_value = select_options[0][value_field]
727
+ first_option = select_options ? select_options.find {|opt| opt['isGroup'] != true } : nil
728
+ if !value_found && default_value.nil? && option_type['defaultFirstOption'] && first_option
729
+ # default_value = first_option[value_field]
729
730
  # nicer to display name instead, it will match and replace with value
730
- default_value = select_options[0]['name'] ? select_options[0]['name'] : select_options[0][value_field]
731
+ default_value = first_option['name'] ? first_option['name'] : first_option[value_field]
731
732
  end
732
733
 
733
734
  if no_prompt
@@ -978,6 +979,7 @@ module Morpheus
978
979
  query_value = (input || '')
979
980
  api_params ||= {}
980
981
  api_params['query'] = query_value
982
+ api_params['phrase'] = query_value
981
983
  # skip refresh if you just hit enter
982
984
  if !query_value.empty? || (select_options.nil? || select_options.empty?)
983
985
  select_options = load_options(option_type, api_client, api_params, query_value)
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "8.0.11.1"
4
+ VERSION = "8.0.12.1"
5
5
  end
6
6
  end
@@ -0,0 +1,184 @@
1
+ # Provides currency symbol lookup
2
+ #
3
+ module Morpheus::Currency
4
+ SYMBOLS = {
5
+ "AED" => "د.إ",
6
+ "AFN" => "؋",
7
+ "ALL" => "L",
8
+ "AMD" => "֏",
9
+ "ANG" => "ƒ",
10
+ "AOA" => "Kz",
11
+ "ARS" => "$",
12
+ "AUD" => "$",
13
+ "AWG" => "ƒ",
14
+ "AZN" => "₼",
15
+ "BAM" => "КМ",
16
+ "BBD" => "$",
17
+ "BDT" => "৳",
18
+ "BGN" => "лв.",
19
+ "BHD" => "د.ب",
20
+ "BIF" => "Fr",
21
+ "BMD" => "$",
22
+ "BND" => "$",
23
+ "BOB" => "Bs.",
24
+ "BRL" => "R$",
25
+ "BSD" => "$",
26
+ "BTN" => "Nu.",
27
+ "BWP" => "P",
28
+ "BYN" => "Br",
29
+ "BYR" => "Br",
30
+ "BZD" => "$",
31
+ "CAD" => "$",
32
+ "CDF" => "Fr",
33
+ "CHF" => "CHF",
34
+ "CLF" => "UF",
35
+ "CLP" => "$",
36
+ "CNY" => "¥",
37
+ "COP" => "$",
38
+ "CRC" => "₡",
39
+ "CUC" => "$",
40
+ "CUP" => "$",
41
+ "CVE" => "$",
42
+ "CZK" => "Kč",
43
+ "DJF" => "Fdj",
44
+ "DKK" => "kr.",
45
+ "DOP" => "$",
46
+ "DZD" => "د.ج",
47
+ "EGP" => "ج.م",
48
+ "ERN" => "Nfk",
49
+ "ETB" => "Br",
50
+ "EUR" => "€",
51
+ "FJD" => "$",
52
+ "FKP" => "£",
53
+ "GBP" => "£",
54
+ "GEL" => "₾",
55
+ "GHS" => "₵",
56
+ "GIP" => "£",
57
+ "GMD" => "D",
58
+ "GNF" => "Fr",
59
+ "GTQ" => "Q",
60
+ "GYD" => "$",
61
+ "HKD" => "$",
62
+ "HNL" => "L",
63
+ "HTG" => "G",
64
+ "HUF" => "Ft",
65
+ "IDR" => "Rp",
66
+ "ILS" => "₪",
67
+ "INR" => "₹",
68
+ "IQD" => "ع.د",
69
+ "IRR" => "﷼",
70
+ "ISK" => "kr.",
71
+ "JMD" => "$",
72
+ "JOD" => "د.ا",
73
+ "JPY" => "¥",
74
+ "KES" => "KSh",
75
+ "KGS" => "som",
76
+ "KHR" => "៛",
77
+ "KMF" => "Fr",
78
+ "KPW" => "₩",
79
+ "KRW" => "₩",
80
+ "KWD" => "د.ك",
81
+ "KYD" => "$",
82
+ "KZT" => "₸",
83
+ "LAK" => "₭",
84
+ "LBP" => "ل.ل",
85
+ "LKR" => "₨",
86
+ "LRD" => "$",
87
+ "LSL" => "L",
88
+ "LYD" => "ل.د",
89
+ "MAD" => "د.م.",
90
+ "MDL" => "L",
91
+ "MGA" => "Ar",
92
+ "MKD" => "ден",
93
+ "MMK" => "K",
94
+ "MNT" => "₮",
95
+ "MOP" => "P",
96
+ "MRU" => "UM",
97
+ "MUR" => "₨",
98
+ "MVR" => "MVR",
99
+ "MWK" => "MK",
100
+ "MXN" => "$",
101
+ "MYR" => "RM",
102
+ "MZN" => "MTn",
103
+ "NAD" => "$",
104
+ "NGN" => "₦",
105
+ "NIO" => "C$",
106
+ "NOK" => "kr",
107
+ "NPR" => "Rs.",
108
+ "NZD" => "$",
109
+ "OMR" => "ر.ع.",
110
+ "PAB" => "B/.",
111
+ "PEN" => "S/",
112
+ "PGK" => "K",
113
+ "PHP" => "₱",
114
+ "PKR" => "₨",
115
+ "PLN" => "zł",
116
+ "PYG" => "₲",
117
+ "QAR" => "ر.ق",
118
+ "RON" => "Lei",
119
+ "RSD" => "RSD",
120
+ "RUB" => "₽",
121
+ "RWF" => "FRw",
122
+ "SAR" => "ر.س",
123
+ "SBD" => "$",
124
+ "SCR" => "₨",
125
+ "SDG" => "£",
126
+ "SEK" => "kr",
127
+ "SGD" => "$",
128
+ "SHP" => "£",
129
+ "SKK" => "Sk",
130
+ "SLE" => "Le",
131
+ "SLL" => "Le",
132
+ "SOS" => "Sh",
133
+ "SRD" => "$",
134
+ "SSP" => "£",
135
+ "STD" => "Db",
136
+ "STN" => "Db",
137
+ "SVC" => "₡",
138
+ "SYP" => "£S",
139
+ "SZL" => "E",
140
+ "THB" => "฿",
141
+ "TJS" => "ЅМ",
142
+ "TMT" => "m",
143
+ "TND" => "د.ت",
144
+ "TOP" => "T$",
145
+ "TRY" => "₺",
146
+ "TTD" => "$",
147
+ "TWD" => "$",
148
+ "TZS" => "Sh",
149
+ "UAH" => "₴",
150
+ "UGX" => "USh",
151
+ "USD" => "$",
152
+ "UYU" => "$U",
153
+ "UZS" => "so'm",
154
+ "VES" => "Bs",
155
+ "VND" => "₫",
156
+ "VUV" => "Vt",
157
+ "WST" => "T",
158
+ "XAF" => "CFA",
159
+ "XAG" => "oz t",
160
+ "XAU" => "oz t",
161
+ "XBA" => "",
162
+ "XBB" => "",
163
+ "XBC" => "",
164
+ "XBD" => "",
165
+ "XCD" => "$",
166
+ "XCG" => "Cg",
167
+ "XDR" => "SDR",
168
+ "XOF" => "Fr",
169
+ "XPD" => "oz t",
170
+ "XPF" => "Fr",
171
+ "XPT" => "oz t",
172
+ "XTS" => "",
173
+ "YER" => "﷼",
174
+ "ZAR" => "R",
175
+ "ZMK" => "ZK",
176
+ "ZMW" => "K",
177
+ "ZWG" => "ZiG"
178
+ }
179
+
180
+ def self.get_symbol(code)
181
+ SYMBOLS[code.upcase.to_s]
182
+ end
183
+
184
+ end
@@ -1,7 +1,7 @@
1
1
  require 'time'
2
2
  require 'filesize'
3
- require 'money'
4
3
  require 'cgi'
4
+ require 'morpheus/currency'
5
5
 
6
6
  DEFAULT_DATE_FORMAT = "%x"
7
7
  DEFAULT_TIME_FORMAT = "%x %I:%M %p"
@@ -403,8 +403,8 @@ def format_sig_dig(n, sigdig=3, min_sigdig=nil, pad_zeros=false)
403
403
  v
404
404
  end
405
405
 
406
- def currency_sym(currency)
407
- Money::Currency.new((currency || 'USD').to_sym).symbol
406
+ def currency_symbol(currency)
407
+ Morpheus::Currency.get_symbol(currency) || Morpheus::Currency.get_symbol("USD")
408
408
  end
409
409
 
410
410
  # returns currency amount formatted like "$4,5123.00". 0.00 is formatted as "$0"
@@ -418,16 +418,16 @@ def format_currency(amount, currency='USD', opts={})
418
418
 
419
419
  amount = amount.to_f
420
420
  if amount == 0
421
- return currency_sym(currency).to_s + "0"
421
+ return currency_symbol(currency).to_s + "0"
422
422
  # elsif amount.to_f < 0.01
423
423
  # # return exponent notation like 3.4e-09
424
- # return currency_sym(currency).to_s + "#{amount}"
424
+ # return currency_symbol(currency).to_s + "#{amount}"
425
425
  else
426
426
  sigdig = opts[:sigdig] ? opts[:sigdig].to_i : 2 # max decimal digits
427
427
  min_sigdig = opts[:min_sigdig] ? opts[:min_sigdig].to_i : (sigdig || 2) # min decimal digits
428
428
  display_value = format_sig_dig(amount, sigdig, min_sigdig, opts[:pad_zeros])
429
429
  display_value = format_number(display_value) # commas
430
- rtn = currency_sym(currency).to_s + display_value
430
+ rtn = currency_symbol(currency).to_s + display_value
431
431
  if amount.to_i < 0
432
432
  rtn = "(#{rtn})"
433
433
  if opts[:minus_color]
@@ -127,7 +127,8 @@ module Morpheus::Routes
127
127
  buckets: {},
128
128
  },
129
129
  :'image-builder' => {},
130
- :vdi => {}
130
+ :migrations => {},
131
+ :vdi => {},
131
132
  },
132
133
  admin: {
133
134
  accounts: {}, # Tenants
data/morpheus-cli.gemspec CHANGED
@@ -31,6 +31,5 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency 'mime-types'
32
32
  spec.add_dependency "http"
33
33
  spec.add_dependency "rubyzip", '~> 2.3.2'
34
- spec.add_dependency "money"
35
34
  spec.add_dependency "test-unit"
36
35
  end
@@ -0,0 +1,39 @@
1
+ require 'morpheus_test'
2
+
3
+ class MorpheusTest::MigrationsTest < MorpheusTest::TestCase
4
+
5
+ def test_migrations_list
6
+ assert_execute %(migrations list)
7
+ assert_execute %(migrations list "apitest")
8
+ end
9
+
10
+ def test_migrations_get
11
+ # migration = client.migrations.list({})['migrations'][0]
12
+ migration = client.migrations.list({})['migrations'].find {|r| r['authority'] !~ /\A\d+\Z/}
13
+ if migration
14
+ assert_execute %(migrations get "#{migration['id']}")
15
+ # beware that duplicates may exist
16
+ name_arg = migration['name']
17
+ assert_execute %(migrations get "#{escape_arg name_arg}")
18
+ else
19
+ puts "No migration found, unable to execute test `#{__method__}`"
20
+ end
21
+ end
22
+
23
+
24
+ # todo: test all the other commands
25
+
26
+ # def test_migrations_add
27
+ # assert_execute %(migrations add "test_migration_#{random_id}" -N)
28
+ # end
29
+
30
+ # def test_migrations_update
31
+ # #skip "Test needs to be added"
32
+ # assert_execute %(migrations update "test_migration_#{random_id}" --description "neat")
33
+ # end
34
+
35
+ # def test_migrations_remove
36
+ # assert_execute %(migrations remove "test_migration_#{random_id}" -y")
37
+ # end
38
+
39
+ end
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.0.11.1
4
+ version: 8.0.12.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: 2025-11-20 00:00:00.000000000 Z
14
+ date: 2025-12-29 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: tins
@@ -181,20 +181,6 @@ dependencies:
181
181
  - - "~>"
182
182
  - !ruby/object:Gem::Version
183
183
  version: 2.3.2
184
- - !ruby/object:Gem::Dependency
185
- name: money
186
- requirement: !ruby/object:Gem::Requirement
187
- requirements:
188
- - - ">="
189
- - !ruby/object:Gem::Version
190
- version: '0'
191
- type: :runtime
192
- prerelease: false
193
- version_requirements: !ruby/object:Gem::Requirement
194
- requirements:
195
- - - ">="
196
- - !ruby/object:Gem::Version
197
- version: '0'
198
184
  - !ruby/object:Gem::Dependency
199
185
  name: test-unit
200
186
  requirement: !ruby/object:Gem::Requirement
@@ -317,6 +303,7 @@ files:
317
303
  - lib/morpheus/api/load_balancers_interface.rb
318
304
  - lib/morpheus/api/log_settings_interface.rb
319
305
  - lib/morpheus/api/logs_interface.rb
306
+ - lib/morpheus/api/migrations_interface.rb
320
307
  - lib/morpheus/api/monitoring_alerts_interface.rb
321
308
  - lib/morpheus/api/monitoring_apps_interface.rb
322
309
  - lib/morpheus/api/monitoring_checks_interface.rb
@@ -445,6 +432,7 @@ files:
445
432
  - lib/morpheus/cli/commands/cloud_resource_pools_command.rb
446
433
  - lib/morpheus/cli/commands/clouds.rb
447
434
  - lib/morpheus/cli/commands/clouds_types.rb
435
+ - lib/morpheus/cli/commands/cluster_types.rb
448
436
  - lib/morpheus/cli/commands/clusters.rb
449
437
  - lib/morpheus/cli/commands/coloring_command.rb
450
438
  - lib/morpheus/cli/commands/containers_command.rb
@@ -509,6 +497,7 @@ files:
509
497
  - lib/morpheus/cli/commands/logout.rb
510
498
  - lib/morpheus/cli/commands/logs_command.rb
511
499
  - lib/morpheus/cli/commands/man_command.rb
500
+ - lib/morpheus/cli/commands/migrations.rb
512
501
  - lib/morpheus/cli/commands/monitoring_alerts_command.rb
513
502
  - lib/morpheus/cli/commands/monitoring_apps_command.rb
514
503
  - lib/morpheus/cli/commands/monitoring_checks_command.rb
@@ -634,6 +623,7 @@ files:
634
623
  - lib/morpheus/cli/option_parser.rb
635
624
  - lib/morpheus/cli/option_types.rb
636
625
  - lib/morpheus/cli/version.rb
626
+ - lib/morpheus/currency.rb
637
627
  - lib/morpheus/ext/hash.rb
638
628
  - lib/morpheus/ext/nil_class.rb
639
629
  - lib/morpheus/ext/rest_client.rb
@@ -659,6 +649,7 @@ files:
659
649
  - test/cli/help_test.rb
660
650
  - test/cli/instances_test.rb
661
651
  - test/cli/man_test.rb
652
+ - test/cli/migrations_test.rb
662
653
  - test/cli/network_routers_test.rb
663
654
  - test/cli/remote_test.rb
664
655
  - test/cli/roles_test.rb
@@ -707,6 +698,7 @@ test_files:
707
698
  - test/cli/help_test.rb
708
699
  - test/cli/instances_test.rb
709
700
  - test/cli/man_test.rb
701
+ - test/cli/migrations_test.rb
710
702
  - test/cli/network_routers_test.rb
711
703
  - test/cli/remote_test.rb
712
704
  - test/cli/roles_test.rb