morpheus-cli 5.3.0.3 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +1 -3
  4. data/lib/morpheus/api/api_client.rb +48 -14
  5. data/lib/morpheus/api/certificate_types_interface.rb +14 -0
  6. data/lib/morpheus/api/certificates_interface.rb +9 -0
  7. data/lib/morpheus/api/integration_types_interface.rb +14 -0
  8. data/lib/morpheus/api/integrations_interface.rb +7 -22
  9. data/lib/morpheus/api/network_services_interface.rb +14 -0
  10. data/lib/morpheus/api/read_interface.rb +23 -0
  11. data/lib/morpheus/api/rest_interface.rb +12 -10
  12. data/lib/morpheus/api/roles_interface.rb +7 -0
  13. data/lib/morpheus/api/servers_interface.rb +7 -0
  14. data/lib/morpheus/api/user_settings_interface.rb +38 -18
  15. data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
  16. data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
  17. data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
  18. data/lib/morpheus/api/vdi_interface.rb +28 -0
  19. data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
  20. data/lib/morpheus/cli.rb +9 -2
  21. data/lib/morpheus/cli/apps.rb +59 -75
  22. data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
  23. data/lib/morpheus/cli/certificates_command.rb +575 -0
  24. data/lib/morpheus/cli/cli_command.rb +61 -6
  25. data/lib/morpheus/cli/clouds.rb +1 -0
  26. data/lib/morpheus/cli/clusters.rb +1 -1
  27. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  28. data/lib/morpheus/cli/hosts.rb +245 -224
  29. data/lib/morpheus/cli/instances.rb +150 -167
  30. data/lib/morpheus/cli/integrations_command.rb +588 -41
  31. data/lib/morpheus/cli/login.rb +7 -0
  32. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  33. data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
  34. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  35. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  36. data/lib/morpheus/cli/networks_command.rb +2 -2
  37. data/lib/morpheus/cli/option_types.rb +34 -33
  38. data/lib/morpheus/cli/remote.rb +1 -1
  39. data/lib/morpheus/cli/reports_command.rb +4 -1
  40. data/lib/morpheus/cli/roles.rb +215 -55
  41. data/lib/morpheus/cli/subnets_command.rb +11 -2
  42. data/lib/morpheus/cli/user_settings_command.rb +268 -57
  43. data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
  44. data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
  45. data/lib/morpheus/cli/vdi_command.rb +359 -0
  46. data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
  47. data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
  48. data/lib/morpheus/cli/version.rb +1 -1
  49. data/lib/morpheus/rest_client.rb +30 -0
  50. data/lib/morpheus/terminal.rb +15 -7
  51. metadata +18 -2
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::VdiAppsInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/vdi-apps"
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::VdiGatewaysInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/vdi-gateways"
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ # Interface for VDI Persona that provides viewing and allocating virtual desktops (VDI pools)
4
+ class Morpheus::VdiInterface < Morpheus::APIClient
5
+
6
+ def base_path
7
+ "/api/vdi"
8
+ end
9
+
10
+ # def dashboard(params={})
11
+ # execute(method: :get, url: "#{base_path}/dashboard", params: params)
12
+ # end
13
+
14
+ def list(params={})
15
+ execute(method: :get, url: "#{base_path}", params: params)
16
+ end
17
+
18
+ def get(id, params={})
19
+ validate_id!(id)
20
+ execute(method: :get, url: "#{base_path}/#{id}", params: params)
21
+ end
22
+
23
+ def allocate(id, payload, params={})
24
+ validate_id!(id)
25
+ execute(method: :post, url: "#{base_path}/#{id}/allocate", params: params, payload: payload.to_json)
26
+ end
27
+
28
+ end
@@ -0,0 +1,19 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::VdiPoolsInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/vdi-pools"
7
+ end
8
+
9
+ # NOT json, multipart file upload
10
+ def update_logo(id, logo_file)
11
+ url = "#{base_path}/#{id}/update-logo"
12
+ headers = { :params => {}, :authorization => "Bearer #{@access_token}"}
13
+ payload = {}
14
+ payload[:logo] = logo_file
15
+ payload[:multipart] = true
16
+ execute(method: :post, url: url, headers: headers, payload: payload)
17
+ end
18
+
19
+ end
data/lib/morpheus/cli.rb CHANGED
@@ -46,6 +46,7 @@ module Morpheus
46
46
  # load interfaces
47
47
  require 'morpheus/api/api_client.rb'
48
48
  require 'morpheus/api/rest_interface.rb'
49
+ require 'morpheus/api/read_interface.rb'
49
50
  Dir[File.dirname(__FILE__) + "/api/**/*.rb"].each {|file| load file }
50
51
 
51
52
  # load mixins
@@ -175,9 +176,15 @@ module Morpheus
175
176
  load 'morpheus/cli/projects_command.rb'
176
177
  load 'morpheus/cli/backups_command.rb'
