morpheus-cli 5.0.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +12 -0
  4. data/lib/morpheus/api/billing_interface.rb +1 -0
  5. data/lib/morpheus/api/deploy_interface.rb +1 -1
  6. data/lib/morpheus/api/deployments_interface.rb +20 -1
  7. data/lib/morpheus/api/forgot_password_interface.rb +17 -0
  8. data/lib/morpheus/api/instances_interface.rb +7 -0
  9. data/lib/morpheus/api/search_interface.rb +13 -0
  10. data/lib/morpheus/api/servers_interface.rb +7 -0
  11. data/lib/morpheus/api/usage_interface.rb +18 -0
  12. data/lib/morpheus/cli.rb +4 -1
  13. data/lib/morpheus/cli/cli_command.rb +26 -9
  14. data/lib/morpheus/cli/commands/standard/curl_command.rb +3 -5
  15. data/lib/morpheus/cli/commands/standard/history_command.rb +3 -1
  16. data/lib/morpheus/cli/commands/standard/man_command.rb +74 -40
  17. data/lib/morpheus/cli/deploy.rb +199 -90
  18. data/lib/morpheus/cli/deployments.rb +341 -28
  19. data/lib/morpheus/cli/deploys.rb +206 -41
  20. data/lib/morpheus/cli/error_handler.rb +7 -0
  21. data/lib/morpheus/cli/forgot_password.rb +133 -0
  22. data/lib/morpheus/cli/health_command.rb +2 -2
  23. data/lib/morpheus/cli/hosts.rb +169 -32
  24. data/lib/morpheus/cli/instances.rb +83 -32
  25. data/lib/morpheus/cli/invoices_command.rb +33 -16
  26. data/lib/morpheus/cli/logs_command.rb +9 -6
  27. data/lib/morpheus/cli/mixins/deployments_helper.rb +31 -2
  28. data/lib/morpheus/cli/mixins/print_helper.rb +0 -21
  29. data/lib/morpheus/cli/mixins/provisioning_helper.rb +24 -4
  30. data/lib/morpheus/cli/option_types.rb +266 -17
  31. data/lib/morpheus/cli/remote.rb +35 -10
  32. data/lib/morpheus/cli/reports_command.rb +99 -30
  33. data/lib/morpheus/cli/search_command.rb +182 -0
  34. data/lib/morpheus/cli/setup.rb +1 -1
  35. data/lib/morpheus/cli/shell.rb +33 -11
  36. data/lib/morpheus/cli/tasks.rb +20 -21
  37. data/lib/morpheus/cli/usage_command.rb +64 -11
  38. data/lib/morpheus/cli/version.rb +1 -1
  39. data/lib/morpheus/cli/virtual_images.rb +280 -199
  40. data/lib/morpheus/cli/whoami.rb +6 -6
  41. data/lib/morpheus/cli/workflows.rb +33 -40
  42. data/lib/morpheus/formatters.rb +22 -0
  43. data/lib/morpheus/terminal.rb +6 -2
  44. metadata +7 -2
@@ -9,7 +9,7 @@ class Morpheus::Cli::Setup
9
9
 
10
10
  set_command_name :setup
11
11
 
12
- register_subcommands :init
12
+ # register_subcommands :init
13
13
 
14
14
  # no authorization needed
15
15
  def connect(options={})
@@ -92,6 +92,13 @@ class Morpheus::Cli::Shell
92
92
  def recalculate_auto_complete_commands
93
93
  @morpheus_commands = Morpheus::Cli::CliRegistry.all.keys.reject {|k| [:shell].include?(k) }
94
94
  @shell_commands = [:clear, :history, :reload, :help, :exit]
95
+ @shell_command_descriptions = {
96
+ :clear => "Clear terminal output and move cursor to the top",
97
+ :history => "View morpheus shell command history",
98
+ :reload => "Reload the shell, can be useful when developing",
99
+ :help => "Print this help",
100
+ :exit => "Exit the morpheus shell"
101
+ }
95
102
  @alias_commands = Morpheus::Cli::CliRegistry.all_aliases.keys
96
103
  @exploded_commands = []
97
104
  Morpheus::Cli::CliRegistry.all.each do |cmd, klass|
@@ -336,6 +343,11 @@ class Morpheus::Cli::Shell
336
343
  #Morpheus::Logging::DarkPrinter.puts "Shell command: #{input}"
337
344
  input = input.to_s.strip
338
345
 
346
+ # allow pasting in commands that have 'morpheus ' prefix
347
+ if input[0..(prog_name.size)] == "#{prog_name} "
348
+ input = input[(prog_name.size + 1)..-1] || ""
349
+ end
350
+
339
351
  if !input.empty?
340
352
 
341
353
  if input == 'exit'
@@ -345,26 +357,36 @@ class Morpheus::Cli::Shell
345
357
  return 0
346
358
  #exit 0
347
359
  elsif input == 'help'
360
+ out = ""
348
361
  if @temporary_shell_mode
349
- puts "You are in a (temporary) morpheus shell"
362
+ out << "You are in a (temporary) morpheus shell\n"
350
363
  else
351
- puts "You are in a morpheus shell."
364
+ out << "You are in a morpheus shell.\n"
352
365
  end
353
- puts "See the available commands below."
366
+ out << "See the available commands below.\n"
354
367
 
355
- puts "\nCommands:"
368
+ out << "\nCommands:"
356
369
  # commands = @morpheus_commands + @shell_commands
