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.
@@ -0,0 +1,453 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::Migrations
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::RestCommand
6
+ include Morpheus::Cli::ProcessesHelper
7
+ #include Morpheus::Cli::MigrationsHelper
8
+
9
+ set_command_name :'migrations'
10
+ set_command_description "View and manage migrations."
11
+ register_subcommands :list, :get, :add, :update, :run, :remove, :history
12
+
13
+ # RestCommand settings
14
+ register_interfaces :migrations, :processes
15
+ # set_rest_has_type false
16
+ # set_rest_type :migration_types
17
+
18
+ def render_response_for_get(json_response, options)
19
+ render_response(json_response, options, rest_object_key) do
20
+ record = json_response[rest_object_key]
21
+ print_h1 rest_label, [], options
22
+ print cyan
23
+ print_description_list(rest_column_definitions(options), record, options)
24
+ # show Migration Configuration
25
+ # config = record['config']
26
+ # if config && !config.empty?
27
+ # print_h2 "Virtual Machines"
28
+ # print_description_list(config.keys, config)
29
+ # end
30
+ # Datastores
31
+ datastores = record['datastores']
32
+ print_h2 "Datastores", options
33
+ if datastores && datastores.size > 0
34
+ columns = [
35
+ {"Source" => lambda {|it| it['sourceDatastore'] ? "#{it['sourceDatastore']['name']} [#{it['sourceDatastore']['id']}]" : "" } },
36
+ {"Destination" => lambda {|it| it['destinationDatastore'] ? "#{it['destinationDatastore']['name']} [#{it['destinationDatastore']['id']}]" : "" } },
37
+ ]
38
+ print as_pretty_table(datastores, columns, options)
39
+ else
40
+ print cyan,"No datatores in migration",reset,"\n"
41
+ end
42
+ # Networks
43
+ print_h2 "Networks", options
44
+ networks = record['networks']
45
+ if networks && networks.size > 0
46
+ columns = [
47
+ {"Source" => lambda {|it| it['sourceNetwork'] ? "#{it['sourceNetwork']['name']} [#{it['sourceNetwork']['id']}]" : "" } },
48
+ {"Destination" => lambda {|it| it['destinationNetwork'] ? "#{it['destinationNetwork']['name']} [#{it['destinationNetwork']['id']}]" : "" } },
49
+ ]
50
+ print as_pretty_table(networks, columns, options)
51
+ else
52
+ print cyan,"No networks found in migration",reset,"\n"
53
+ end
54
+ # Virtual Machines
55
+ print_h2 "Virtual Machines", options
56
+ servers = record['servers']
57
+ if servers && servers.size > 0
58
+ columns = [
59
+ # {"ID" => lambda {|it| it['sourceServer'] ? it['sourceServer']['id'] : "" } },
60
+ # {"Name" => lambda {|it| it['sourceServer'] ? it['sourceServer']['name'] : "" } },
61
+ {"Source" => lambda {|it| it['sourceServer'] ? "#{it['sourceServer']['name']} [#{it['sourceServer']['id']}]" : "" } },
62
+ {"Destination" => lambda {|it| it['destinationServer'] ? "#{it['destinationServer']['name']} [#{it['destinationServer']['id']}]" : "" } },
63
+ {"Status" => lambda {|it| format_migration_server_status(it) } }
64
+ ]
65
+ print as_pretty_table(servers, columns, options)
66
+ else
67
+ print cyan,"No virtual machines found in migration",reset,"\n"
68
+ end
69
+ print reset,"\n"
70
+ end
71
+ end
72
+
73
+ def history(args)
74
+ handle_history_command(args, "migration", "Migration", "migrationPlan") do |id|
75
+ record = rest_find_by_name_or_id(id)
76
+ if record.nil?
77
+ # raise_command_error "#{rest_name} not found for '#{id}'"
78
+ return 1, "#{rest_name} not found for '#{id}'"
79
+ end
80
+ record
81
+ end
82
+ end
83
+
84
+ def add(args)
85
+ options = {}
86
+ params = {}
87
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
88
+ opts.banner = subcommand_usage("[name] [options]")
89
+ build_option_type_options(opts, options, add_migration_option_types)
90
+ build_standard_add_options(opts, options)
91
+ opts.footer = <<-EOT
92
+ Create a new migration plan.
93
+ EOT
94
+ end
95
+ optparse.parse!(args)
96
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
97
+ options[:options]['name'] = args[0] if args[0]
98
+ connect(options)
99
+ payload = {}
100
+ if options[:payload]
101
+ payload = options[:payload]
102
+ payload.deep_merge!({rest_object_key => parse_passed_options(options)})
103
+ else
104
+ params.deep_merge!(parse_passed_options(options))
105
+ # prompt for option types
106
+ # skip config if using interactive prompt
107
+ add_option_types = add_migration_option_types
108
+ # handle some option types in a special way
109
+ servers_option_type = add_option_types.find {|it| it['fieldName'] == 'servers' } # || {'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Virtual Machines', 'type' => 'multiSelect', 'optionSource' => 'searchServers', 'required' => true, 'description' => 'Virtual Machines to be migrated, comma separated list of server names or IDs.'}
110
+ add_option_types.reject! {|it| it['fieldName'] == 'servers' }
111
+ # prompt
112
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_option_types, options[:options], @api_client, options[:params])
113
+ params.deep_merge!(v_prompt)
114
+ # convert checkbox "on" and "off" to true and false
115
+ params.booleanize!
116
+
117
+ # prompt for servers
118
+ server_ids = nil
119
+ if params['sourceServerIds']
120
+ server_ids = parse_id_list(params.delete('sourceServerIds'))
121
+ elsif params['servers']
122
+ server_ids = parse_id_list(params.delete('servers'))
123
+ end
124
+
125
+ if server_ids
126
+ # lookup each value as an id or name and collect id
127
+ # server_ids = server_ids.collect {|it| find_server_by_name_or_id(it)}.compact.collect {|it| it['id']}
128
+ # available_servers = @api_client.options.options_for_source("searchServers", {'cloudId' => params['sourceCloudId'], 'max' => 1000})['data']
129
+ available_servers = @api_client.migrations.source_servers({'sourceCloudId' => params['sourceCloudId'], 'max' => 5000})['sourceServers']
130
+ bad_ids = []
131
+ server_ids = server_ids.collect {|server_id|
132
+ found_option = available_servers.find {|it| it['id'].to_s == server_id.to_s || it['name'] == server_id.to_s }
133
+ if found_option
134
+ found_option['value'] || found_option['id']
135
+ else
136
+ bad_ids << server_id
137
+ end
138
+ }
139
+ if bad_ids.size > 0
140
+ raise_command_error "No such server found for: #{bad_ids.join(', ')}"
141
+ end
142
+ else
143
+ # prompt for servers
144
+ # servers_option_type = {'fieldName' => 'servers', 'fieldLabel' => 'Virtual Machines', 'type' => 'multiSelect', 'optionSource' => 'searchServers', 'description' => 'Select virtual machine servers to be migrated.', 'required' => true}
145
+ api_params = {'cloudId' => params['sourceCloudId']}
146
+ # server_ids = Morpheus::Cli::OptionTypes.prompt([servers_option_type], options[:options], @api_client, {'cloudId' => params['sourceCloudId'], 'max' => 1000})['servers']
147
+ server_ids = Morpheus::Cli::OptionTypes.prompt([servers_option_type], options[:options], @api_client, {'sourceCloudId' => params['sourceCloudId'], 'max' => 5000})['servers']
148
+ # todo: Add prompt for Add more servers?
149
+ # while self.confirm("Add more #{servers_option_type['fieldLabel']}?", {:default => false}) do
150
+ # more_ids = Morpheus::Cli::OptionTypes.prompt([servers_option_type.merge({'required' => false})], {}, @api_client, api_params)['servers']
151
+ # server_ids += more_ids
152
+ # end
153
+ end
154
+ server_ids.uniq!
155
+ params['sourceServerIds'] = server_ids
156
+
157
+ # prompt for datastores
158
+ datastore_mappings = []
159
+ source_datastores = @api_client.migrations.source_storage({'sourceCloudId' => params['sourceCloudId'], 'sourceServerIds' => params['sourceServerIds'].join(",")})['sourceStorage']
160
+ source_datastores.each do |datastore|
161
+ target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => "datastore.#{datastore['id']}", 'fieldLabel' => "Datastore #{datastore['name']}", 'type' => 'select', 'required' => true, 'defaultFirstOption' => true, 'description' => "Datastore destination for datastore #{datastore['name']} [#{datastore['id']}]", 'optionSource' => lambda {|api_client, api_params|
162
+ api_client.migrations.target_storage(api_params)['targetStorage'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
163
+ } }], options[:options], @api_client, {'targetCloudId' => params['targetCloudId'], 'targetPoolId' => params['targetPoolId']})["datastore"]["#{datastore['id']}"]
164
+ datastore_mappings << {'sourceDatastore' => {'id' => datastore['id']}, 'destinationDatastore' => {'id' => target_id}}
165
+ end
166
+ params['datastores'] = datastore_mappings
167
+ params.delete('datastore') # remove options passed in as -O datastore.id=
168
+
169
+ # prompt for networks
170
+ network_mappings = []
171
+ source_networks = @api_client.migrations.source_networks({'sourceCloudId' => params['sourceCloudId'], 'sourceServerIds' => params['sourceServerIds'].join(",")})['sourceNetworks']
172
+ source_networks.each do |network|
173
+ target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => "network.#{network['id']}", 'fieldLabel' => "Network #{network['name']}", 'type' => 'select', 'required' => true, 'defaultFirstOption' => true, 'description' => "Network destination for network #{network['name']} [#{network['id']}]", 'optionSource' => lambda {|api_client, api_params|
174
+ api_client.migrations.target_networks(api_params)['targetNetworks'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
175
+ } }], options[:options], @api_client, {'targetCloudId' => params['targetCloudId'], 'targetPoolId' => params['targetPoolId']})["network"]["#{network['id']}"]
176
+ network_mappings << {'sourceNetwork' => {'id' => network['id']}, 'destinationNetwork' => {'id' => target_id}}
177
+ end
178
+ params['networks'] = network_mappings
179
+ params.delete('network') # remove options passed in as -O network.id=
180
+
181
+ payload.deep_merge!({rest_object_key => params})
182
+ end
183
+ @migrations_interface.setopts(options)
184
+ if options[:dry_run]
185
+ print_dry_run @migrations_interface.dry.create(payload)
186
+ return 0, nil
187
+ end
188
+ json_response = @migrations_interface.create(payload)
189
+ migration = json_response[rest_object_key]
190
+ render_response(json_response, options, rest_object_key) do
191
+ print_green_success "Added migration #{migration['name']}"
192
+ return _get(migration["id"], {}, options)
193
+ end
194
+ return 0, nil
195
+ end
196
+
197
+ def update(args)
198
+ options = {}
199
+ params = {}
200
+ payload = {}
201
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
202
+ opts.banner = subcommand_usage("[migration] [options]")
203
+ build_option_type_options(opts, options, update_migration_option_types)
204
+ build_standard_update_options(opts, options)
205
+ opts.footer = <<-EOT
206
+ Update a migration plan.
207
+ [migration] is required. This is the name or id of a migration plan.
208
+ EOT
209
+ end
210
+ optparse.parse!(args)
211
+ verify_args!(args:args, optparse:optparse, count:1)
212
+ connect(options)
213
+ migration = find_migration_by_name_or_id(args[0])
214
+ return 1 if migration.nil?
215
+ payload = {}
216
+ if options[:payload]
217
+ payload = options[:payload]
218
+ payload.deep_merge!({rest_object_key => parse_passed_options(options)})
219
+ else
220
+ params.deep_merge!(parse_passed_options(options))
221
+ # prompt for option types
222
+ # skip config if using interactive prompt
223
+ update_option_types = update_migration_option_types
224
+ # handle some option types in a special way
225
+ servers_option_type = update_option_types.find {|it| it['fieldName'] == 'servers' } # || {'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Virtual Machines', 'type' => 'multiSelect', 'optionSource' => 'searchServers', 'required' => true, 'description' => 'Virtual Machines to be migrated, comma separated list of server names or IDs.'}
226
+ update_option_types.reject! {|it| it['fieldName'] == 'servers' }
227
+ # prompt (edit uses no_prompt)
228
+ # need these parameters for prompting..
229
+ default_api_params = {}
230
+ default_api_params['sourceCloudId'] = migration['sourceCloud']['id'] if migration['sourceCloud']
231
+ default_api_params['targetCloudId'] = migration['targetCloud']['id'] if migration['targetCloud']
232
+ default_api_params['targetGroupId'] = migration['targetGroup']['id'] if migration['targetGroup']
233
+ default_api_params['targetPoolId'] = "pool-" + migration['targetPool']['id'].to_s if migration['targetPool']
234
+ options[:params] = default_api_params.merge(options[:options])
235
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_option_types, options[:options], @api_client, options[:params])
236
+ params.deep_merge!(v_prompt)
237
+ # convert checkbox "on" and "off" to true and false
238
+ params.booleanize!
239
+
240
+ # prompt for servers
241
+ server_ids = nil
242
+ if params['sourceServerIds']
243
+ server_ids = parse_id_list(params.delete('sourceServerIds'))
244
+ elsif params['servers']
245
+ server_ids = parse_id_list(params.delete('servers'))
246
+ end
247
+
248
+ if server_ids
249
+ # lookup each value as an id or name and collect id
250
+ # server_ids = server_ids.collect {|it| find_server_by_name_or_id(it)}.compact.collect {|it| it['id']}
251
+ # available_servers = @api_client.options.options_for_source("searchServers", {'cloudId' => params['sourceCloudId'], 'max' => 1000})['data']
252
+ available_servers = @api_client.migrations.source_servers({'sourceCloudId' => params['sourceCloudId'], 'max' => 5000})['sourceServers']
253
+ bad_ids = []
254
+ server_ids = server_ids.collect {|server_id|
255
+ found_option = available_servers.find {|it| it['id'].to_s == server_id.to_s || it['name'] == server_id.to_s }
256
+ if found_option
257
+ found_option['value'] || found_option['id']
258
+ else
259
+ bad_ids << server_id
260
+ end
261
+ }
262
+ if bad_ids.size > 0
263
+ raise_command_error "No such server found for: #{bad_ids.join(', ')}"
264
+ end
265
+ server_ids.uniq!
266
+ params['sourceServerIds'] = server_ids
267
+ else
268
+ # no prompt for update
269
+ end
270
+
271
+ source_server_ids = params['sourceServerIds'] || migration['servers'].collect {|it| it['sourceServer'] ? it['sourceServer']['id'] : nil }.compact
272
+ source_cloud_id = params['sourceCloudId'] || (migration['sourceCloud'] ? migration['sourceCloud']['id'] : nil)
273
+ target_cloud_id = params['targetCloudId'] || (migration['targetCloud'] ? migration['targetCloud']['id'] : nil)
274
+ target_pool_id = params['targetPoolId'] || (migration['targetPool'] ? migration['targetPool']['id'] : nil)
275
+
276
+ # prompt for datastores
277
+ if options[:options]['datastore'].is_a?(Hash)
278
+ datastore_mappings = []
279
+ source_datastores = @api_client.migrations.source_storage({'sourceCloudId' => source_cloud_id, 'sourceServerIds' => source_server_ids.join(",")})['sourceStorage']
280
+ source_datastores.each do |datastore|
281
+ found_mapping = migration['datastores'].find {|it| it['sourceDatastore'] && it['sourceDatastore']['id'] == datastore['id'] }
282
+ default_value = found_mapping && found_mapping['destinationDatastore'] ? found_mapping['destinationDatastore']['name'] : nil
283
+ target_id = Morpheus::Cli::OptionTypes.no_prompt([{'fieldName' => "datastore.#{datastore['id']}", 'fieldLabel' => "Datastore #{datastore['name']}", 'type' => 'select', 'description' => "Datastore destination for datastore #{datastore['name']} [#{datastore['id']}]", 'defaultValue' => default_value, 'optionSource' => lambda {|api_client, api_params|
284
+ api_client.migrations.target_storage(api_params)['targetStorage'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
285
+ } }], options[:options], @api_client, {'targetCloudId' => target_cloud_id, 'targetPoolId' => target_pool_id})["datastore"]["#{datastore['id']}"]
286
+ datastore_mappings << {'sourceDatastore' => {'id' => datastore['id']}, 'destinationDatastore' => {'id' => target_id}}
287
+ end
288
+ params['datastores'] = datastore_mappings
289
+ params.delete('datastore') # remove options passed in as -O datastore.id=
290
+ end
291
+
292
+ # prompt for networks
293
+ if options[:options]['network'].is_a?(Hash)
294
+ network_mappings = []
295
+ source_networks = @api_client.migrations.source_networks({'sourceCloudId' => source_cloud_id, 'sourceServerIds' => source_server_ids.join(",")})['sourceNetworks']
296
+ source_networks.each do |network|
297
+ found_mapping = migration['networks'].find {|it| it['sourceNetwork'] && it['sourceNetwork']['id'] == network['id'] }
298
+ default_value = found_mapping && found_mapping['destinationNetwork'] ? found_mapping['destinationNetwork']['name'] : nil
299
+ target_id = Morpheus::Cli::OptionTypes.no_prompt([{'fieldName' => "network.#{network['id']}", 'fieldLabel' => "Network #{network['name']}", 'type' => 'select', 'description' => "Network destination for network #{network['name']} [#{network['id']}]", 'defaultValue' => default_value, 'optionSource' => lambda {|api_client, api_params|
300
+ api_client.migrations.target_networks(api_params)['targetNetworks'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
301
+ } }], options[:options], @api_client, {'targetCloudId' => target_cloud_id, 'targetPoolId' => target_pool_id})["network"]["#{network['id']}"]
302
+ network_mappings << {'sourceNetwork' => {'id' => network['id']}, 'destinationNetwork' => {'id' => target_id}}
303
+ end
304
+ params['networks'] = network_mappings
305
+ params.delete('network') # remove options passed in as -O network.id=
306
+ end
307
+
308
+ if params.empty? # || options[:no_prompt]
309
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
310
+ end
311
+ payload.deep_merge!({rest_object_key => params})
312
+ end
313
+ @migrations_interface.setopts(options)
314
+ if options[:dry_run]
315
+ print_dry_run @migrations_interface.dry.update(migration['id'], payload)
316
+ return
317
+ end
318
+ json_response = @migrations_interface.update(migration['id'], payload)
319
+ migration = json_response[rest_object_key]
320
+ render_response(json_response, options, rest_object_key) do
321
+ print_green_success "Updated migration #{migration['name']}"
322
+ return _get(migration["id"], {}, options)
323
+ end
324
+ return 0, nil
325
+ end
326
+
327
+ def run(args)
328
+ options = {}
329
+ params = {}
330
+ payload = {}
331
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
332
+ opts.banner = subcommand_usage("[migration]")
333
+ build_standard_post_options(opts, options, [:auto_confirm])
334
+ opts.footer = <<-EOT
335
+ Runs a migration plan to transition it from pending to scheduled for execution.
336
+ [migration] is required. This is the name or id of a migration.
337
+ EOT
338
+ end
339
+ optparse.parse!(args)
340
+ verify_args!(args:args, optparse:optparse, count:1)
341
+ connect(options)
342
+ migration = find_migration_by_name_or_id(args[0])
343
+ return 1 if migration.nil?
344
+ parse_payload(options) do |payload|
345
+ end
346
+ servers = migration['servers']
347
+ print cyan, "The following #{servers.size == 1 ? 'server' : servers.size.to_s + ' servers'} will be migrated:", "\n"
348
+ puts ""
349
+ print as_pretty_table(servers, {"Virtual Machine" => lambda {|it| it['sourceServer'] ? "#{it['sourceServer']['name']} [#{it['sourceServer']['id']}]" : "" } }, options)
350
+ puts ""
351
+ confirm!("Are you sure you want to execute the migration plan?", options)
352
+ execute_api(@migrations_interface, :run, [migration['id']], options, 'migration') do |json_response|
353
+ print_green_success "Running migration #{migration['name']}"
354
+ end
355
+ end
356
+
357
+ protected
358
+
359
+ def migration_list_column_definitions(options)
360
+ {
361
+ "ID" => 'id',
362
+ "Name" => 'name',
363
+ "VMs" => lambda {|it| it['servers'] ? it['servers'].size : 0 },
364
+ "Status" => lambda {|it| format_migration_status(it) },
365
+ }
366
+ end
367
+
368
+ def migration_column_definitions(options)
369
+ {
370
+ "ID" => 'id',
371
+ "Name" => 'name',
372
+ "Source Cloud" => lambda {|it| it['sourceCloud'] ? it['sourceCloud']['name'] : '' },
373
+ "Destination Cloud" => lambda {|it| it['targetCloud'] ? it['targetCloud']['name'] : '' },
374
+ "Resource Pool" => lambda {|it| it['targetPool'] ? it['targetPool']['name'] : '' },
375
+ "Group" => lambda {|it| it['targetGroup'] ? it['targetGroup']['name'] : '' },
376
+ "Skip Prechecks" => lambda {|it| format_boolean(it['skipPrechecks']) },
377
+ "Install Guest Tools" => lambda {|it| format_boolean(it['installGuestTools']) },
378
+ # "ReInitialize Server" => lambda {|it| format_boolean(it['reInitializeServerOnMigration']) },
379
+ "VMs" => lambda {|it| it['servers'] ? it['servers'].size : 0 },
380
+ "Status" => lambda {|it| format_migration_status(it) },
381
+ }
382
+ end
383
+
384
+ def add_migration_option_types()
385
+ [
386
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
387
+ # {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
388
+ {'switch' => 'source-cloud', 'fieldName' => 'sourceCloudId', 'fieldLabel' => 'Source Cloud', 'type' => 'select', 'required' => true, 'description' => 'Source Cloud', 'optionSource' => lambda {|api_client, api_params|
389
+ api_client.migrations.source_clouds(api_params)['sourceClouds'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
390
+ } },
391
+ {'switch' => 'cloud', 'fieldName' => 'targetCloudId', 'fieldLabel' => 'Destination Cloud', 'type' => 'select', 'required' => true, 'description' => 'Destination Cloud', 'optionSource' => lambda {|api_client, api_params|
392
+ api_client.migrations.target_clouds(api_params)['targetClouds'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
393
+ } },
394
+ {'switch' => 'group', 'fieldName' => 'targetGroupId', 'fieldLabel' => 'Group', 'type' => 'select', 'optionSource' => 'targetGroups', 'required' => true, 'defaultFirstOption' => true, 'description' => 'Destination Group'},
395
+ {'switch' => 'pool', 'fieldName' => 'targetPoolId', 'fieldLabel' => 'Resource Pool', 'type' => 'select', 'required' => true, 'defaultFirstOption' => true, 'optionSource' => lambda {|api_client, api_params|
396
+ api_params = api_params.merge({'provisionTypeCode' => 'kvm', 'zoneId' => api_params['targetCloudId'], 'groupId' => api_params['targetGroupId']})
397
+ api_client.options.options_for_source("zonePools", api_params)['data']
398
+ } },
399
+ {'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Virtual Machines', 'type' => 'multiSelect', 'required' => true, 'description' => 'Virtual Machines to be migrated, comma separated list of server names or IDs.', 'optionSource' => lambda {|api_client, api_params|
400
+ api_client.migrations.source_servers(api_params)['sourceServers'].collect {|it| {'name' => it['name'], 'value' => it['id']} }
401
+ } },
402
+ {'fieldName' => 'skipPrechecks', 'fieldLabel' => 'Skip Prechecks', 'type' => 'checkbox', 'required' => false, 'defaultValue' => false},
403
+ {'fieldName' => 'installGuestTools', 'fieldLabel' => 'Install Guest Tools', 'type' => 'checkbox', 'required' => false, 'defaultValue' => true},
404
+ ]
405
+ end
406
+
407
+ def add_migration_advanced_option_types()
408
+ [
409
+ # {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'fieldGroup' => 'Advanced', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}], 'required' => false, 'description' => 'Visibility', 'category' => 'permissions'},
410
+ # {'fieldName' => 'tenants', 'fieldLabel' => 'Tenants', 'fieldGroup' => 'Advanced', 'type' => 'multiSelect', 'optionSource' => lambda { |api_client, api_params|
411
+ # api_client.options.options_for_source("allTenants", {})['data']
412
+ # }},
413
+ ]
414
+ end
415
+
416
+ def update_migration_option_types()
417
+ add_migration_option_types.collect {|it|
418
+ it.delete('required')
419
+ it.delete('defaultValue')
420
+ it.delete('defaultFirstOption')
421
+ it
422
+ }
423
+ end
424
+
425
+ def update_migration_advanced_option_types()
426
+ add_migration_advanced_option_types()
427
+ end
428
+
429
+ def format_migration_status(migration, return_color=cyan)
430
+ # migration statuses: pending, scheduled, precheck, running, failed, completed
431
+ out = ""
432
+ status_string = migration['status']
433
+ if status_string.nil? || status_string.empty? || status_string == "unknown"
434
+ out << "#{white}UNKNOWN#{return_color}"
435
+ elsif status_string == 'completed'
436
+ out << "#{green}#{status_string.upcase}#{return_color}"
437
+ elsif status_string == 'pending' || status_string == 'scheduled' || status_string == 'precheck' || status_string == 'running'
438
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
439
+ else
440
+ out << "#{red}#{status_string ? status_string.upcase : 'N/A'}#{migration['statusMessage'] ? "#{return_color} - #{migration['statusMessage']}" : ''}#{return_color}"
441
+ end
442
+ out
443
+ end
444
+
445
+ def format_migration_server_status(migration_server, return_color=cyan)
446
+ return format_migration_status(migration_server, return_color)
447
+ end
448
+
449
+ def find_migration_by_name_or_id(arg)
450
+ find_by_name_or_id(rest_key, arg)
451
+ end
452
+
453
+ end
@@ -791,7 +791,7 @@ class Morpheus::Cli::NetworksCommand
791
791
  end
792
792
 
793
793
  # Allow IP Override
794
- if network_type['staticOverrideEditable'] && payload['network']['allowStaticOverride'].nil?
794
+ if payload['network']['allowStaticOverride'].nil?
795
795
  if options['allowStaticOverride'] != nil
796
796
  payload['network']['allowStaticOverride'] = options['allowStaticOverride']
797
797
  else
@@ -1,5 +1,4 @@
1
1
  require 'morpheus/cli/cli_command'
2
- require 'money'
3
2
 
4
3
  class Morpheus::Cli::PriceSetsCommand
5
4
  include Morpheus::Cli::CliCommand
@@ -153,7 +152,7 @@ class Morpheus::Cli::PriceSetsCommand
153
152
  {
154
153
  id: it['id'],
155
154
  name: it['name'],
156
- pricing: (it['priceType'] == 'platform' ? '+' : '') + currency_sym(it['currency']) + (it['price'] || 0.0).to_s + (it['additionalPriceUnit'].nil? ? '' : '/' + it['additionalPriceUnit']) + '/' + (it['priceUnit'] || 'month').capitalize
155
+ pricing: (it['priceType'] == 'platform' ? '+' : '') + currency_symbol(it['currency']) + (it['price'] || 0.0).to_s + (it['additionalPriceUnit'].nil? ? '' : '/' + it['additionalPriceUnit']) + '/' + (it['priceUnit'] || 'month').capitalize
157
156
  }
158
157
  end
159
158
  print as_pretty_table(rows, [:id, :name, :pricing], options)
@@ -465,17 +464,13 @@ class Morpheus::Cli::PriceSetsCommand
465
464
 
466
465
  private
467
466
 
468
- def currency_sym(currency)
469
- Money::Currency.new((currency.to_s != '' ? currency : 'usd').to_sym).symbol
470
- end
471
-
472
467
  def price_prefix(price)
473
- (['platform', 'software'].include?(price['priceType']) ? '+' : '') + currency_sym(price['currency'])
468
+ (['platform', 'software'].include?(price['priceType']) ? '+' : '') + currency_symbol(price['currency'])
474
469
  end
475
470
 
476
471
  def price_markup(price)
477
472
  if price['markupType'] == 'fixed'
478
- currency_sym(price['currency']) + format_amount(price['markup'] || 0)
473
+ currency_symbol(price['currency']) + format_amount(price['markup'] || 0)
479
474
  elsif price['markupType'] == 'percent'
480
475
  (price['markupPercent'] || 0).to_s + '%'
481
476
  else
@@ -1,5 +1,4 @@
1
1
  require 'morpheus/cli/cli_command'
2
- require 'money'
3
2
 
4
3
  class Morpheus::Cli::PricesCommand
5
4
  include Morpheus::Cli::CliCommand
@@ -564,22 +563,13 @@ class Morpheus::Cli::PricesCommand
564
563
  (val.to_s =~ /\A\d{1,}\Z/) ? @prices_interface.get_volume_type(val.to_i)['volumeType'] : @prices_interface.list_volume_types({'name' => val})['volumeTypes'].first
565
564
  end
566
565
 
567
- def currency_sym(currency)
568
- begin
569
- Money::Currency.new((currency.to_s.empty? ? 'usd' : currency).to_sym).symbol
570
- rescue
571
- # sometimes '' is getting passed in here, so figure that out...
572
- Money::Currency.new(:usd).symbol
573
- end
574
- end
575
-
576
566
  def price_prefix(price)
577
- (['platform', 'software'].include?(price['priceType']) ? '+' : '') + currency_sym(price['currency'])
567
+ (['platform', 'software'].include?(price['priceType']) ? '+' : '') + currency_symbol(price['currency'])
578
568
  end
579
569
 
580
570
  def price_markup(price)
581
571
  if price['markupType'] == 'fixed'
582
- currency_sym(price['currency']) + format_amount(price['markup'] || 0)
572
+ currency_symbol(price['currency']) + format_amount(price['markup'] || 0)
583
573
  elsif price['markupType'] == 'percent'
584
574
  (price['markupPercent'] || 0).to_s + '%'
585
575
  else