177
178
  load 'morpheus/cli/backup_jobs_command.rb'
178
- load 'morpheus/cli/catalog_item_types_command.rb' # catalog-types
179
- load 'morpheus/cli/service_catalog_command.rb' # catalog
179
+ load 'morpheus/cli/catalog_item_types_command.rb' # self-service
180
+ load 'morpheus/cli/service_catalog_command.rb' # catalog (Service Catalog persona)
180
181
  load 'morpheus/cli/usage_command.rb'
182
+ load 'morpheus/cli/vdi_pools_command.rb'
183
+ load 'morpheus/cli/vdi_allocations_command.rb'
184
+ load 'morpheus/cli/vdi_apps_command.rb'
185
+ load 'morpheus/cli/vdi_gateways_command.rb'
186
+ load 'morpheus/cli/vdi_command.rb' # (VDI persona)
187
+ load 'morpheus/cli/certificates_command.rb' # (VDI persona)
181
188
  # add new commands here...
182
189
 
183
190
  end
@@ -88,7 +88,7 @@ class Morpheus::Cli::Apps
88
88
  opts.on('-a', '--details', "Display all details: memory and storage usage used / max values." ) do
89
89
  options[:details] = true
90
90
  end
91
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
91
+ build_standard_list_options(opts, options)
92
92
  opts.footer = "List apps."
93
93
  end
94
94
  optparse.parse!(args)
@@ -97,64 +97,53 @@ class Morpheus::Cli::Apps
97
97
  options[:phrase] = args.join(" ")
98
98
  end
99
99
  connect(options)
100
- begin
101
- if options[:type]
102
- params['type'] = [options[:type]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
103
- end
104
- if options[:blueprint]
105
- blueprint_ids = [options[:blueprint]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
106
- params['blueprintId'] = blueprint_ids.collect do |blueprint_id|
107
- if blueprint_id.to_s =~ /\A\d{1,}\Z/
108
- return blueprint_id
109
- else
110
- blueprint = find_blueprint_by_name_or_id(blueprint_id)
111
- return 1 if blueprint.nil?
112
- blueprint['id']
113
- end
100
+
101
+ if options[:type]
102
+ params['type'] = [options[:type]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
103
+ end
104
+ if options[:blueprint]
105
+ blueprint_ids = [options[:blueprint]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
106
+ params['blueprintId'] = blueprint_ids.collect do |blueprint_id|
107
+ if blueprint_id.to_s =~ /\A\d{1,}\Z/
108
+ return blueprint_id
109
+ else
110
+ blueprint = find_blueprint_by_name_or_id(blueprint_id)
111
+ return 1 if blueprint.nil?
112
+ blueprint['id']
114
113
  end
115
114
  end
116
- if options[:owner]
117
- owner_ids = [options[:owner]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
118
- params['ownerId'] = owner_ids.collect do |owner_id|
119
- if owner_id.to_s =~ /\A\d{1,}\Z/
120
- return owner_id
121
- else
122
- user = find_available_user_option(owner_id)
123
- return 1 if user.nil?
124
- user['id']
125
- end
115
+ end
116
+ if options[:owner]
117
+ owner_ids = [options[:owner]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
118
+ params['ownerId'] = owner_ids.collect do |owner_id|
119
+ if owner_id.to_s =~ /\A\d{1,}\Z/
120
+ return owner_id
121
+ else
122
+ user = find_available_user_option(owner_id)
123
+ return 1 if user.nil?
124
+ user['id']
126
125
  end
127
126
  end
128
- params.merge!(parse_list_options(options))
129
- account = nil
130
- if options[:owner]
131
- created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:owner])
132
- return if created_by_ids.nil?
133
- params['createdBy'] = created_by_ids
134
- # params['ownerId'] = created_by_ids # 4.2.1+
135
- end
136
-
137
- params['showDeleted'] = options[:showDeleted] if options.key?(:showDeleted)
138
- params['deleted'] = options[:deleted] if options.key?(:deleted)
127
+ end
128
+ params.merge!(parse_list_options(options))
129
+ account = nil
130
+ if options[:owner]
131
+ created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:owner])
132
+ return if created_by_ids.nil?
133
+ params['createdBy'] = created_by_ids
134
+ # params['ownerId'] = created_by_ids # 4.2.1+
135
+ end
139
136
 
140
- @apps_interface.setopts(options)
141
- if options[:dry_run]
142
- print_dry_run @apps_interface.dry.list(params)
143
- return
144
- end
137
+ params['showDeleted'] = options[:showDeleted] if options.key?(:showDeleted)
138
+ params['deleted'] = options[:deleted] if options.key?(:deleted)
145
139
 
146
- json_response = @apps_interface.list(params)
147
- if options[:json]
148
- puts as_json(json_response, options, "apps")
149
- return 0
150
- elsif options[:yaml]
151
- puts as_yaml(json_response, options, "apps")
152
- return 0
153
- elsif options[:csv]
154
- puts records_as_csv(json_response['apps'], options)
155
- return 0
156
- end
157
-
140
+ @apps_interface.setopts(options)
141
+ if options[:dry_run]
142
+ print_dry_run @apps_interface.dry.list(params)
143
+ return
144
+ end
145
+ json_response = @apps_interface.list(params)
146
+ render_response(json_response, options, "apps") do
158
147
  apps = json_response['apps']
159
148
  title = "Morpheus Apps"
160
149
  subtitles = []
@@ -167,10 +156,8 @@ class Morpheus::Cli::Apps
167
156
  print_results_pagination(json_response)
168
157
  end
169
158
  print reset,"\n"
170
- rescue RestClient::Exception => e
171
- print_rest_exception(e, options)
172
- exit 1
173
159
  end
160
+ return 0, nil
174
161
  end
175
162
 
176
163
  def count(args)
@@ -645,7 +632,7 @@ class Morpheus::Cli::Apps
645
632
  opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
646
633
  options[:refresh_until_status] = val.to_s.downcase
647
634
  end
648
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :outfile, :dry_run, :remote])
635
+ build_standard_get_options(opts, options)
649
636
  opts.footer = "Get details about an app.\n" +