357
- @morpheus_commands.sort.each {|cmd|
358
- puts "\t#{cmd.to_s}"
370
+ # @morpheus_commands.sort.each {|cmd|
371
+ # out << "\t#{cmd.to_s}\n"
372
+ # }
373
+ sorted_commands = Morpheus::Cli::CliRegistry.all.values.sort { |x,y| x.command_name.to_s <=> y.command_name.to_s }
374
+ sorted_commands.each {|cmd|
375
+ # JD: not ready to show description yet, gotta finish filling in every command first
376
+ # maybe change 'View and manage' to something more concise like 'Manage'
377
+ # out << "\t#{cmd.command_name.to_s.ljust(28, ' ')} #{cmd.command_description}\n"
378
+ out << "\t#{cmd.command_name.to_s}\n"
359
379
  }
360
380
  #puts "\n"
361
- puts "\nShell Commands:"
381
+ out << "\nShell Commands:\n"
362
382
  @shell_commands.each {|cmd|
363
- puts "\t#{cmd.to_s}"
383
+ # out << "\t#{cmd.to_s.ljust(28, ' ')} #{@shell_command_descriptions ? @shell_command_descriptions[cmd] : ''}\n"
384
+ out << "\t#{cmd.to_s}\n"
364
385
  }
365
- puts "\n"
366
- puts "For more information, see https://github.com/gomorpheus/morpheus-cli/wiki"
367
- #print "\n"
386
+ out << "\n"
387
+ out << "For more information, see https://github.com/gomorpheus/morpheus-cli/wiki"
388
+ out << "\n"
389
+ print out
368
390
  return 0
369
391
  elsif input =~ /^\s*#/
370
392
  Morpheus::Logging::DarkPrinter.puts "ignored comment: #{input}" if Morpheus::Logging.debug?
@@ -27,34 +27,31 @@ class Morpheus::Cli::Tasks
27
27
  params = {}
28
28
  options = {}
29
29
  optparse = Morpheus::Cli::OptionParser.new do |opts|
30
- opts.banner = subcommand_usage()
30
+ opts.banner = subcommand_usage("[search]")
31
31
  opts.on('-t', '--type x,y,z', Array, "Filter by task type code(s)") do |val|
32
32
  params['taskTypeCodes'] = val
33
33
  end
34
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
34
+ build_standard_list_options(opts, options)
35
+ opts.footer = "List tasks."
35
36
  end
36
37
  optparse.parse!(args)
38
+ connect(options)
37
39
  if args.count > 0
38
- raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
40
+ options[:phrase] = args.join(" ")
39
41
  end
40
- connect(options)
41
- begin
42
- params.merge!(parse_list_options(options))
43
- @tasks_interface.setopts(options)
44
- if options[:dry_run]
45
- print_dry_run @tasks_interface.dry.list(params)
46
- return
47
- end
48
- json_response = @tasks_interface.list(params)
49
-
50
- render_result = render_with_format(json_response, options, 'tasks')
51
- return 0 if render_result
52
-
42
+ params.merge!(parse_list_options(options))
43
+ @tasks_interface.setopts(options)
44
+ if options[:dry_run]
45
+ print_dry_run @tasks_interface.dry.list(params)
46
+ return
47
+ end
48
+ json_response = @tasks_interface.list(params)
49
+ tasks = json_response['tasks']
50
+ render_response(json_response, options, 'tasks') do
53
51
  title = "Morpheus Tasks"
54
52
  subtitles = []
55
53
  subtitles += parse_list_subtitles(options)
56
54
  print_h1 title, subtitles
57
- tasks = json_response['tasks']
58
55
  if tasks.empty?
59
56
  print cyan,"No tasks found.",reset,"\n"
60
57
  else
@@ -63,11 +60,13 @@ class Morpheus::Cli::Tasks
63
60
  print_results_pagination(json_response)
64
61
  end
65
62
  print reset,"\n"
66
- return 0
67
- rescue RestClient::Exception => e
68
- print_rest_exception(e, options)
69
- exit 1
70
63
  end
64
+ if tasks.empty?
65
+ return 1, "no tasks found"
66
+ else
67
+ return 0, nil
68
+ end
69
+
71
70
  end
72
71
 
73
72
  def get(args)
@@ -2,18 +2,18 @@ require 'morpheus/cli/cli_command'
2
2
 
3
3
  # CLI command usages
4
4
  # UI is Costing - Usage
5
- # API is /billing and returns usages
5
+ # API is /usage and returns usages
6
6
  class Morpheus::Cli::UsageCommand
7
7
  include Morpheus::Cli::CliCommand
8
8
  include Morpheus::Cli::OptionSourceHelper
9
9
 
10
10
  set_command_name :'usage'
11
11
 
12
- register_subcommands :list #, :list_tenant, :list_clouds, :list_zones, :list_zones, :list_zones, :list_zones
12
+ register_subcommands :list, :get
13
13
 
14
14
  def connect(opts)
15
15
  @api_client = establish_remote_appliance_connection(opts)
16
- @billing_interface = @api_client.billing
16
+ @usage_interface = @api_client.usage
17
17
  end
18
18
 
19
19
  def handle(args)
@@ -33,10 +33,10 @@ class Morpheus::Cli::UsageCommand
33
33
  options[:cloud] = val
34
34
  end
35
35
  opts.on('--start DATE', String, "Start date in the format YYYY-MM-DD.") do |val|
36
- params['startDate'] = val #parse_time(val).utc.iso8601
36
+ params['startDate'] = val # parse_time(val).utc.iso8601
37
37
  end
38
- opts.on('--end DATE', String, "End date in the format YYYY-MM-DD. Default is now.") do |val|
39
- params['endDate'] = val #parse_time(val).utc.iso8601
38
+ opts.on('--end DATE', String, "End date in the format YYYY-MM-DD. Default is the current date.") do |val|
39
+ params['endDate'] = val # parse_time(val).utc.iso8601
40
40
  end
41
41
  opts.on('--sigdig DIGITS', "Significant digits when rounding cost values for display as currency. Default is 5.") do |val|
42
42
  options[:sigdig] = val.to_i
@@ -64,12 +64,12 @@ class Morpheus::Cli::UsageCommand
64
64
  }
65
65
  end
66
66
 
67
- @billing_interface.setopts(options)
67
+ @usage_interface.setopts(options)
68
68
  if options[:dry_run]
69
- print_dry_run @billing_interface.dry.list(params)
69
+ print_dry_run @usage_interface.dry.list(params)
70
70
  return
71
71
  end
72
- json_response = @billing_interface.list(params)
72
+ json_response = @usage_interface.list(params)
73
73
  usages = json_response[usage_list_key]
74
74
  render_response(json_response, options, usage_list_key) do
75
75
  print_h1 "Morpheus Usages", parse_list_subtitles(options), options
@@ -85,6 +85,7 @@ class Morpheus::Cli::UsageCommand
85
85
  "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
86
86
  "End Date" => lambda {|it| format_local_dt(it['endDate']) },
87
87
  "Usage Status" => lambda {|it| format_usage_status(it) },