650
637
  "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
651
638
  end
@@ -665,19 +652,19 @@ class Morpheus::Cli::Apps
665
652
 
666
653
  end
667
654
 
668
- def _get(arg, options={})
669
- begin
670
- app = find_app_by_name_or_id(arg)
671
- @apps_interface.setopts(options)
672
- if options[:dry_run]
673
- print_dry_run @apps_interface.dry.get(app['id'])
674
- return
675
- end
676
- json_response = @apps_interface.get(app['id'])
677
-
678
- render_result = render_with_format(json_response, options, 'blueprint')
679
- return 0 if render_result
680
-
655
+ def _get(id, options={})
656
+ app = nil
657
+ if id.to_s !~ /\A\d{1,}\Z/
658
+ app = find_app_by_name_or_id(id)
659
+ id = app['id']
660
+ end
661
+ @apps_interface.setopts(options)
662
+ if options[:dry_run]
663
+ print_dry_run @apps_interface.dry.get(id)
664
+ return
665
+ end
666
+ json_response = @apps_interface.get(id.to_i)
667
+ render_response(json_response, options, 'app') do
681
668
  app = json_response['app']
682
669
  app_tiers = app['appTiers']
683
670
  print_h1 "App Details", [], options
@@ -799,14 +786,11 @@ class Morpheus::Cli::Apps
799
786
  print cyan, "Refreshing in #{options[:refresh_interval] > 1 ? options[:refresh_interval].to_i : options[:refresh_interval]} seconds"
800
787
  sleep_with_dots(options[:refresh_interval])
801
788
  print "\n"
802
- _get(arg, options)
789
+ _get(app['id'], options)
803
790
  end
804
791
  end
805
-
806
- rescue RestClient::Exception => e
807
- print_rest_exception(e, options)
808
- exit 1
809
792
  end
793
+ return 0, nil
810
794
  end
811
795
 
812
796
  def update(args)
@@ -532,22 +532,22 @@ EOT
532
532
 
533
533
  def add_catalog_item_type_option_types
534
534
  [
535
- {'code' => 'catalogItemType.type', 'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Blueprint', 'value' => 'blueprint'}, {'name' => 'Workflow', 'value' => 'workflow'}], 'defaultValue' => 'instance', 'required' => true},
536
- {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
537
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
538
- {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true},
539
- {'fieldName' => 'featured', 'fieldLabel' => 'Featured', 'type' => 'checkbox', 'defaultValue' => false},
540
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true},
541
- {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList'},
542
- #{'fieldName' => 'optionTypes', 'fieldLabel' => 'Option Types', 'type' => 'text', 'description' => 'Option Types to include, comma separated list of names or IDs.'},
543
- {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true},
544
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true},
545
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true},
546
- {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'noParams' => true},
535
+ {'code' => 'catalogItemType.type', 'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Blueprint', 'value' => 'blueprint'}, {'name' => 'Workflow', 'value' => 'workflow'}], 'defaultValue' => 'instance', 'required' => true, 'displayOrder' => 1},
536
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 2},
537
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 3},
538
+ {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'displayOrder' => 4},
539
+ {'fieldName' => 'featured', 'fieldLabel' => 'Featured', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 5},
540
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true, 'displayOrder' => 6},
541
+ {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'displayOrder' => 7},
542
+ #{'fieldName' => 'optionTypes', 'fieldLabel' => 'Option Types', 'type' => 'text', 'description' => 'Option Types to include, comma separated list of names or IDs.', 'displayOrder' => 8},
543
+ {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true, 'displayOrder' => 9},
544
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true, 'displayOrder' => 10},
545
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'displayOrder' => 11},
546
+ {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'noParams' => true, 'displayOrder' => 12},
547
547
  {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'context', 'fieldLabel' => 'Context Type', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
548
548
  [{'name' => "Select", 'value' => ""}, {'name' => "None", 'value' => "appliance"}, {'name' => "Instance", 'value' => "instance"}, {'name' => "Server", 'value' => "server"}]
549
549
  }, 'description' => 'Context for operational workflow, determines target type', 'defaultValue' => 'Select', 'required' => false},