88
+ "Usage Cost" => lambda {|it| format_money(it['costDetails']['cost'], it['currency'] || 'USD', {sigdig: (options[:sigdig] || 5)}) },
88
89
  "Usage Price" => lambda {|it| format_money(it['price'], it['currency'] || 'USD', {sigdig: (options[:sigdig] || 5)}) },
89
90
  }
90
91
  print as_pretty_table(usages, list_columns.upcase_keys!, options)
@@ -99,6 +100,58 @@ class Morpheus::Cli::UsageCommand
99
100
  end
100
101
  end
101
102
 
103
+ def get(args)
104
+ params = {}
105
+ options = {}
106
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
107
+ opts.banner = subcommand_usage("[usage]")
108
+ build_standard_get_options(opts, options)
109
+ opts.footer = <<-EOT
110
+ Get details about a specific usage.
111
+ [usage] is required. This is the id of a usage record.
112
+ EOT
113
+ end
114
+ optparse.parse!(args)
115
+ verify_args!(args:args, optparse:optparse, min:1)
116
+ connect(options)
117
+ id_list = parse_id_list(args)
118
+ return run_command_for_each_arg(id_list) do |arg|
119
+ _get(arg, params, options)
120
+ end
121
+ end
122
+
123
+ def _get(id, params, options)
124
+ usage = nil
125
+ @usage_interface.setopts(options)
126
+ if options[:dry_run]
127
+ print_dry_run @usage_interface.dry.get(id, params)
128
+ return
129
+ end
130
+ json_response = @usage_interface.get(id, params)
131
+ usage = json_response[usage_object_key]
132
+ render_response(json_response, options, usage_object_key) do
133
+ print_h1 "Usage Details", [], options
134
+ print cyan
135
+ show_columns = {
136
+ "ID" => 'id',
137
+ "Cloud" => 'zoneName',
138
+ "Type" => lambda {|it| format_usage_type(it) },
139
+ "Name" => 'name',
140
+ "Plan" => 'planName',
141
+ "Start Date" => lambda {|it| format_local_dt(it['startDate']) },
142
+ "End Date" => lambda {|it| format_local_dt(it['endDate']) },
143
+ "Usage Status" => lambda {|it| format_usage_status(it) },
144
+ "Usage Cost" => lambda {|it| format_money(it['costDetails']['cost'], it['currency'] || 'USD', {sigdig: (options[:sigdig] || 5)}) },
145
+ "Usage Price" => lambda {|it| format_money(it['price'], it['currency'] || 'USD', {sigdig: (options[:sigdig] || 5)}) },
146
+ }
147
+ print_description_list(show_columns, usage)
148
+
149
+ # print_h2 "Applicable Prices"
150
+
151
+ print reset,"\n"
152
+ end
153
+ return 0, nil
154
+ end
102
155
 
103
156
  private
104
157
 
@@ -141,9 +194,9 @@ class Morpheus::Cli::UsageCommand
141
194
  #return usage['status'].to_s.capitalize
142
195
  status_string = usage['status'].to_s
143
196
  if status_string == 'stopped'
144
- return "#{cyan}#{status_string.upcase}#{return_color}"
197
+ return "#{red}#{status_string.upcase}#{return_color}"
145
198
  else
146
- return "#{cyan}#{status_string.upcase}#{return_color}"
199
+ return "#{green}#{status_string.upcase}#{return_color}"
147
200
  end
148
201
  end
149
202
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "5.0.0"
4
+ VERSION = "5.0.1"
5
5
  end
6
6
  end
@@ -27,6 +27,7 @@ class Morpheus::Cli::VirtualImages
27
27
  end
28
28
 
29
29
  def list(args)
30
+ params = {}
30
31
  options = {}
31
32
  optparse = Morpheus::Cli::OptionParser.new do |opts|
32
33
  opts.banner = subcommand_usage()
@@ -42,42 +43,32 @@ class Morpheus::Cli::VirtualImages
42
43
  opts.on('--system', "System Images" ) do
43
44
  options[:filterType] = 'System'
44
45
  end
45
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
46
+ build_standard_list_options(opts, options)
46
47
  opts.footer = "List virtual images."
47
48
  end
48
49
  optparse.parse!(args)
49
50
  connect(options)
50
- begin
51
- params = {}
52
- params.merge!(parse_list_options(options))
53
- if options[:imageType]
54
- params[:imageType] = options[:imageType]
55
- end
56
- if options[:filterType]
57
- params[:filterType] = options[:filterType]
58
- end
59
- @virtual_images_interface.setopts(options)
60
- if options[:dry_run]
61
- print_dry_run @virtual_images_interface.dry.get(params)
62
- return
63
- end
64
- json_response = @virtual_images_interface.get(params)
65
-
66
- if options[:json]
67
- puts as_json(json_response, options, "virtualImages")
68
- return 0
69
- elsif options[:yaml]
70
- puts as_yaml(json_response, options, "virtualImages")
71
- return 0
72
- elsif options[:csv]
73
- puts records_as_csv(json_response["virtualImages"], options)
74
- return 0
75
- end
76
-
77
-
78
- images = json_response['virtualImages']
51
+ # verify_args!(args:args, optparse:optparse, count:0)
52
+ if args.count > 0
53
+ options[:phrase] = args.join(" ")
54
+ end
55
+ params.merge!(parse_list_options(options))
56
+ if options[:imageType]
57
+ params[:imageType] = options[:imageType]
58
+ end
59
+ if options[:filterType]
60
+ params[:filterType] = options[:filterType]
61
+ end
62
+ @virtual_images_interface.setopts(options)
63
+ if options[:dry_run]
64
+ print_dry_run @virtual_images_interface.dry.list(params)
65
+ return
66
+ end
67
+ json_response = @virtual_images_interface.list(params)
68
+ images = json_response['virtualImages']
69
+ render_response(json_response, options, 'virtualImages') do
79
70
  title = "Morpheus Virtual Images"
80
- subtitles = []
71
+ subtitles = parse_list_subtitles(options)
81
72
  if options[:imageType]
82
73
  subtitles << "Image Type: #{options[:imageType]}".strip
83
74
  end
@@ -91,6 +82,7 @@ class Morpheus::Cli::VirtualImages
91
82
  if images.empty?
92
83
  print cyan,"No virtual images found.",reset,"\n"
93
84
  else
85
+ # print as_pretty_table(images, virtual_image_column_definitions.upcase_keys!, options)
94
86
  rows = images.collect do |image|
95
87
  image_type = virtual_image_type_for_name_or_code(image['imageType'])
96
88
  image_type_display = image_type ? "#{image_type['name']}" : image['imageType']
@@ -103,117 +95,134 @@ class Morpheus::Cli::VirtualImages
103
95
  print_results_pagination(json_response)
104
96
  end
105
97
  print reset,"\n"
106
-
107
- return 0
108
- rescue RestClient::Exception => e
109
- print_rest_exception(e, options)
110
- exit 1
98
+ end
99
+ if images.empty?
100
+ return -1, "no virtual images found"
101
+ else
102
+ return 0, nil
111
103
  end
112
104
  end
113
105
 
114
106
  def get(args)
107
+ params = {}
115
108
  options = {}
116
- show_details = false
117
109
  optparse = Morpheus::Cli::OptionParser.new do |opts|
118
- opts.banner = subcommand_usage("[name]")
110
+ opts.banner = subcommand_usage("[image]")
119
111
  opts.on('--details', "Show more details." ) do
120
- show_details = true
112
+ options[:details] = true
121
113
  end
122
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
123
- opts.footer = "Get details about a virtual image." + "\n" +
124
- "[name] is required. This is the name or id of a virtual image."
114
+ build_standard_get_options(opts, options)
115
+ opts.footer = <<-EOT
116
+ Get details about a virtual image.
117
+ [image] is required. This is the name or id of a virtual image.
118
+ EOT
125
119
  end
126
120
  optparse.parse!(args)
127
- if args.count < 1
128
- puts optparse
129
- exit 1
130
- end
131
- image_name = args[0]
121
+ verify_args!(args:args, optparse:optparse, min:1)
132
122
  connect(options)
133
- begin
134
- @virtual_images_interface.setopts(options)
135
- if options[:dry_run]
136
- if args[0].to_s =~ /\A\d{1,}\Z/
137
- print_dry_run @virtual_images_interface.dry.get(args[0].to_i)
123
+ id_list = parse_id_list(args)
124
+ # lookup IDs if names are given
125
+ id_list = id_list.collect do |id|
126
+ if id.to_s =~ /\A\d{1,}\Z/
127
+ id
128
+ else
129
+ image = find_virtual_image_by_name_or_id(id)
130
+ if image
131
+ image['id']
138
132
  else
139
- print_dry_run @virtual_images_interface.dry.get({name:args[0]})
133
+ raise_command_error "virtual image not found for name '#{id}'"
140
134
  end
141
- return
142
- end
143
- image = find_virtual_image_by_name_or_id(image_name)
144
- return 1 if image.nil?
145
- # refetch
146
- json_response = @virtual_images_interface.get(image['id'])
147
- if options[:json]
148
- puts as_json(json_response, options, "virtualImage")
149
- return 0
150
- elsif options[:yaml]
151
- puts as_yaml(json_response, options, "virtualImage")
152
- return 0
153
- elsif options[:csv]
154
- puts records_as_csv([json_response["virtualImage"]], options)
155
- return 0
156
135
  end
136
+ end
137
+ return run_command_for_each_arg(id_list) do |arg|
138
+ _get(arg, params, options)
139
+ end
140
+ end
157
141
 
142
+ def _get(id, params, options)
143
+ @virtual_images_interface.setopts(options)
144
+ if options[:dry_run]
145
+ print_dry_run @virtual_images_interface.dry.get(id.to_i)
146
+ return
147
+ end
148
+ json_response = @virtual_images_interface.get(id.to_i)
158
149
  image = json_response['virtualImage']
150
+ image_config = image['config'] || {}
159
151
  image_files = json_response['cloudFiles'] || json_response['files']
160
-
161
-
162
152
  image_type = virtual_image_type_for_name_or_code(image['imageType'])
163
153
  image_type_display = image_type ? "#{image_type['name']}" : image['imageType']