550
- {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item'}
550
+ {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item', 'displayOrder' => 13}
551
551
  ]
552
552
  end
553
553
 
@@ -0,0 +1,575 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::CertificatesCommand
4
+ include Morpheus::Cli::CliCommand
5
+
6
+ set_command_name :'certificates'
7
+ set_command_description "Certificates: View and manage SSL certificates."
8
+
9
+ register_subcommands :list, :get, :add, :update, :remove
10
+ register_subcommands :list_types, :get_type
11
+
12
+ def connect(opts)
13
+ @api_client = establish_remote_appliance_connection(opts)
14
+ @certificates_interface = @api_client.certificates
15
+ @certificate_types_interface = @api_client.certificate_types
16
+ end
17
+
18
+ def handle(args)
19
+ handle_subcommand(args)
20
+ end
21
+
22
+ def list(args)
23
+ options = {}
24
+ params = {}
25
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
26
+ opts.banner = subcommand_usage("[search]")
27
+ build_standard_list_options(opts, options)
28
+ opts.footer = "List certificates."
29
+ end
30
+ optparse.parse!(args)
31
+ if args.count > 0
32
+ options[:phrase] = args.join(" ")
33
+ end
34
+ connect(options)
35
+ params.merge!(parse_list_options(options))
36
+ @certificates_interface.setopts(options)
37
+ if options[:dry_run]
38
+ print_dry_run @certificates_interface.dry.list(params)
39
+ return
40
+ end
41
+ json_response = @certificates_interface.list(params)
42
+ render_response(json_response, options, certificate_list_key) do
43
+ certificates = json_response[certificate_list_key]
44
+ print_h1 "Morpheus Certificates", parse_list_subtitles(options), options
45
+ if certificates.empty?
46
+ print cyan,"No certificates found.",reset,"\n"
47
+ else
48
+ list_columns = {
49
+ "ID" => 'id',
50
+ "Name" => 'name',
51
+ "Issued To" => lambda {|it| it['commonName'] },
52
+ "Cert Type" => lambda {|it| it['certType'] },
53
+ "Domain Name" => lambda {|it| it['domainName'] },
54
+ }.upcase_keys!
55
+ print as_pretty_table(certificates, list_columns, options)
56
+ print_results_pagination(json_response)
57
+ end
58
+ print reset,"\n"
59
+ end
60
+ return 0, nil
61
+ end
62
+
63
+ def get(args)
64
+ params = {}
65
+ options = {}
66
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
67
+ opts.banner = subcommand_usage("[certificate]")
68
+ build_standard_get_options(opts, options)
69
+ opts.footer = <<-EOT
70
+ Get details about a specific certificate.
71
+ [certificate] is required. This is the name or id of a certificate.
72
+ EOT
73
+ end
74
+ optparse.parse!(args)
75
+ verify_args!(args:args, optparse:optparse, min:1)
76
+ connect(options)
77
+ params.merge!(parse_query_options(options))
78
+ id_list = parse_id_list(args)
79
+ return run_command_for_each_arg(id_list) do |arg|
80
+ _get(arg, params, options)
81
+ end
82
+ end
83
+
84
+ def _get(id, params, options)
85
+ certificate = nil
86
+ if id.to_s !~ /\A\d{1,}\Z/
87
+ certificate = find_certificate_by_name_or_id(id)
88
+ return 1, "certificate not found for #{id}" if certificate.nil?
89
+ id = certificate['id']
90
+ end
91
+ @certificates_interface.setopts(options)
92
+ if options[:dry_run]
93
+ print_dry_run @certificates_interface.dry.get(id, params)
94
+ return
95
+ end
96
+ json_response = @certificates_interface.get(id, params)
97
+ certificate = json_response[certificate_object_key]
98
+ render_response(json_response, options, certificate_object_key) do
99
+ print_h1 "Certificate Details", [], options
100
+ print cyan
101
+ show_columns = {
102
+ "ID" => 'id',
103
+ "Name" => 'name',
104
+ "Description" => 'description',
105
+ "Issued To" => lambda {|it| it['commonName'] },
106
+ "Cert Type" => lambda {|it| it['certType'] },
107
+ "Domain Name" => lambda {|it| it['domainName'] },
108
+ "Wildcard" => lambda {|it| format_boolean(it['wildcard']) },
109
+ }
110
+ print_description_list(show_columns, certificate, options)
111
+ print reset,"\n"
112
+ end
113
+ return 0, nil
114
+ end
115
+
116
+ def add(args)
117
+ options = {}
118
+ params = {}
119
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
120
+ opts.banner = subcommand_usage("[name] -t CODE [options]")
121
+ # opts.on('-t', '--type CODE', "Certificate Type code, see `#{command_name} list-types` for available type codes") do |val|
122
+ # options[:options]['type'] = val
123
+ # end
124
+ build_option_type_options(opts, options, add_certificate_option_types)
125
+ build_option_type_options(opts, options, add_certificate_advanced_option_types)
126
+ build_standard_add_options(opts, options)
127
+ opts.footer = <<-EOT
128
+ Create a new certificate.
129
+ [name] is required. This is the name of the new certificate
130
+ Configuration options vary by certificate type.
131
+ EOT
132
+ end
133
+ optparse.parse!(args)
134
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
135
+ options[:options]['name'] = args[0] if args[0]
136
+ connect(options)
137
+ payload = {}
138
+ if options[:payload]
139
+ payload = options[:payload]
140
+ payload.deep_merge!({certificate_object_key => parse_passed_options(options)})
141
+ else
142
+ payload.deep_merge!({certificate_object_key => parse_passed_options(options)})
143
+ # Type prompt first
144
+ #params['type'] = Morpheus::Cli::OptionTypes.no_prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Blueprint', 'value' => 'blueprint'}, {'name' => 'Workflow', 'value' => 'workflow'}], 'defaultValue' => 'instance', 'required' => true}], options[:options], @api_client, options[:params])['type']
145
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_certificate_option_types(), options[:options], @api_client, options[:params])
146
+ params.deep_merge!(v_prompt)
147
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(add_certificate_advanced_option_types, options[:options], @api_client, options[:params])
148
+ advanced_config.deep_compact!
149
+ params.deep_merge!(advanced_config)
150
+
151
+ # lookup type by name or code to validate it exists and to prompt for its optionTypes
152
+ # set certificate.type=code because the api expects it that way.
153
+ if params['type'].to_s.empty?
154
+ raise_command_error "missing required option: --type TYPE", args, optparse
155
+ end
156
+ certificate_type = find_certificate_type_by_name_or_code_id(params['type'])
157
+ if certificate_type.nil?
158
+ print_red_alert "certificate type not found for #{params['type']}"
159
+ return 1, "certificate type not found for #{params['type']}"
160
+ end
161
+ params['type'] = certificate_type['code']
162
+ config_option_types = certificate_type['optionTypes']
163
+ if config_option_types.nil?
164
+ config_option_types = @certificate_types_interface.option_types(certificate_type['id'])['optionTypes']
165
+ end
166
+ if config_option_types.nil?
167
+ print yellow,"No option types found for certificate type: #{certificate_type['name']} (#{certificate_type['code']})", reset, "\n"
168
+ end
169
+ if config_option_types && config_option_types.size > 0
170
+ # optionTypes do not need fieldContext: 'certificate'
171
+ config_option_types.each do |opt|
172
+ if opt['fieldContext'] == 'certificate' || opt['fieldContext'] == 'domain'
173
+ opt['fieldContext'] = nil
174
+ end
175
+ end
176
+ # reject hardcoded optionTypes
177
+ config_option_types = config_option_types.reject {|it| it['fieldName'] == 'name' || it['fieldName'] == 'description' || it['fieldName'] == 'domainName' }
178
+ config_prompt = Morpheus::Cli::OptionTypes.prompt(config_option_types, options[:options], @api_client, options[:params])
179
+ config_prompt.deep_compact!
180
+ params.deep_merge!(config_prompt)
181
+ end
182
+ # convert checkbox "on" and "off" to true and false
183
+ params.booleanize!
184
+ payload[certificate_object_key].deep_merge!(params)
185
+ end
186
+ @certificates_interface.setopts(options)
187
+ if options[:dry_run]
188
+ print_dry_run @certificates_interface.dry.create(payload)
189
+ return 0, nil
190
+ end
191
+ json_response = @certificates_interface.create(payload)
192
+ certificate = json_response[certificate_object_key]
193
+ render_response(json_response, options, certificate_object_key) do
194
+ print_green_success "Added certificate #{certificate['name']}"
195
+ return _get(certificate["id"], {}, options)
196
+ end
197
+ return 0, nil
198
+ end
199
+
200
+ def update(args)
201
+ options = {}
202
+ params = {}
203
+ payload = {}
204
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
205
+ opts.banner = subcommand_usage("[certificate] [options]")
206
+ build_option_type_options(opts, options, update_certificate_option_types)
207
+ build_option_type_options(opts, options, update_certificate_advanced_option_types)
208
+ opts.on(nil, '--no-refresh', "Skip refresh on update.") do
209
+ payload['refresh'] = 'false'
210
+ end
211
+ build_standard_update_options(opts, options)
212
+ opts.footer = <<-EOT
213
+ Update a certificate.
214
+ [certificate] is required. This is the name or id of a certificate.
215
+ EOT
216
+ end
217
+ optparse.parse!(args)
218
+ verify_args!(args:args, optparse:optparse, count:1)
219
+ connect(options)
220
+ certificate = find_certificate_by_name_or_id(args[0])
221
+ return 1 if certificate.nil?
222
+ payload = {}
223
+ if options[:payload]
224
+ payload = options[:payload]
225
+ payload.deep_merge!({certificate_object_key => parse_passed_options(options)})
226
+ else
227
+ payload.deep_merge!({certificate_object_key => parse_passed_options(options)})
228
+ # do not prompt on update
229
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_certificate_option_types, options[:options], @api_client, options[:params])
230
+ v_prompt.deep_compact!
231
+ params.deep_merge!(v_prompt)
232
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(update_certificate_advanced_option_types, options[:options], @api_client, options[:params])
233
+ advanced_config.deep_compact!
234
+ params.deep_merge!(advanced_config)
235
+ # convert checkbox "on" and "off" to true and false
236
+ params.booleanize!
237
+ payload.deep_merge!({certificate_object_key => params})
238
+ if payload[certificate_object_key].empty? # || options[:no_prompt]
239
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
240
+ end
241
+ end
242
+ @certificates_interface.setopts(options)
243
+ if options[:dry_run]
244
+ print_dry_run @certificates_interface.dry.update(certificate['id'], payload)
245
+ return
246
+ end
247
+ json_response = @certificates_interface.update(certificate['id'], payload)
248
+ certificate = json_response[certificate_object_key]
249
+ render_response(json_response, options, certificate_object_key) do
250
+ print_green_success "Updated certificate #{certificate['name']}"
251
+ return _get(certificate["id"], {}, options)
252
+ end
253
+ return 0, nil
254
+ end
255
+
256
+ def remove(args)
257
+ options = {}
258
+ params = {}
259
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
260
+ opts.banner = subcommand_usage("[certificate] [options]")
261
+ build_standard_remove_options(opts, options)
262
+ opts.footer = <<-EOT
263
+ Delete a certificate.
264
+ [certificate] is required. This is the name or id of a certificate.
265
+ EOT
266
+ end
267
+ optparse.parse!(args)
268
+ verify_args!(args:args, optparse:optparse, count:1)
269
+ connect(options)
270
+ certificate = find_certificate_by_name_or_id(args[0])
271
+ return 1 if certificate.nil?
272
+ @certificates_interface.setopts(options)
273
+ if options[:dry_run]
274
+ print_dry_run @certificates_interface.dry.destroy(certificate['id'], params)
275
+ return
276
+ end
277
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the certificate #{certificate['name']}?")
278
+ return 9, "aborted command"
279
+ end
280
+ json_response = @certificates_interface.destroy(certificate['id'], params)
281
+ render_response(json_response, options) do
282
+ print_green_success "Removed certificate #{certificate['name']}"
283
+ end
284
+ return 0, nil
285
+ end
286
+
287
+
288
+ def list_types(args)
289
+ options = {}
290
+ params = {}
291
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
292
+ opts.banner = subcommand_usage("[search]")
293
+ opts.on('--optionTypes [true|false]', String, "Include optionTypes in the response. Default is false.") do |val|
294
+ params['optionTypes'] = (val.to_s == '' || val.to_s == 'on' || val.to_s == 'true')
295
+ end
296
+ build_standard_list_options(opts, options)
297
+ opts.footer = "List certificate types."
298
+ end
299
+ optparse.parse!(args)
300
+ connect(options)
301
+ # verify_args!(args:args, optparse:optparse, count:0)
302
+ if args.count > 0
303
+ options[:phrase] = args.join(" ")
304
+ end
305
+ params.merge!(parse_list_options(options))
306
+ @certificate_types_interface.setopts(options)
307
+ if options[:dry_run]
308
+ print_dry_run @certificate_types_interface.dry.list(params)
309
+ return
310
+ end
311
+ json_response = @certificate_types_interface.list(params)
312
+ render_response(json_response, options, certificate_type_list_key) do
313
+ certificate_types = json_response[certificate_type_list_key]
314
+ print_h1 "Morpheus Certificate Types", parse_list_subtitles(options), options
315
+ if certificate_types.empty?
316
+ print cyan,"No certificate types found.",reset,"\n"
317
+ else
318
+ list_columns = certificate_type_column_definitions.upcase_keys!
319
+ print as_pretty_table(certificate_types, list_columns, options)
320
+ print_results_pagination(json_response)
321
+ end
322
+ print reset,"\n"
323
+ end
324
+ return 0, nil
325
+ end
326
+
327
+ def get_type(args)
328
+ params = {'optionTypes' => true}
329
+ options = {}
330
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
331
+ opts.banner = subcommand_usage("[type]")
332
+ opts.on('--optionTypes [true|false]', String, "Include optionTypes in the response. Default is true.") do |val|
333
+ params['optionTypes'] = (val.to_s == '' || val.to_s == 'on' || val.to_s == 'true')
334
+ end
335
+ build_standard_get_options(opts, options)
336
+ opts.footer = <<-EOT
337
+ Get details about a specific certificate type.
338
+ [type] is required. This is the name or id of a certificate type.
339
+ EOT
340
+ end
341
+ optparse.parse!(args)
342
+ verify_args!(args:args, optparse:optparse, min:1)
343
+ connect(options)
344
+ params.merge!(parse_query_options(options))
345
+ id_list = parse_id_list(args)
346
+ return run_command_for_each_arg(id_list) do |arg|
347
+ _get_type(arg, params, options)
348
+ end
349
+ end
350
+
351
+ def _get_type(id, params, options)
352
+ certificate_type = nil
353
+ if id.to_s !~ /\A\d{1,}\Z/
354
+ certificate_type = find_certificate_type_by_name_or_code(id)
355
+ return 1, "certificate type not found for name or code '#{id}'" if certificate_type.nil?
356
+ id = certificate_type['id']
357
+ end
358
+ # /api/certificate-types does not return optionTypes by default, use ?optionTypes=true
359
+ @certificate_types_interface.setopts(options)
360
+ if options[:dry_run]
361
+ print_dry_run @certificate_types_interface.dry.get(id, params)
362
+ return
363
+ end
364
+ json_response = @certificate_types_interface.get(id, params)
365
+ certificate_type = json_response[certificate_type_object_key]
366
+ render_response(json_response, options, certificate_type_object_key) do
367
+ print_h1 "Certificate Type Details", [], options
368
+ print cyan
369
+ show_columns = certificate_type_column_definitions
370
+ print_description_list(show_columns, certificate_type)
371
+
372
+ if certificate_type['optionTypes'] && certificate_type['optionTypes'].size > 0
373
+ print_h2 "Option Types"
374
+ opt_columns = [
375
+ # {"ID" => lambda {|it| it['id'] } },
376
+ {"FIELD NAME" => lambda {|it| (it['fieldContext'] && it['fieldContext'] != 'certificate') ? [it['fieldContext'], it['fieldName']].join('.') : it['fieldName'] } },
377
+ {"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
378
+ {"TYPE" => lambda {|it| it['type'] } },
379
+ {"DEFAULT" => lambda {|it| it['defaultValue'] } },
380
+ {"REQUIRED" => lambda {|it| format_boolean it['required'] } },
381
+ # {"DESCRIPTION" => lambda {|it| it['description'] }, # do it!
382
+ ]
383
+ print as_pretty_table(certificate_type['optionTypes'], opt_columns)
384
+ else
385
+ # print cyan,"No option types found for this certificate type.","\n",reset
386
+ end
387
+
388
+ print reset,"\n"
389
+ end
390
+ return 0, nil
391
+ end
392
+
393
+ private
394
+
395
+ def format_certificate_type(certificate)
396
+ (certificate['certificateType']['name'] || certificate['certificateType']['code']) rescue certificate['certificateType'].to_s
397
+ end
398
+
399
+ def add_certificate_option_types
400
+ [
401
+ {'code' => 'certificate.type', 'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
402
+ # @certificate_types_interface.list(max:-1)[certificate_list_key].collect {|it|
403
+ get_available_certificate_types().collect {|it|
404
+ {'name' => it['code'], 'value' => it['id']}
405
+ } }, 'required' => true, 'description' => "Certificate Type code, see `#{command_name} list-types` for available type codes", 'defaultValue' => "internal"},
406
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Name of the certificate'},
407
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false, 'description' => 'Description of the certificate'},
408
+ {'fieldName' => 'domainName', 'fieldLabel' => 'Domain Name', 'type' => 'text', 'required' => false, 'description' => 'Domain Name of the certificate'},
409
+ # {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'description' => 'Can be used to disable a certificate'}
410
+ ]
411
+ end
412
+
413
+ def add_certificate_advanced_option_types
414
+ []
415
+ end
416
+
417
+ def update_certificate_option_types
418
+ list = add_certificate_option_types.collect {|it|
419
+ it.delete('required')
420
+ it.delete('defaultValue')
421
+ it
422
+ }
423
+ list = list.reject {|it| ["type"].include? it['fieldName'] }
424
+ list
425
+ end
426
+
427
+ def update_certificate_advanced_option_types
428
+ add_certificate_advanced_option_types.collect {|it|
429
+ it.delete('required')
430
+ it.delete('defaultValue')
431
+ it
432
+ }
433
+ end
434
+
435
+ def certificate_object_key
436
+ 'certificate'
437
+ end
438
+
439
+ def certificate_list_key
440
+ 'certificates'
441
+ end
442
+
443
+ def find_certificate_by_name_or_id(val)
444
+ if val.to_s =~ /\A\d{1,}\Z/
445
+ return find_certificate_by_id(val)
446
+ else
447
+ return find_certificate_by_name(val)
448
+ end
449
+ end
450
+
451
+ def find_certificate_by_id(id)
452
+ begin
453
+ json_response = @certificates_interface.get(id.to_i)
454
+ return json_response[certificate_object_key]
455
+ rescue RestClient::Exception => e
456
+ if e.response && e.response.code == 404
457
+ print_red_alert "certificate not found by id '#{id}'"
458
+ else
459
+ raise e
460
+ end
461
+ end
462
+ end
463
+
464
+ def find_certificate_by_name(name)
465
+ json_response = @certificates_interface.list({name: name.to_s})
466
+ certificates = json_response[certificate_list_key]
467
+ if certificates.empty?
468
+ print_red_alert "certificate not found by name '#{name}'"
469
+ return nil
470
+ elsif certificates.size > 1
471
+ print_red_alert "#{certificates.size} certificates found by name '#{name}'"
472
+ puts_error as_pretty_table(certificates, [:id, :name], {color:red})
473
+ print_red_alert "Try using ID instead"
474
+ print reset,"\n"
475
+ return nil
476
+ else
477
+ return certificates[0]
478
+ end
479
+ end
480
+
481
+ def format_certificate_status(certificate, return_color=cyan)
482
+ out = ""
483
+ status_string = certificate['status']
484
+ if status_string.nil? || status_string.empty? || status_string == "unknown"
485
+ out << "#{white}UNKNOWN#{certificate['statusMessage'] ? "#{return_color} - #{certificate['statusMessage']}" : ''}#{return_color}"
486
+ # elsif certificate['enabled'] == false
487
+ # out << "#{red}DISABLED#{certificate['statusMessage'] ? "#{return_color} - #{certificate['statusMessage']}" : ''}#{return_color}"
488
+ elsif status_string == 'ok'
489
+ out << "#{green}#{status_string.upcase}#{return_color}"
490
+ elsif status_string == 'error' || status_string == 'offline'
491
+ out << "#{red}#{status_string ? status_string.upcase : 'N/A'}#{certificate['statusMessage'] ? "#{return_color} - #{certificate['statusMessage']}" : ''}#{return_color}"
492
+ else
493
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
494
+ end
495
+ out
496
+ end
497
+
498
+
499
+ def certificate_type_column_definitions()
500
+ {
501
+ "ID" => 'id',
502
+ "Code" => 'code',
503
+ "Name" => 'name',
504
+ # "Description" => 'description',
505
+ # "Enabled" => lambda {|it| format_boolean(it['enabled']) },
506
+ "Creatable" => lambda {|it| format_boolean(it['creatable']) },
507
+ }
508
+ end
509
+
510
+ def certificate_type_object_key
511
+ 'certificateType'
512
+ end
513
+
514
+ def certificate_type_list_key
515
+ 'certificateTypes'
516
+ end
517
+
518
+ def find_certificate_type_by_name_or_code_id(val, params={})
519
+ if val.to_s =~ /\A\d{1,}\Z/
520
+ return find_certificate_type_by_id(val, params)
521
+ else
522
+ return find_certificate_type_by_name_or_code(val)
523
+ end
524
+ end
525
+
526
+ def find_certificate_type_by_id(id, params={})
527
+ begin
528
+ json_response = @certificate_types_interface.get(id.to_i, params)
529
+ return json_response[certificate_type_object_key]
530
+ rescue RestClient::Exception => e
531
+ if e.response && e.response.code == 404
532
+ print_red_alert "certificate not found by id '#{id}'"
533
+ else
534
+ raise e
535
+ end
536
+ end
537
+ end
538
+
539
+ def find_certificate_type_by_name(name, params={})
540
+ json_response = @certificate_types_interface.list(params.merge({name: name.to_s}))
541
+ certificate_types = json_response[certificate_type_list_key]
542
+ if certificate_types.empty?
543
+ print_red_alert "certificate type not found by name '#{name}'"
544
+ return nil
545
+ elsif certificate_types.size > 1
546
+ print_red_alert "#{certificate_types.size} certificate types found by name '#{name}'"
547
+ puts_error as_pretty_table(certificate_types, [:id, :code, :name], {color:red})
548
+ print_red_alert "Try using ID instead"
549
+ print reset,"\n"
550
+ return nil
551
+ else
552
+ return certificate_types[0]
553
+ end
554
+ end
555
+
556
+ def get_available_certificate_types(refresh=false)
557
+ if !@available_certificate_types || refresh
558
+ @available_certificate_types = @certificate_types_interface.list(max:10000)[certificate_type_list_key]
559
+ end
560
+ return @available_certificate_types
561
+ end
562
+
563
+ def find_certificate_type_by_name_or_code(name)
564
+ records = get_available_certificate_types()
565
+ record = records.find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
566
+ record = record ? record : records.find { |z| z['id'].to_s == name.to_s }
567
+ if record
568
+ return record
569
+ else
570
+ print_red_alert "certificate type not found by '#{name}'"
571
+ return nil
572
+ end
573
+ end
574
+
575
+ end