164
- print_h1 "Virtual Image Details"
165
- print cyan
166
- description_cols = {
167
- "ID" => 'id',
168
- "Name" => 'name',
169
- "Type" => lambda {|it| image_type_display },
170
- "Storage" => lambda {|it| !image['storageProvider'].nil? ? image['storageProvider']['name'] : 'Default' },
171
- "Size" => lambda {|it| image['rawSize'].nil? ? 'Unknown' : "#{Filesize.from("#{image['rawSize']} B").pretty}" },
172
- "Source" => lambda {|it| image['userUploaded'] ? "#{green}UPLOADED#{cyan}" : (image['systemImage'] ? 'SYSTEM' : "#{white}SYNCED#{cyan}") },
173
- # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
174
- # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
175
- }
176
- advanced_description_cols = {
177
- "OS Type" => lambda {|it| it['osType'] ? it['osType']['name'] : "" },
178
- "Min Memory" => lambda {|it| it['minRam'].to_i != 0 ? Filesize.from("#{it['minRam']} B").pretty : "" },
179
- "Cloud Init?" => lambda {|it| format_boolean it['osType'] },
180
- "Install Agent?" => lambda {|it| format_boolean it['osType'] },
181
- "SSH Username" => lambda {|it| it['sshUsername'] },
182
- "SSH Password" => lambda {|it| it['sshPassword'] },
183
- "User Data" => lambda {|it| it['userData'] },
184
- "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
185
- "Tenants" => lambda {|it| format_tenants(it['accounts']) },
186
- "Auto Join Domain?" => lambda {|it| format_boolean it['isAutoJoinDomain'] },
187
- "VirtIO Drivers Loaded?" => lambda {|it| format_boolean it['virtioSupported'] },
188
- "VM Tools Installed?" => lambda {|it| format_boolean it['vmToolsInstalled'] },
189
- "Force Guest Customization?" => lambda {|it| format_boolean it['isForceCustomization'] },
190
- "Trial Version" => lambda {|it| format_boolean it['trialVersion'] },
191
- "Sysprep Enabled?" => lambda {|it| format_boolean it['isSysprep'] },
192
- }
193
- if show_details
194
- description_cols.merge!(advanced_description_cols)
195
- end
196
- print_description_list(description_cols, image)
197
-
198
- if image_files
199
- print_h2 "Files (#{image_files.size})"
200
- # image_files.each {|image_file|
201
- # pretty_filesize = Filesize.from("#{image_file['size']} B").pretty
202
- # print cyan," = #{image_file['name']} [#{pretty_filesize}]", "\n"
203
- # }
204
- image_file_rows = image_files.collect do |image_file|
205
-
206
- {filename: image_file['name'], size: Filesize.from("#{image_file['size']} B").pretty}
154
+ render_response(json_response, options, 'virtualImage') do
155
+ print_h1 "Virtual Image Details", [], options
156
+ description_cols = {
157
+ "ID" => 'id',
158
+ "Name" => 'name',
159
+ "Type" => lambda {|it| image_type_display },
160
+ "Storage" => lambda {|it| !image['storageProvider'].nil? ? image['storageProvider']['name'] : 'Default' },
161
+ "Size" => lambda {|it| image['rawSize'].nil? ? 'Unknown' : "#{Filesize.from("#{image['rawSize']} B").pretty}" },
162
+ "Azure Publisher" => lambda {|it| image_config['publisher'] },
163
+ "Azure Offer" => lambda {|it| image_config['offer'] },
164
+ "Azure Sku" => lambda {|it| image_config['sku'] },
165
+ "Azure Version" => lambda {|it| image_config['version'] },
166
+ "Source" => lambda {|it| image['userUploaded'] ? "#{green}UPLOADED#{cyan}" : (image['systemImage'] ? 'SYSTEM' : "#{white}SYNCED#{cyan}") },
167
+ # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
168
+ # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
169
+ }
170
+ if image['imageType'] == "azure-reference" || image['imageType'] == "azure"
171
+ description_cols.delete("Size")
172
+ description_cols.delete("Storage")
173
+ description_cols["Source"] = lambda {|it| "#{bold}#{cyan}AZURE#{reset}#{cyan}" }
174
+ else
175
+ description_cols.delete("Azure Marketplace")
176
+ description_cols.delete("Azure Marketplace Publisher")
177
+ description_cols.delete("Azure Marketplace Sku")
178
+ description_cols.delete("Azure Marketplace Offer")
179
+ description_cols.delete("Azure Marketplace Version")
207
180
  end
208
- print cyan
209
- print as_pretty_table(image_file_rows, [:filename, :size])
210
- # print reset,"\n"
181
+ advanced_description_cols = {
182
+ "OS Type" => lambda {|it| it['osType'] ? it['osType']['name'] : "" },
183
+ "Min Memory" => lambda {|it| it['minRam'].to_i != 0 ? Filesize.from("#{it['minRam']} B").pretty : "" },
184
+ "Cloud Init?" => lambda {|it| format_boolean it['osType'] },
185
+ "Install Agent?" => lambda {|it| format_boolean it['osType'] },
186
+ "SSH Username" => lambda {|it| it['sshUsername'] },
187
+ "SSH Password" => lambda {|it| it['sshPassword'] },
188
+ "User Data" => lambda {|it| it['userData'] },
189
+ "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
190
+ "Tenants" => lambda {|it| format_tenants(it['accounts']) },
191
+ "Auto Join Domain?" => lambda {|it| format_boolean it['isAutoJoinDomain'] },
192
+ "VirtIO Drivers Loaded?" => lambda {|it| format_boolean it['virtioSupported'] },
193
+ "VM Tools Installed?" => lambda {|it| format_boolean it['vmToolsInstalled'] },
194
+ "Force Guest Customization?" => lambda {|it| format_boolean it['isForceCustomization'] },
195
+ "Trial Version" => lambda {|it| format_boolean it['trialVersion'] },
196
+ "Sysprep Enabled?" => lambda {|it| format_boolean it['isSysprep'] },
197
+ }
198
+ if options[:details]
199
+ description_cols.merge!(advanced_description_cols)
200
+ end
201
+ print_description_list(description_cols, image)
202
+
203
+ if image_files
204
+ print_h2 "Files (#{image_files.size})"
205
+ # image_files.each {|image_file|
206
+ # pretty_filesize = Filesize.from("#{image_file['size']} B").pretty
207
+ # print cyan," = #{image_file['name']} [#{pretty_filesize}]", "\n"
208
+ # }
209
+ image_file_rows = image_files.collect do |image_file|
210
+ {filename: image_file['name'], size: Filesize.from("#{image_file['size']} B").pretty}
211
+ end
212
+ print cyan
213
+ print as_pretty_table(image_file_rows, [:filename, :size])
214
+ # print reset,"\n"
215
+ end
216
+
217
+ if options[:details] && image_config && !image_config.empty?
218
+ print_h2 "Config", options
219
+ print cyan
220
+ print as_description_list(image_config, image_config.keys, options)
221
+ print "\n", reset
222
+ end
223
+ print reset,"\n"
211
224
  end
212
- print reset,"\n"
213
- rescue RestClient::Exception => e
214
- print_rest_exception(e, options)
215
- exit 1
216
- end
225
+ return 0, nil
217
226
  end
218
227
 
219
228
  def update(args)
@@ -339,6 +348,19 @@ class Morpheus::Cli::VirtualImages
339
348
  opts.on( '-U', '--url URL', "Image File URL. This can be used instead of uploading local files." ) do |val|
340
349
  file_url = val
341
350
  end
351
+ opts.on( '-c', '--cloud CLOUD', "Cloud to scope image to, certain types require a cloud to be selected, eg. Azure Reference" ) do |val|
352
+ # options[:cloud] = val
353
+ options[:options]['cloud'] = val
354
+ end
355
+ opts.on( '--azure-offer OFFER', String, "Azure Reference offer value, only applies to Azure Reference" ) do |val|
356
+ options[:options]['offer'] = val
357
+ end
358
+ opts.on( '--azure-sku SKU', String, "Azure SKU value, only applies to Azure Reference" ) do |val|
359
+ options[:options]['sku'] = val
360
+ end
361
+ opts.on( '--azure-version VERSION', String, "Azure Version value, only applies to Azure Reference" ) do |val|
362
+ options[:options]['version'] = val
363
+ end
342
364
  opts.on('--tenants LIST', Array, "Tenant Access, comma separated list of account IDs") do |list|
343
365
  if list.size == 1 && list[0] == 'null' # hacky way to clear it
344
366
  tenants_list = []
@@ -346,7 +368,10 @@ class Morpheus::Cli::VirtualImages
346
368
  tenants_list = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
347
369
  end
348
370
  end
349
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
371
+ # build_option_type_options(opts, options, add_virtual_image_option_types)
372
+ # build_option_type_options(opts, options, add_virtual_image_advanced_option_types)
373
+ build_standard_add_options(opts, options)
374
+
350
375
  opts.footer = "Create a virtual image."
351
376
  end
352
377
  optparse.parse!(args)
@@ -368,26 +393,43 @@ class Morpheus::Cli::VirtualImages
368
393
  options[:options]['name'] ||= image_name
369
394
  end
370
395
 
371
- if image_type_name
372
- image_type = virtual_image_type_for_name_or_code(image_type_name)
373
- # fix issue with api returning imageType vmware instead of vmdk
374
- if image_type.nil? && image_type_name == 'vmware'
375
- image_type = virtual_image_type_for_name_or_code('vmdk')
376
- elsif image_type.nil? && image_type_name == 'vmdk'
377
- image_type = virtual_image_type_for_name_or_code('vmware')
396
+ payload = {}
397
+ if options[:payload]
398
+ payload = options[:payload]
399
+ payload.deep_merge!({'virtualImage' => parse_passed_options(options)})
400
+ else
401
+ payload.deep_merge!({'virtualImage' => parse_passed_options(options)})
402
+ virtual_image_payload = {}
403
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt(add_virtual_image_option_types, options[:options], @api_client, options[:params])
404
+ if image_type_name
405
+ image_type = virtual_image_type_for_name_or_code(image_type_name)
406
+ # fix issue with api returning imageType vmware instead of vmdk
407
+ if image_type.nil? && image_type_name == 'vmware'
408
+ image_type = virtual_image_type_for_name_or_code('vmdk')
409
+ elsif image_type.nil? && image_type_name == 'vmdk'
410
+ image_type = virtual_image_type_for_name_or_code('vmware')
411
+ end
412
+ if image_type.nil?
413
+ print_red_alert "Virtual Image Type not found by code '#{image_type_name}'"
414
+ return 1
415
+ end
416
+ # options[:options] ||= {}
417
+ # options[:options]['imageType'] ||= image_type['code']
418
+ else
419
+ image_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'imageType', 'fieldLabel' => 'Image Type', 'type' => 'select', 'optionSource' => 'virtualImageTypes', 'required' => true, 'description' => 'Select Virtual Image Type.', 'displayOrder' => 2}],options[:options],@api_client,{})
420
+ image_type = virtual_image_type_for_name_or_code(image_type_prompt['imageType'])
378
421
  end
379
- if image_type.nil?
380
- print_red_alert "Virtual Image Type not found by code '#{image_type_name}'"
381
- return 1
422
+
423
+ # azure requires us to search the marketplace to select publisher, cloud, offerm sku
424
+ if image_type['code'] == "azure-reference" || image_type['code'] == "azure"
425
+ cloud_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'fieldLabel' => 'Cloud', 'type' => 'select', 'optionSource' => 'clouds', 'required' => true, 'description' => 'Select Azure Cloud.', :fmt=>:natural}],options[:options],@api_client, {zoneTypeWhiteList: 'azure'})
426
+ cloud_id = cloud_prompt['cloud'].to_i
427
+
428
+ marketplace_config = prompt_azure_marketplace(cloud_id, options)
429
+ virtual_image_payload['config'] ||= {}
430
+ virtual_image_payload['config'].deep_merge!(marketplace_config)
382
431
  end
383
- # options[:options] ||= {}
384
- # options[:options]['imageType'] ||= image_type['code']
385
- else
386
- image_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'imageType', 'fieldLabel' => 'Image Type', 'type' => 'select', 'optionSource' => 'virtualImageTypes', 'required' => true, 'description' => 'Select Virtual Image Type.', 'displayOrder' => 2}],options[:options],@api_client,{})
387
- image_type = virtual_image_type_for_name_or_code(image_type_prompt['imageType'])
388
- end
389
432
 
390
- begin
391
433
  my_option_types = add_virtual_image_option_types(image_type, !file_url)
392
434
  # if options[:no_prompt]
393
435
  # my_option_types.each do |it|
@@ -396,9 +438,9 @@ class Morpheus::Cli::VirtualImages
396
438
  # end
397
439
  # end
398
440
  # end
399
- params = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, options[:params])
400
- params.deep_compact!
401
- virtual_image_payload = {}.merge(params)
441
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, options[:params])
442
+ v_prompt.deep_compact!
443
+ virtual_image_payload.deep_merge!(v_prompt)
402
444
  virtual_image_files = virtual_image_payload.delete('virtualImageFiles')
403
445
  virtual_image_payload['imageType'] = image_type['code']
404
446
  storage_provider_id = virtual_image_payload.delete('storageProviderId')
@@ -412,61 +454,62 @@ class Morpheus::Cli::VirtualImages
412
454
  if virtual_image_payload && virtual_image_payload['imageType'] == 'vmware'
413
455
  virtual_image_payload['imageType'] == 'vmdk'
414
456
  end
415
- payload = {virtualImage: virtual_image_payload}
416
- @virtual_images_interface.setopts(options)
417
- if options[:dry_run]
418
- print_dry_run @virtual_images_interface.dry.create(payload)
419
- if file_url
420
- print_dry_run @virtual_images_interface.dry.upload_by_url(":id", file_url, file_name)
421
- elsif virtual_image_files && !virtual_image_files.empty?
422
- virtual_image_files.each do |key, filepath|
423
- print_dry_run @virtual_images_interface.dry.upload(":id", "(Contents of file #{filepath})")
424
- end
425
- end
426
- return
427
- end
428
-
429
- json_response = @virtual_images_interface.create(payload)
430
- virtual_image = json_response['virtualImage']
431
-
432
- if options[:json]
433
- print JSON.pretty_generate(json_response)
434
- elsif !options[:quiet]
435
- print "\n", cyan, "Virtual Image #{virtual_image['name']} created successfully", reset, "\n\n"
436
- end
457
+ #payload = {'virtualImage' => virtual_image_payload}
458
+ payload.deep_merge!({'virtualImage' => virtual_image_payload})
459
+ end
437
460
 
438
- # now upload the file, do this in the background maybe?
461
+ @virtual_images_interface.setopts(options)
462
+ if options[:dry_run]
463
+ print_dry_run @virtual_images_interface.dry.create(payload)
439
464
  if file_url
440
- unless options[:quiet]
441
- print cyan, "Uploading file by url #{file_url} ...", reset, "\n"
442
- end
443
- upload_json_response = @virtual_images_interface.upload_by_url(virtual_image['id'], file_url, file_name)
444
- if options[:json]
445
- print JSON.pretty_generate(upload_json_response)
446
- end
465
+ print_dry_run @virtual_images_interface.dry.upload_by_url(":id", file_url, file_name)
447
466
  elsif virtual_image_files && !virtual_image_files.empty?
448
467
  virtual_image_files.each do |key, filepath|
449
- unless options[:quiet]
450
- print cyan, "Uploading file (#{key}) #{filepath} ...", reset, "\n"
451
- end
452
- image_file = File.new(filepath, 'rb')
453
- upload_json_response = @virtual_images_interface.upload(virtual_image['id'], image_file, file_name)
454
- if options[:json]
455
- print JSON.pretty_generate(upload_json_response)
456
- end
468
+ print_dry_run @virtual_images_interface.dry.upload(":id", "(Contents of file #{filepath})")
457
469
  end
458
- else
459
- puts cyan, "No files uploaded.", reset
460
470
  end
471
+ return
472
+ end
461
473
 
462
- if !options[:json]
463
- get([virtual_image['id']])
474
+ json_response = @virtual_images_interface.create(payload)
475
+ virtual_image = json_response['virtualImage']
476
+
477
+ # if options[:json]
478
+ # print JSON.pretty_generate(json_response)
479
+ # elsif !options[:quiet]
480
+ # print "\n", cyan, "Virtual Image #{virtual_image['name']} created successfully", reset, "\n\n"
481
+ # end
482
+
483
+ # now upload the file, do this in the background maybe?
484
+ if file_url
485
+ unless options[:quiet]
486
+ print cyan, "Uploading file by url #{file_url} ...", reset, "\n"
487
+ end
488
+ upload_json_response = @virtual_images_interface.upload_by_url(virtual_image['id'], file_url, file_name)
489
+ # if options[:json]
490
+ # print JSON.pretty_generate(upload_json_response)
491
+ # end
492
+ elsif virtual_image_files && !virtual_image_files.empty?
493
+ virtual_image_files.each do |key, filepath|
494
+ unless options[:quiet]
495
+ print cyan, "Uploading file (#{key}) #{filepath} ...", reset, "\n"
496
+ end
497
+ image_file = File.new(filepath, 'rb')
498
+ upload_json_response = @virtual_images_interface.upload(virtual_image['id'], image_file, file_name)
499
+ # if options[:json]
500
+ # print JSON.pretty_generate(upload_json_response)
501
+ # end
464
502
  end
503
+ else
504
+ # puts cyan, "No files uploaded.", reset
505
+ end
465
506
 
466
- rescue RestClient::Exception => e
467
- print_rest_exception(e, options)
468
- exit 1
507
+ render_response(json_response, options, 'virtualImage') do
508
+ print_green_success "Added virtual image #{virtual_image['name']}"
509
+ return _get(virtual_image["id"], {}, options)
469
510
  end
511
+ return 0, nil
512
+
470
513
  end
471
514
 
472
515
  def add_file(args)
@@ -678,11 +721,11 @@ class Morpheus::Cli::VirtualImages
678
721
  #{'fieldName' => 'imageType', 'fieldLabel' => 'Image Type', 'type' => 'select', 'optionSource' => 'virtualImageTypes', 'required' => true, 'description' => 'Select Virtual Image Type.', 'displayOrder' => 2},
679
722
  {'fieldName' => 'osType', 'fieldLabel' => 'OS Type', 'type' => 'select', 'optionSource' => 'osTypes', 'required' => false, 'description' => 'Select OS Type.', 'displayOrder' => 3},
680
723
  {'fieldName' => 'minRam', 'fieldLabel' => 'Minimum Memory (MB)', 'type' => 'number', 'required' => false, 'description' => 'Minimum Memory (MB)', 'displayOrder' => 4},
681
- {'fieldName' => 'isCloudInit', 'fieldLabel' => 'Cloud Init Enabled?', 'type' => 'checkbox', 'required' => false, 'description' => 'Cloud Init Enabled?', 'displayOrder' => 4},
682
- {'fieldName' => 'installAgent', 'fieldLabel' => 'Install Agent?', 'type' => 'checkbox', 'required' => false, 'description' => 'Install Agent?', 'displayOrder' => 4},
683
- {'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text', 'required' => false, 'description' => 'Enter an SSH Username', 'displayOrder' => 5},
684
- {'fieldName' => 'sshPassword', 'fieldLabel' => 'SSH Password', 'type' => 'password', 'required' => false, 'description' => 'Enter an SSH Password', 'displayOrder' => 6},
685
- {'fieldName' => 'storageProviderId', 'type' => 'select', 'fieldLabel' => 'Storage Provider', 'optionSource' => 'storageProviders', 'required' => false, 'description' => 'Select Storage Provider.', 'displayOrder' => 7},
724
+ {'fieldName' => 'isCloudInit', 'fieldLabel' => 'Cloud Init Enabled?', 'type' => 'checkbox', 'required' => false, 'description' => 'Cloud Init Enabled?', 'displayOrder' => 5},
725
+ {'fieldName' => 'installAgent', 'fieldLabel' => 'Install Agent?', 'type' => 'checkbox', 'required' => false, 'description' => 'Install Agent?', 'displayOrder' => 6},
726
+ {'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text', 'required' => false, 'description' => 'Enter an SSH Username', 'displayOrder' => 7},
727
+ {'fieldName' => 'sshPassword', 'fieldLabel' => 'SSH Password', 'type' => 'password', 'required' => false, 'description' => 'Enter an SSH Password', 'displayOrder' => 8},
728
+ {'fieldName' => 'storageProviderId', 'type' => 'select', 'fieldLabel' => 'Storage Provider', 'optionSource' => 'storageProviders', 'required' => false, 'description' => 'Select Storage Provider.', 'displayOrder' => 9},
686
729
  {'fieldName' => 'userData', 'fieldLabel' => 'Cloud-Init User Data', 'type' => 'textarea', 'required' => false, 'displayOrder' => 10},
687
730
  {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}], 'required' => false, 'description' => 'Visibility', 'category' => 'permissions', 'defaultValue' => 'private', 'displayOrder' => 40},
688
731
  {'fieldName' => 'isAutoJoinDomain', 'fieldLabel' => 'Auto Join Domain?', 'type' => 'checkbox', 'required' => false, 'description' => 'Auto Join Domain?', 'category' => 'advanced', 'displayOrder' => 40},
@@ -696,22 +739,25 @@ class Morpheus::Cli::VirtualImages
696
739
  image_type_code = image_type ? image_type['code'] : nil
697
740
  if image_type_code
698
741
  if image_type_code == 'ami'
699
- tmp_option_types << {'fieldName' => 'externalId', 'fieldLabel' => 'AMI id', 'type' => 'text', 'required' => false, 'displayOrder' => 10}
742
+ tmp_option_types << {'fieldName' => 'externalId', 'fieldLabel' => 'AMI id', 'type' => 'text', 'required' => false, 'displayOrder' => 11}
700
743
  if include_file_selection
701
- tmp_option_types << {'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => false, 'displayOrder' => 10}
744
+ tmp_option_types << {'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => false, 'displayOrder' => 12}
702
745
  end
703
746
  elsif image_type_code == 'vmware' || image_type_code == 'vmdk'
704
747
  if include_file_selection
705
- tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'OVF File', 'type' => 'file', 'required' => false, 'displayOrder' => 10}
706
- tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageDescriptorFile', 'fieldLabel' => 'VMDK File', 'type' => 'file', 'required' => false, 'displayOrder' => 10}
748
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'OVF File', 'type' => 'file', 'required' => false, 'displayOrder' => 11}
749
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageDescriptorFile', 'fieldLabel' => 'VMDK File', 'type' => 'file', 'required' => false, 'displayOrder' => 12}
707
750
  end
708
751
  elsif image_type_code == 'pxe'
709
- tmp_option_types << {'fieldName' => 'config.menu', 'fieldLabel' => 'Menu', 'type' => 'text', 'required' => false, 'displayOrder' => 10}
710
- tmp_option_types << {'fieldName' => 'imagePath', 'fieldLabel' => 'Image Path', 'type' => 'text', 'required' => true, 'displayOrder' => 10}
752
+ tmp_option_types << {'fieldName' => 'config.menu', 'fieldLabel' => 'Menu', 'type' => 'text', 'required' => false, 'displayOrder' => 11}
753
+ tmp_option_types << {'fieldName' => 'imagePath', 'fieldLabel' => 'Image Path', 'type' => 'text', 'required' => true, 'displayOrder' => 12}
711
754
  tmp_option_types.reject! {|opt| ['isCloudInit', 'installAgent', 'sshUsername', 'sshPassword'].include?(opt['fieldName'])}
755
+ elsif image_type_code == 'azure' || image_type_code == 'azure-reference'
756
+ # Azure Marketplace Prompt happens elsewhere
757
+ tmp_option_types.reject! {|opt| ['storageProviderId', 'userData', 'sshUsername', 'sshPassword'].include?(opt['fieldName'])}
712
758
  else
713
759
  if include_file_selection
714
- tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => false, 'description' => 'Choose an image file to upload', 'displayOrder' => 10}
760
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => false, 'description' => 'Choose an image file to upload', 'displayOrder' => 11}
715
761
  end
716
762
  end
717
763
  end
@@ -735,5 +781,40 @@ class Morpheus::Cli::VirtualImages
735
781
  ""
736
782
  end
737
783
  end
784
+
785
+ def prompt_azure_marketplace(cloud_id, options)
786
+ rtn = {}
787
+ publisher_value, offer_value, sku_value, version_value = nil, nil, nil, nil
788
+
789
+ # Marketplace Publisher & Offer
790
+ marketplace_api_params = {'zoneId' => cloud_id}
791
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'offer', 'fieldLabel' => 'Azure Marketplace Offer', 'type' => 'typeahead', 'optionSource' => 'searchAzureMarketplace', 'required' => true, 'description' => "Select Azure Marketplace Offer."}], options[:options],@api_client, marketplace_api_params)
792
+ # offer_value = v_prompt['marketplace']
793
+ # actually need both offer and publisher of these to query correctly..sigh
794
+ marketplace_option = Morpheus::Cli::OptionTypes.get_last_select()
795
+ offer_value = marketplace_option['offer']
796
+ publisher_value = marketplace_option['publisher']
797
+
798
+ # SKU & VERSION
799
+ if options[:options] && options[:options]['sku'] && options[:options]['version']
800
+ # the value to match on is actually sku|version
801
+ options[:options]['sku'] = options[:options]['sku'] + '|' + options[:options]['version']
802
+ end
803
+ sku_api_params = {'zoneId' => cloud_id, publisher: publisher_value, offer: offer_value}
804
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'sku', 'fieldLabel' => 'Azure Marketplace SKU', 'type' => 'select', 'optionSource' => 'searchAzureMarketplaceSkus', 'required' => true, 'description' => "Select Azure Marketplace SKU and Version, the format is SKU|Version"}], options[:options],@api_client, sku_api_params)
805
+ # marketplace_option = Morpheus::Cli::OptionTypes.get_last_select()
806
+ # sku_value = marketplace_option['sku']
807
+ # version_value = marketplace_option['version']
808
+ sku_value = v_prompt['sku']
809
+ if sku_value && sku_value.include?("|")
810
+ sku_value, version_value = sku_value.split("|")
811
+ end
812
+
813
+ rtn['publisher'] = publisher_value
814
+ rtn['offer'] = offer_value
815
+ rtn['sku'] = sku_value
816
+ rtn['version'] = version_value
817
+ return rtn
818
+ end
738
819
 
739
820
  end