morpheus-cli 4.2.1 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46d92f279d50a64ebeb6f5c08623f9e54573cbf4855f08f2bfbb6a201b706522
4
- data.tar.gz: 19c96eb29f3afc1943d6e52667fac9be0364a8d8e87d9b2d04da13671d2b68c4
3
+ metadata.gz: 44226a558c2f02d9ca3aea75025930a2adb9c8b63e41fa6437d14ad55f34211b
4
+ data.tar.gz: debee18e47a2930a5da5cf2085d6463ce23171a70e875bbe81dc33d68ec1b640
5
5
  SHA512:
6
- metadata.gz: d7267a3cfbc6052d7963dcda64401c289031b65b12779721361154dfdbf6344195779a8b44d8da5b45a0d52a1b06c5f53cd8a4cf07b300693e23ec71463adaf6
7
- data.tar.gz: bca94b7c92c9c420224a5695d05f8f35f89c8211c61c74b3e106b2789439df2a46ab8f7adcee04654655cbff6275fddf5da68408f9691711c25152fcb9636c81
6
+ metadata.gz: b3d6087cb623b08e6adabe1bf9e8f214b38c9eef2a627a47442b30c3f9523c7a5487fdff61c63f4fef885176695fc595cd0d751287a3e42b8edbebbf984b06f8
7
+ data.tar.gz: ab8829f3d013e6ef4345447f90cd2defb13e2d91491f5b860ad44fe06ac022b3e2bf8baa3c12e525ab7e33f527e7b51b35043538ab82a52e79c5d628fff3dd0f
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.5.1
2
2
 
3
- RUN gem install morpheus-cli -v 4.2.1
3
+ RUN gem install morpheus-cli -v 4.2.2
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -319,6 +319,10 @@ class Morpheus::APIClient
319
319
  Morpheus::CloudFoldersInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
320
320
  end
321
321
 
322
+ def datastores
323
+ Morpheus::DatastoresInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
324
+ end
325
+
322
326
  def servers
323
327
  Morpheus::ServersInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
324
328
  end
@@ -335,6 +339,14 @@ class Morpheus::APIClient
335
339
  Morpheus::ProvisioningSettingsInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
336
340
  end
337
341
 
342
+ def provisioning_licenses
343
+ Morpheus::ProvisioningLicensesInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
344
+ end
345
+
346
+ def provisioning_license_types
347
+ Morpheus::ProvisioningLicenseTypesInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
348
+ end
349
+
338
350
  def containers
339
351
  Morpheus::ContainersInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
340
352
  end
@@ -670,6 +682,10 @@ class Morpheus::APIClient
670
682
  Morpheus::HealthInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
671
683
  end
672
684
 
673
- # new interfaces get added here
685
+ def invoices
686
+ Morpheus::InvoicesInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
687
+ end
688
+
689
+ # add new interfaces here
674
690
 
675
691
  end
@@ -0,0 +1,18 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::DatastoresInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil, api='data-stores')
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @api_url = "#{base_url}/api/#{api}"
9
+ @expires_at = expires_at
10
+ end
11
+
12
+ def list(params={})
13
+ url = @api_url
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
+ execute(method: :get, url: url, headers: headers)
16
+ end
17
+
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::InvoicesInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @expires_at = expires_at
9
+ end
10
+
11
+ def get(id)
12
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
+ url = "#{@base_url}/api/invoices/#{id}"
14
+ headers = { params: {}, authorization: "Bearer #{@access_token}" }
15
+ execute(method: :get, url: url, headers: headers)
16
+ end
17
+
18
+ def list(params={})
19
+ url = "#{@base_url}/api/invoices"
20
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
21
+ execute(method: :get, url: url, headers: headers)
22
+ end
23
+
24
+ end
@@ -0,0 +1,37 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::ProvisioningLicenseTypesInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @expires_at = expires_at
9
+ end
10
+
11
+ def get(id, params={})
12
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
+ url = build_url(id)
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
+ opts = {method: :get, url: url, headers: headers}
16
+ execute(opts)
17
+ end
18
+
19
+ def list(params={})
20
+ url = build_url()
21
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
22
+ opts = {method: :get, url: url, headers: headers}
23
+ execute(opts)
24
+ end
25
+
26
+ private
27
+
28
+ def build_url(id=nil)
29
+ url = "#{@base_url}/api/provisioning-license-types"
30
+ if id
31
+ url += "/#{id}"
32
+ end
33
+ url
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,66 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::ProvisioningLicensesInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @expires_at = expires_at
9
+ end
10
+
11
+ def get(id, params={})
12
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
+ url = build_url(id)
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
+ opts = {method: :get, url: url, headers: headers}
16
+ execute(opts)
17
+ end
18
+
19
+ def list(params={})
20
+ url = build_url()
21
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
22
+ opts = {method: :get, url: url, headers: headers}
23
+ execute(opts)
24
+ end
25
+
26
+ def create(payload)
27
+ url = build_url()
28
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
29
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
30
+ execute(opts)
31
+ end
32
+
33
+ def update(id, payload)
34
+ url = build_url(id)
35
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
36
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
37
+ execute(opts)
38
+ end
39
+
40
+ def destroy(id, params={})
41
+ url = build_url(id)
42
+ headers = { :params => params, :authorization => "Bearer #{@access_token}"}
43
+ opts = {method: :delete, url: url, headers: headers}
44
+ execute(opts)
45
+ end
46
+
47
+ def reservations(id, params={})
48
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
49
+ url = build_url(id) + "/reservations"
50
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
51
+ opts = {method: :get, url: url, headers: headers}
52
+ execute(opts)
53
+ end
54
+
55
+ private
56
+
57
+ def build_url(id=nil)
58
+ url = "#{@base_url}/api/provisioning-licenses"
59
+ if id
60
+ url += "/#{id}"
61
+ end
62
+ url
63
+ end
64
+
65
+
66
+ end
data/lib/morpheus/cli.rb CHANGED
@@ -161,9 +161,11 @@ module Morpheus
161
161
  load 'morpheus/cli/price_sets_command.rb'
162
162
  load 'morpheus/cli/prices_command.rb'
163
163
  load 'morpheus/cli/provisioning_settings_command.rb'
164
+ load 'morpheus/cli/provisioning_licenses_command.rb'
164
165
  load 'morpheus/cli/budgets_command.rb'
165
166
  load 'morpheus/cli/health_command.rb'
166
- # Your new commands go here...
167
+ load 'morpheus/cli/invoices_command.rb'
168
+ # add new commands here...
167
169
 
168
170
  end
169
171
 
@@ -0,0 +1,339 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'date'
3
+
4
+ class Morpheus::Cli::InvoicesCommand
5
+ include Morpheus::Cli::CliCommand
6
+
7
+ set_command_name :'invoices'
8
+
9
+ register_subcommands :list, :get
10
+
11
+ def connect(opts)
12
+ @api_client = establish_remote_appliance_connection(opts)
13
+ @invoices_interface = @api_client.invoices
14
+ end
15
+
16
+ def handle(args)
17
+ handle_subcommand(args)
18
+ end
19
+
20
+ def list(args)
21
+ options = {}
22
+ params = {}
23
+ ref_ids = []
24
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
25
+ opts.banner = subcommand_usage()
26
+ opts.on('--type TYPE', String, "Find invoices for a Ref Type eg. ComputeSite (Group), ComputeZone (Cloud), ComputeServer (Host), Instance, Container, User") do |val|
27
+ if val.to_s.downcase == 'cloud' || val.to_s.downcase == 'zone'
28
+ params['refType'] = 'ComputeZone'
29
+ elsif val.to_s.downcase == 'instance'
30
+ params['refType'] = 'Instance'
31
+ elsif val.to_s.downcase == 'server' || val.to_s.downcase == 'host'
32
+ params['refType'] = 'ComputeServer'
33
+ elsif val.to_s.downcase == 'cluster'
34
+ params['refType'] = 'ComputeServerGroup'
35
+ elsif val.to_s.downcase == 'group'
36
+ params['refType'] = 'ComputeSite'
37
+ elsif val.to_s.downcase == 'user'
38
+ params['refType'] = 'User'
39
+ else
40
+ params['refType'] = val
41
+ end
42
+ end
43
+ opts.on('--ref-id ID', String, "Find invoices for a Ref ID") do |val|
44
+ ref_ids << val
45
+ end
46
+ opts.on('--group ID', String, "Find invoices for a Group") do |val|
47
+ # params['siteId'] = val
48
+ params['refType'] = 'ComputeSite'
49
+ ref_ids << val
50
+ end
51
+ opts.on('--cloud ID', String, "Find invoices for a Cloud") do |val|
52
+ # params['zoneId'] = val
53
+ params['refType'] = 'ComputeZone'
54
+ ref_ids << val
55
+ end
56
+ opts.on('--instance ID', String, "Find invoices for an Instance") do |val|
57
+ # params['instanceId'] = val
58
+ params['refType'] = 'Instance'
59
+ ref_ids << val
60
+ end
61
+ opts.on('--container ID', String, "Find invoices for a Container") do |val|
62
+ # params['instanceId'] = val
63
+ params['refType'] = 'Container'
64
+ ref_ids << val
65
+ end
66
+ opts.on('--server ID', String, "Find invoices for a Server (Host)") do |val|
67
+ # params['serverId'] = val
68
+ params['refType'] = 'ComputeServer'
69
+ ref_ids << val
70
+ end
71
+ opts.on('--user ID', String, "Find invoices for a User") do |val|
72
+ # params['userId'] = val
73
+ params['refType'] = 'User'
74
+ ref_ids << val
75
+ end
76
+ # opts.on('--cluster ID', String, "Filter by Cluster") do |val|
77
+ # # params['clusterId'] = val
78
+ # params['refType'] = 'ComputeServerGroup'
79
+ # params['refId'] = val.to_i
80
+ # end
81
+ opts.on('--start DATE', String, "Start date in the format YYYY-MM-DD.") do |val|
82
+ params['startDate'] = val #parse_time(val).utc.iso8601
83
+ end
84
+ opts.on('--end DATE', String, "End date in the format YYYY-MM-DD. Default is now.") do |val|
85
+ params['endDate'] = val #parse_time(val).utc.iso8601
86
+ end
87
+ opts.on('--period PERIOD', String, "Period in the format YYYYMM. This can be used instead of start/end.") do |val|
88
+ params['period'] = parse_period(val)
89
+ end
90
+ opts.on('--active [true|false]',String, "Filter by active.") do |val|
91
+ params['active'] = (val.to_s != 'false' && val.to_s != 'off')
92
+ end
93
+ opts.on('--tenant ID', String, "View invoices for a tenant. Default is your own account.") do |val|
94
+ params['accountId'] = val
95
+ end
96
+ build_standard_list_options(opts, options)
97
+ opts.footer = "List invoices."
98
+ end
99
+ optparse.parse!(args)
100
+ connect(options)
101
+ if args.count > 0
102
+ print_error Morpheus::Terminal.angry_prompt
103
+ puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
104
+ return 1
105
+ end
106
+ begin
107
+ params['refId'] = ref_ids unless ref_ids.empty?
108
+ params.merge!(parse_list_options(options))
109
+ @invoices_interface.setopts(options)
110
+ if options[:dry_run]
111
+ print_dry_run @invoices_interface.dry.list(params)
112
+ return
113
+ end
114
+ json_response = @invoices_interface.list(params)
115
+ render_result = render_with_format(json_response, options, 'invoice')
116
+ return 0 if render_result
117
+ invoices = json_response['invoices']
118
+ title = "Morpheus Invoices"
119
+ subtitles = []
120
+ if params['status']
121
+ subtitles << "Status: #{params['status']}"
122
+ end
123
+ if params['alarmStatus'] == 'acknowledged'
124
+ subtitles << "(Acknowledged)"
125
+ end
126
+ if params['startDate']
127
+ subtitles << "Start Date: #{params['startDate']}"
128
+ end
129
+ if params['endDate']
130
+ subtitles << "End Date: #{params['endDate']}"
131
+ end
132
+ subtitles += parse_list_subtitles(options)
133
+ print_h1 title, subtitles
134
+ if invoices.empty?
135
+ print cyan,"No invoices found.",reset,"\n"
136
+ else
137
+ print_invoices_table(invoices, options)
138
+ print_results_pagination(json_response, {:label => "invoice", :n_label => "invoices"})
139
+ end
140
+ print reset,"\n"
141
+ rescue RestClient::Exception => e
142
+ print_rest_exception(e, options)
143
+ return 1
144
+ end
145
+ end
146
+
147
+ def get(args)
148
+ options = {}
149
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
150
+ opts.banner = subcommand_usage("[id]")
151
+ build_standard_get_options(opts, options)
152
+ opts.footer = "Get details about a specific invoice."
153
+ end
154
+ optparse.parse!(args)
155
+ if args.count < 1
156
+ puts optparse
157
+ return 1
158
+ end
159
+ connect(options)
160
+ id_list = parse_id_list(args)
161
+ return run_command_for_each_arg(id_list) do |arg|
162
+ _get(arg, options)
163
+ end
164
+ end
165
+
166
+ def _get(id, options)
167
+
168
+ begin
169
+ @invoices_interface.setopts(options)
170
+ if options[:dry_run]
171
+ print_dry_run @invoices_interface.dry.get(id)
172
+ return
173
+ end
174
+ json_response = @invoices_interface.get(id)
175
+ invoice = json_response['invoice']
176
+ render_result = render_with_format(json_response, options, 'invoice')
177
+ return 0 if render_result
178
+
179
+ print_h1 "Invoice Details"
180
+ print cyan
181
+
182
+
183
+ description_cols = {
184
+ "Invoice ID" => lambda {|it| it['id'] },
185
+ "Type" => lambda {|it| format_invoice_ref_type(it) },
186
+ "Ref ID" => lambda {|it| it['refId'] },
187
+ "Ref Name" => lambda {|it| it['refName'] },
188
+ "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
189
+ "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
190
+ "Active" => lambda {|it| format_boolean(it['active']) },
191
+ "Period" => lambda {|it| format_invoice_period(it) },
192
+ #"Interval" => lambda {|it| it['interval'] },
193
+ "Start" => lambda {|it| format_local_dt(it['startDate']) },
194
+ "End" => lambda {|it| it['endDate'] ? format_local_dt(it['endDate']) : '' },
195
+ "Estimate" => lambda {|it| format_boolean(it['estimate']) },
196
+ "Price" => lambda {|it| format_money(it['totalPrice']) },
197
+ "Cost" => lambda {|it| format_money(it['totalCost']) },
198
+ # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
199
+ # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
200
+ }
201
+ print_description_list(description_cols, invoice)
202
+
203
+ if invoice['rawData'] && !invoice['rawData'].empty?
204
+ print_h2 "Raw Data"
205
+ puts invoice['rawData']
206
+ end
207
+
208
+
209
+ print reset,"\n"
210
+ return 0
211
+ rescue RestClient::Exception => e
212
+ print_rest_exception(e, options)
213
+ return 1
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ # def find_invoice_by_name_or_id(val)
220
+ # if val.to_s =~ /\A\d{1,}\Z/
221
+ # return find_invoice_by_id(val)
222
+ # else
223
+ # return find_invoice_by_name(val)
224
+ # end
225
+ # end
226
+
227
+ def find_invoice_by_id(id)
228
+ begin
229
+ json_response = @invoices_interface.get(id.to_i)
230
+ return json_response['invoice']
231
+ rescue RestClient::Exception => e
232
+ if e.response && e.response.code == 404
233
+ print_red_alert "Invoice not found by id #{id}"
234
+ else
235
+ raise e
236
+ end
237
+ end
238
+ end
239
+
240
+ # def find_invoice_by_name(name)
241
+ # invoices = @invoices_interface.list({name: name.to_s})['invoices']
242
+ # if invoices.empty?
243
+ # print_red_alert "Invoice not found by name #{name}"
244
+ # return nil
245
+ # elsif invoices.size > 1
246
+ # print_red_alert "#{invoices.size} invoices found by name #{name}"
247
+ # print_invoices_table(invoices, {color: red})
248
+ # print_red_alert "Try using ID instead"
249
+ # print reset,"\n"
250
+ # return nil
251
+ # else
252
+ # return invoices[0]
253
+ # end
254
+ # end
255
+
256
+ def print_invoices_table(invoices, opts={})
257
+ columns = [
258
+ {"INVOICE ID" => lambda {|it| it['id'] } },
259
+ {"TYPE" => lambda {|it| format_invoice_ref_type(it) } },
260
+ {"REF ID" => lambda {|it| it['refId'] } },
261
+ {"REF NAME" => lambda {|it| it['refName'] } },
262
+ #{"INTERVAL" => lambda {|it| it['interval'] } },
263
+ {"ACCOUNT" => lambda {|it| it['account'] ? it['account']['name'] : '' } },
264
+ {"ACTIVE" => lambda {|it| format_boolean(it['active']) } },
265
+ {"PERIOD" => lambda {|it| format_invoice_period(it) } },
266
+ {"START" => lambda {|it| format_local_dt(it['startDate']) } },
267
+ {"END" => lambda {|it| it['endDate'] ? format_local_dt(it['endDate']) : '' } },
268
+ {"PRICE" => lambda {|it| format_money(it['totalPrice']) } },
269
+ {"COST" => lambda {|it| format_money(it['totalCost']) } },
270
+ ]
271
+ if opts[:include_fields]
272
+ columns = opts[:include_fields]
273
+ end
274
+ print as_pretty_table(invoices, columns, opts)
275
+ end
276
+
277
+ def format_invoice_ref_type(it)
278
+ if it['refType'] == 'ComputeZone'
279
+ "Cloud"
280
+ elsif it['refType'] == 'Instance'
281
+ "Instance"
282
+ elsif it['refType'] == 'ComputeServer'
283
+ "Host"
284
+ elsif it['refType'] == 'ComputeServerGroup'
285
+ "Cluster"
286
+ elsif it['refType'] == 'ComputeSite'
287
+ "Group"
288
+ else
289
+ it['refType']
290
+ end
291
+ end
292
+
293
+ # convert "202003" to "March 2020"
294
+ def format_invoice_period(it)
295
+ interval = it['interval']
296
+ period = it['period']
297
+ if period
298
+ if interval == 'month'
299
+ year = period[0..3]
300
+ month = period[4..5]
301
+ if year && month
302
+ month_name = Date::MONTHNAMES[month.to_i] || "#{month}?"
303
+ return "#{month_name} #{year}"
304
+ else
305
+ return "#{year}"
306
+ end
307
+ else
308
+ return it['period']
309
+ end
310
+ else
311
+ return "n/a"
312
+ end
313
+ end
314
+
315
+ # convert "March 2020" to "202003"
316
+ def parse_period(period, interval='month')
317
+ if period
318
+ if interval == 'month'
319
+ if period.include?(" ")
320
+ period_parts = period.split(" ")
321
+ month = Date::MONTHNAMES.index(period_parts[0])
322
+ year = period_parts[1].to_i
323
+ if month
324
+ return "#{year}#{month.to_s.rjust(2, '0')}"
325
+ else
326
+ return "#{year}00" # meh, bad month name, raise error probably
327
+ end
328
+ else
329
+ return "#{period}"
330
+ end
331
+ else
332
+ return "#{period}"
333
+ end
334
+ else
335
+ return nil
336
+ end
337
+ end
338
+
339
+ end
@@ -41,6 +41,10 @@ module Morpheus::Cli::ProvisioningHelper
41
41
  @api_client.cloud_datastores
42
42
  end
43
43
 
44
+ def datastores_interface
45
+ @api_client.datastores
46
+ end
47
+
44
48
  def accounts_interface
45
49
  @api_client.accounts
46
50
  end
@@ -323,10 +327,10 @@ module Morpheus::Cli::ProvisioningHelper
323
327
  available_clouds = get_available_clouds(group_id)
324
328
  cloud_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'selectOptions' => get_available_clouds(group_id), 'required' => true, 'description' => 'Select Cloud.', 'defaultValue' => options[:default_cloud] ? options[:default_cloud] : nil}],options[:options],api_client,{groupId: group_id})
325
329
  cloud_id = cloud_prompt['cloud']
330
+ cloud = available_clouds.find {|it| it['value'] == cloud_id}
326
331
  end
327
332
 
328
- cloud = clouds_interface.get(cloud_id)['zone']
329
- cloud_type = cloud['zoneType'] || {}
333
+ cloud_type = clouds_interface.cloud_type(cloud['zoneTypeId'])
330
334
 
331
335
  # Instance Type
332
336
  instance_type_code = nil
@@ -371,6 +375,7 @@ module Morpheus::Cli::ProvisioningHelper
371
375
 
372
376
  # config
373
377
  config = {}
378
+ =begin
374
379
  if cloud_type['code'] == 'amazon' && (cloud['config'] || {})['isVpc'] == 'false' && (cloud['config'] || {})['vpc'] == ''
375
380
  config['isEC2'] = true
376
381
  else
@@ -382,6 +387,7 @@ module Morpheus::Cli::ProvisioningHelper
382
387
  config['isVpcSelectable'] = true
383
388
  end
384
389
  end
390
+ =end
385
391
 
386
392
  payload = {
387
393
  'zoneId' => cloud_id,
@@ -490,7 +496,8 @@ module Morpheus::Cli::ProvisioningHelper
490
496
  layout_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.', 'defaultValue' => default_layout_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_value, creatable: true})['layout']
491
497
  end
492
498
 
493
- layout = find_instance_type_layout_by_id(instance_type['id'], layout_id.to_i)
499
+ # layout = find_instance_type_layout_by_id(instance_type['id'], layout_id.to_i)
500
+ layout = (instance_type['instanceTypeLayouts'] || []).find {|it| it['id'] == layout_id.to_i }
494
501
  if !layout
495
502
  print_red_alert "Layout not found by id #{layout_id}"
496
503
  exit 1
@@ -587,6 +594,7 @@ module Morpheus::Cli::ProvisioningHelper
587
594
  elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
588
595
  pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
589
596
  end
597
+ resource_pool ||= resource_pool_options.find {|it| it['id'] == pool_id}
590
598
  end
591
599
  end
592
600
 
@@ -600,23 +608,33 @@ module Morpheus::Cli::ProvisioningHelper
600
608
  # add selectable datastores for resource pool
601
609
  if options[:select_datastore]
602
610
  begin
603
- service_plan['datastores'] = {'cluster' => [], 'store' => []}
604
- selectable_datastores = cloud_datastores_interface.selectable(cloud_id, {'siteId' => group_id, 'resourcePoolId' => resource_pool['id']})
605
- ['cluster', 'store'].each do |type|
611
+ selectable_datastores = datastores_interface.list({'zoneId' => cloud_id, 'siteId' => group_id, 'resourcePoolId' => resource_pool['id']})
612
+ service_plan['datastores'] = {'clusters' => [], 'datastores' => []}
613
+ ['clusters', 'datastores'].each do |type|
606
614
  service_plan['datastores'][type] ||= []
607
615
  selectable_datastores[type].reject { |ds| service_plan['datastores'][type].find {|it| it['id'] == ds['id']} }.each { |ds|
608
616
  service_plan['datastores'][type] << ds
609
617
  }
610
618
  end
611
619
  rescue => error
620
+ Morpheus::Logging::DarkPrinter.puts "Unable to load available data-stores, using datastores option source instead." if Morpheus::Logging.debug?
612
621
  end
613
622
 
614
623
  if provision_type && provision_type['supportsAutoDatastore']
624
+ service_plan['supportsAutoDatastore'] = true
615
625
  service_plan['autoOptions'] ||= []
616
- if service_plan['datastores']['cluster'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
626
+ if service_plan['datastores'] && service_plan['datastores']['clusters']
627
+ if service_plan['datastores']['clusters'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
628
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
629
+ end
630
+ else
617
631
  service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
618
632
  end
619
- if service_plan['datastores']['store'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
633
+ if service_plan['datastores'] && service_plan['datastores']['datastores']
634
+ if service_plan['datastores']['datastores'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
635
+ service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
636
+ end
637
+ else
620
638
  service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
621
639
  end
622
640
  end
@@ -767,6 +785,8 @@ module Morpheus::Cli::ProvisioningHelper
767
785
  plan_info['datastores'].each do |k, v|
768
786
  v.each do |opt|
769
787
  if !opt.nil?
788
+ k = 'datastores' if k == 'store'
789
+ k = 'clusters' if k == 'cluster'
770
790
  datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
771
791
  end
772
792
  end
@@ -1,5 +1,6 @@
1
1
  require 'term/ansicolor'
2
2
  require 'readline'
3
+ require 'csv'
3
4
  module Morpheus
4
5
  module Cli
5
6
  module OptionTypes
@@ -106,9 +107,21 @@ module Morpheus
106
107
  input_value = ['select', 'multiSelect'].include?(option_type['type']) && option_type['fieldInput'] ? cur_namespace[option_type['fieldInput']] : nil
107
108
  if option_type['type'] == 'number'
108
109
  value = value.to_s.include?('.') ? value.to_f : value.to_i
109
- elsif ['select', 'multiSelect'].include?(option_type['type'])
110
- # this should just fall down through below, with the extra params no_prompt, use_value
110
+ # these select prompts should just fall down through below, with the extra params no_prompt, use_value
111
+ elsif option_type['type'] == 'select'
111
112
  value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (api_params || {}).merge(results), true)
113
+ elsif option_type['type'] == 'multiSelect'
114
+ value_list = value.is_a?(String) ? value.parse_csv : [value].flatten
115
+ input_value_list = input_value.is_a?(String) ? input_value.parse_csv : [input_value].flatten
116
+ if value_list.size > 1
117
+ select_value_list = []
118
+ value_list.each_with_index do |v, i|
119
+ select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, (api_params || {}).merge(results), true)
120
+ end
121
+ value = select_value_list
122
+ else
123
+ value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (api_params || {}).merge(results), true)
124
+ end
112
125
  end
113
126
  if options[:always_prompt] != true
114
127
  value_found = true
@@ -125,7 +138,7 @@ module Morpheus
125
138
  no_prompt = no_prompt || options[:no_prompt]
126
139
  if no_prompt
127
140
  if !value_found
128
- if option_type['defaultValue'] != nil && option_type['type'] != 'select'
141
+ if option_type['defaultValue'] != nil && !['select', 'multiSelect'].include?(option_type['type'])
129
142
  value = option_type['defaultValue']
130
143
  value_found = true
131
144
  end
@@ -0,0 +1,506 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::ProvisioningLicensesCommand
4
+ include Morpheus::Cli::CliCommand
5
+ set_command_name :'provisioning-licenses'
6
+ register_subcommands :list, :get, :add, :update, :remove, :reservations, :'list-types'
7
+
8
+ set_command_hidden
9
+
10
+ def connect(opts)
11
+ @api_client = establish_remote_appliance_connection(opts)
12
+ @provisioning_licenses_interface = @api_client.provisioning_licenses
13
+ @provisioning_license_types_interface = @api_client.provisioning_license_types
14
+ @virtual_images_interface = @api_client.virtual_images
15
+ @options_interface = @api_client.options
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()
27
+ build_standard_list_options(opts, options)
28
+ opts.footer = "List licenses."
29
+ end
30
+ optparse.parse!(args)
31
+ if args.count != 0
32
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
33
+ end
34
+ connect(options)
35
+ begin
36
+ params.merge!(parse_list_options(options))
37
+ @provisioning_licenses_interface.setopts(options)
38
+ if options[:dry_run]
39
+ print_dry_run @provisioning_licenses_interface.dry.list(params)
40
+ return 0
41
+ end
42
+ json_response = @provisioning_licenses_interface.list(params)
43
+ render_result = render_with_format(json_response, options, 'licenses')
44
+ return 0 if render_result
45
+ licenses = json_response['licenses']
46
+
47
+ title = "Morpheus Licenses"
48
+ subtitles = []
49
+ subtitles += parse_list_subtitles(options)
50
+ print_h1 title, subtitles
51
+ if licenses.empty?
52
+ print cyan,"No licenses found.",reset,"\n"
53
+ else
54
+ columns = [
55
+ {"ID" => lambda {|license| license['id'] } },
56
+ {"NAME" => lambda {|license| license['name'] } },
57
+ {"LICENSE TYPE" => lambda {|license| license['licenseType']['name'] rescue license['licenseType'] } },
58
+ {"COPIES" => lambda {|license|
59
+ "#{license['reservationCount']}/#{license['copies']}"
60
+ } },
61
+ {"TENANTS" => lambda {|it| it['tenants'] ? it['tenants'].collect {|acnt| acnt['name']}.join(', ') : '' } },
62
+ {"VIRTUAL IMAGES" => lambda {|it| it['virtualImages'] ? it['virtualImages'].collect {|v| v['name']}.join(', ') : '' } },
63
+ ]
64
+ if options[:include_fields]
65
+ columns = options[:include_fields]
66
+ end
67
+ print as_pretty_table(licenses, columns, options)
68
+ print_results_pagination(json_response)
69
+ end
70
+ print reset,"\n"
71
+
72
+ return 0
73
+ rescue RestClient::Exception => e
74
+ print_rest_exception(e, options)
75
+ exit 1
76
+ end
77
+ end
78
+
79
+ def get(args)
80
+ options = {}
81
+ params = {}
82
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
83
+ opts.banner = subcommand_usage("[license]")
84
+ build_standard_get_options(opts, options)
85
+ opts.footer = "Get details about a license.\n[license] is required. License ID or name"
86
+ end
87
+ optparse.parse!(args)
88
+
89
+ if args.count < 1
90
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
91
+ end
92
+
93
+ connect(options)
94
+
95
+ begin
96
+ @provisioning_licenses_interface.setopts(options)
97
+ if options[:dry_run]
98
+ if args[0].to_s =~ /\A\d{1,}\Z/
99
+ print_dry_run @provisioning_licenses_interface.dry.get(args[0], params)
100
+ else
101
+ print_dry_run @provisioning_licenses_interface.dry.list({name: args[0].to_s})
102
+ end
103
+ return 0
104
+ end
105
+ license = find_license_by_name_or_id(args[0])
106
+ return 1 if license.nil?
107
+ # skip reload if already fetched via get(id)
108
+ json_response = {'license' => license}
109
+ if args[0].to_s != license['id'].to_s
110
+ json_response = @provisioning_licenses_interface.get(license['id'], params)
111
+ license = json_response['license']
112
+ end
113
+ render_result = render_with_format(json_response, options, 'license')
114
+ return 0 if render_result
115
+
116
+
117
+ print_h1 "License Details"
118
+ print cyan
119
+ columns = [
120
+ {"ID" => lambda {|license| license['id'] } },
121
+ {"Name" => lambda {|license| license['name'] } },
122
+ {"License Type" => lambda {|license| license['licenseType']['name'] rescue license['licenseType'] } },
123
+ {"License Key" => lambda {|license| license['licenseKey'] } },
124
+ {"Copies" => lambda {|license|
125
+ "#{license['reservationCount']}/#{license['copies']}"
126
+ } },
127
+ {"Tenants" => lambda {|it| it['tenants'] ? it['tenants'].collect {|acnt| acnt['name']}.join(', ') : '' } },
128
+ {"Virtual Images" => lambda {|it| it['virtualImages'] ? it['virtualImages'].collect {|v| v['name']}.join(', ') : '' } },
129
+ ]
130
+ print_description_list(columns, license, options)
131
+ print reset,"\n"
132
+ return 0
133
+ rescue RestClient::Exception => e
134
+ print_rest_exception(e, options)
135
+ return 1
136
+ end
137
+ end
138
+
139
+ def add(args)
140
+ options = {}
141
+ params = {}
142
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
143
+ opts.banner = subcommand_usage("[name] [options]")
144
+ opts.on( '-t', '--type TYPE', "License Type Code eg. win" ) do |val|
145
+ options[:options]['licenseType'] ||= val
146
+ end
147
+ opts.add_hidden_option('--licenseType')
148
+ build_option_type_options(opts, options, add_license_option_types)
149
+ build_standard_add_options(opts, options)
150
+ opts.footer = "Create license."
151
+ end
152
+ optparse.parse!(args)
153
+ if args.count > 1
154
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args}\n#{optparse}"
155
+ end
156
+ if args[0]
157
+ options[:options]['name'] ||= args[0]
158
+ end
159
+ connect(options)
160
+ begin
161
+ # construct payload
162
+ passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
163
+ payload = nil
164
+ if options[:payload]
165
+ payload = options[:payload]
166
+ payload.deep_merge!({'license' => passed_options}) unless passed_options.empty?
167
+ else
168
+ payload = {
169
+ 'license' => {
170
+ }
171
+ }
172
+ # allow arbitrary -O options
173
+ payload.deep_merge!({'license' => passed_options}) unless passed_options.empty?
174
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_license_option_types, options[:options], @api_client)
175
+ params.deep_merge!(v_prompt)
176
+ payload.deep_merge!({'license' => params}) unless params.empty?
177
+ end
178
+
179
+ @provisioning_licenses_interface.setopts(options)
180
+ if options[:dry_run]
181
+ print_dry_run @provisioning_licenses_interface.dry.create(payload)
182
+ return
183
+ end
184
+ json_response = @provisioning_licenses_interface.create(payload)
185
+ if options[:json]
186
+ print JSON.pretty_generate(json_response)
187
+ print "\n"
188
+ else
189
+ display_name = json_response['license'] ? json_response['license']['name'] : ''
190
+ print_green_success "License #{display_name} added"
191
+ get([json_response['license']['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
192
+ end
193
+ return 0
194
+ rescue RestClient::Exception => e
195
+ print_rest_exception(e, options)
196
+ exit 1
197
+ end
198
+ end
199
+
200
+ def update(args)
201
+ options = {}
202
+ params = {}
203
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
204
+ opts.banner = subcommand_usage("[license] [options]")
205
+ build_option_type_options(opts, options, update_license_option_types)
206
+ build_standard_update_options(opts, options)
207
+ opts.footer = "Update license.\n[license] is required. License ID or name"
208
+ end
209
+ optparse.parse!(args)
210
+
211
+ if args.count != 1
212
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
213
+ end
214
+
215
+ connect(options)
216
+ begin
217
+
218
+ license = find_license_by_name_or_id(args[0])
219
+ return 1 if license.nil?
220
+
221
+ # construct payload
222
+ passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
223
+ payload = nil
224
+ if options[:payload]
225
+ payload = options[:payload]
226
+ payload.deep_merge!({'license' => passed_options}) unless passed_options.empty?
227
+ else
228
+ payload = {
229
+ 'license' => {
230
+ }
231
+ }
232
+ # allow arbitrary -O options
233
+ # virtual_images = passed_options.delete('virtualImages')
234
+ # tenants = passed_options.delete('tenants')
235
+ payload.deep_merge!({'license' => passed_options}) unless passed_options.empty?
236
+ # prompt for options
237
+ #params = Morpheus::Cli::OptionTypes.prompt(update_license_option_types, options[:options], @api_client, options[:params])
238
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(update_license_option_types, options[:options].merge(:no_prompt => true), @api_client)
239
+ params.deep_merge!(v_prompt)
240
+
241
+ # if !virtual_images.empty?
242
+ # params['virtualImages'] = virtual_images # split(",") and lookup ?
243
+ # end
244
+
245
+ payload.deep_merge!({'license' => params}) unless params.empty?
246
+
247
+ if payload.empty? || payload['license'].empty?
248
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
249
+ end
250
+ end
251
+ @provisioning_licenses_interface.setopts(options)
252
+ if options[:dry_run]
253
+ print_dry_run @provisioning_licenses_interface.dry.update(license['id'], payload)
254
+ return
255
+ end
256
+ json_response = @provisioning_licenses_interface.update(license['id'], payload)
257
+ if options[:json]
258
+ print JSON.pretty_generate(json_response)
259
+ print "\n"
260
+ else
261
+ display_name = json_response['license'] ? json_response['license']['name'] : ''
262
+ print_green_success "License #{display_name} updated"
263
+ get([json_response['license']['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
264
+ end
265
+ return 0
266
+ rescue RestClient::Exception => e
267
+ print_rest_exception(e, options)
268
+ exit 1
269
+ end
270
+ end
271
+
272
+ def remove(args)
273
+ options = {}
274
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
275
+ opts.banner = subcommand_usage("[name]")
276
+ build_standard_remove_options(opts, options)
277
+ opts.footer = "Delete license.\n[license] is required. License ID or name"
278
+ end
279
+ optparse.parse!(args)
280
+
281
+ if args.count != 1
282
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
283
+ end
284
+
285
+ connect(options)
286
+ begin
287
+ license = find_license_by_name_or_id(args[0])
288
+ return 1 if license.nil?
289
+
290
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the license #{license['name']}?")
291
+ return 9, "aborted command"
292
+ end
293
+ @provisioning_licenses_interface.setopts(options)
294
+ if options[:dry_run]
295
+ print_dry_run @provisioning_licenses_interface.dry.destroy(license['id'])
296
+ return
297
+ end
298
+ json_response = @provisioning_licenses_interface.destroy(license['id'])
299
+ if options[:json]
300
+ print JSON.pretty_generate(json_response)
301
+ print "\n"
302
+ else
303
+ print_green_success "License #{license['name']} removed"
304
+ # list([] + (options[:remote] ? ["-r",options[:remote]] : []))
305
+ end
306
+ return 0
307
+ rescue RestClient::Exception => e
308
+ print_rest_exception(e, options)
309
+ exit 1
310
+ end
311
+ end
312
+
313
+ def reservations(args)
314
+ params = {}
315
+ options = {}
316
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
317
+ opts.banner = subcommand_usage("[name]")
318
+ build_standard_list_options(opts, options)
319
+ opts.footer = "List reservations for a license.\n[license] is required. License ID or name"
320
+ end
321
+ optparse.parse!(args)
322
+ if args.count != 1
323
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
324
+ end
325
+ connect(options)
326
+ begin
327
+ license = find_license_by_name_or_id(args[0])
328
+ return 1 if license.nil?
329
+ params.merge!(parse_list_options(options))
330
+ @provisioning_licenses_interface.setopts(options)
331
+ if options[:dry_run]
332
+ print_dry_run @provisioning_licenses_interface.dry.reservations(license['id'], params)
333
+ return 0
334
+ end
335
+ json_response = @provisioning_licenses_interface.reservations(license['id'], params)
336
+ render_result = render_with_format(json_response, options, 'reservations')
337
+ return 0 if render_result
338
+ reservations = json_response['reservations']
339
+
340
+ title = "License Reservations: [#{license['id']}] #{license['name']}"
341
+ subtitles = []
342
+ subtitles += parse_list_subtitles(options)
343
+ print_h1 title, subtitles
344
+ if reservations.empty?
345
+ print cyan,"No reservations found.",reset,"\n"
346
+ else
347
+ columns = [
348
+ #{"ID" => lambda {|it| it['id'] } },
349
+ {"RESOURCE ID" => lambda {|it| it['resourceId'] } },
350
+ {"RESOURCE TYPE" => lambda {|it| it['resourceType'] } },
351
+ ]
352
+ if options[:include_fields]
353
+ columns = options[:include_fields]
354
+ end
355
+ print as_pretty_table(reservations, columns, options)
356
+ print_results_pagination(json_response)
357
+ end
358
+ print reset,"\n"
359
+
360
+ return 0
361
+ rescue RestClient::Exception => e
362
+ print_rest_exception(e, options)
363
+ exit 1
364
+ end
365
+ end
366
+
367
+ def list_types(args)
368
+ options = {}
369
+ params = {}
370
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
371
+ opts.banner = subcommand_usage()
372
+ build_standard_list_options(opts, options)
373
+ opts.footer = "List license types."
374
+ end
375
+ optparse.parse!(args)
376
+ if args.count != 0
377
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
378
+ end
379
+ connect(options)
380
+ begin
381
+ params.merge!(parse_list_options(options))
382
+ @provisioning_license_types_interface.setopts(options)
383
+ if options[:dry_run]
384
+ print_dry_run @provisioning_license_types_interface.dry.list(params)
385
+ return 0
386
+ end
387
+ json_response = @provisioning_license_types_interface.list(params)
388
+ render_result = render_with_format(json_response, options, 'licenseTypes')
389
+ return 0 if render_result
390
+ license_types = json_response['licenseTypes']
391
+
392
+ title = "Morpheus License Types"
393
+ subtitles = []
394
+ subtitles += parse_list_subtitles(options)
395
+ print_h1 title, subtitles
396
+ if license_types.empty?
397
+ print cyan,"No license types found.",reset,"\n"
398
+ else
399
+ columns = [
400
+ {"ID" => lambda {|it| it['id'] } },
401
+ {"NAME" => lambda {|it| it['name'] } },
402
+ {"CODE" => lambda {|it| it['code'] } },
403
+ ]
404
+ if options[:include_fields]
405
+ columns = options[:include_fields]
406
+ end
407
+ print as_pretty_table(license_types, columns, options)
408
+ print_results_pagination(json_response)
409
+ end
410
+ print reset,"\n"
411
+
412
+ return 0
413
+ rescue RestClient::Exception => e
414
+ print_rest_exception(e, options)
415
+ exit 1
416
+ end
417
+ end
418
+
419
+ private
420
+
421
+ def find_license_by_name_or_id(val)
422
+ if val.to_s =~ /\A\d{1,}\Z/
423
+ return find_license_by_id(val)
424
+ else
425
+ return find_license_by_name(val)
426
+ end
427
+ end
428
+
429
+ def find_license_by_id(id)
430
+ raise "#{self.class} has not defined @provisioning_licenses_interface" if @provisioning_licenses_interface.nil?
431
+ begin
432
+ json_response = @provisioning_licenses_interface.get(id)
433
+ return json_response['license']
434
+ rescue RestClient::Exception => e
435
+ if e.response && e.response.code == 404
436
+ print_red_alert "License not found by id #{id}"
437
+ else
438
+ raise e
439
+ end
440
+ end
441
+ end
442
+
443
+ def find_license_by_name(name)
444
+ raise "#{self.class} has not defined @provisioning_licenses_interface" if @provisioning_licenses_interface.nil?
445
+ licenses = @provisioning_licenses_interface.list({name: name.to_s})['licenses']
446
+ if licenses.empty?
447
+ print_red_alert "License not found by name #{name}"
448
+ return nil
449
+ elsif licenses.size > 1
450
+ print_red_alert "#{licenses.size} Licenses found by name #{name}"
451
+ print as_pretty_table(licenses, [:id,:name], {color:red})
452
+ print reset,"\n"
453
+ return nil
454
+ else
455
+ return licenses[0]
456
+ end
457
+ end
458
+
459
+ # def get_license_types_dropdown()
460
+ # [{"name" => "Windows", "value" => "win"}]
461
+ # end
462
+
463
+ def get_license_types_dropdown()
464
+ @provisioning_license_types_interface.list({max:10000})['licenseTypes'].collect { |it|
465
+ {"name" => it["name"], "value" => it["code"]}
466
+ }
467
+ end
468
+
469
+ def get_virtual_images_dropdown()
470
+ @virtual_images_interface.list({max:10000})['virtualImages'].collect { |it|
471
+ {"name" => it["name"], "value" => it["id"]}
472
+ }
473
+ end
474
+
475
+ def add_license_option_types
476
+ [
477
+ {'fieldName' => 'licenseType', 'fieldLabel' => 'License Type', 'type' => 'select', 'optionSource' => lambda {
478
+ # @options_interface.options_for_source("licenseTypes", {})['data']
479
+ get_license_types_dropdown()
480
+ }, 'required' => true, 'displayOrder' => 1},
481
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 2},
482
+ {'fieldName' => 'licenseKey', 'fieldLabel' => 'License Key', 'type' => 'text', 'required' => true, 'displayOrder' => 3},
483
+ {'fieldName' => 'orgName', 'fieldLabel' => 'Org Name', 'type' => 'text', 'description' => "The Organization Name (if applicable) related to the license key", 'displayOrder' => 4},
484
+ {'fieldName' => 'fullName', 'fieldLabel' => 'Full Name', 'type' => 'text', 'description' => "The Full Name (if applicable) related to the license key", 'displayOrder' => 5},
485
+ {'fieldName' => 'licenseVersion', 'fieldLabel' => 'Version', 'type' => 'text', 'displayOrder' => 6},
486
+ {'fieldName' => 'copies', 'fieldLabel' => 'Copies', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 7},
487
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 8},
488
+ {'fieldName' => 'virtualImages', 'fieldLabel' => 'Virtual Images', 'type' => 'multiSelect', 'optionSource' => lambda {
489
+ # @options_interface.options_for_source("virtualImages", {})['data']
490
+ get_virtual_images_dropdown()
491
+ }, 'displayOrder' => 9},
492
+ {'fieldName' => 'tenants', 'fieldLabel' => 'Tenants', 'type' => 'multiSelect', 'optionSource' => lambda {
493
+ @options_interface.options_for_source("allTenants", {})['data']
494
+ }, 'displayOrder' => 10},
495
+ ]
496
+ end
497
+
498
+ def update_license_option_types
499
+ list = add_license_option_types()
500
+ list = list.reject {|it| ["licenseType", "licenseKey", "orgName", "fullName"].include? it['fieldName'] }
501
+ list.each {|it| it.delete('required') }
502
+ list.each {|it| it.delete('defaultValue') }
503
+ list
504
+ end
505
+
506
+ end
@@ -483,9 +483,6 @@ class Morpheus::Cli::ServicePlanCommand
483
483
  opts.on('--active [on|off]', String, "Can be used to enable / disable the plan. Default is on") do |val|
484
484
  params['active'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
485
485
  end
486
- opts.on('--editable [on|off]', String, "Can be used to enable / disable the editability of the service plan. Default is on") do |val|
487
- params['editable'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
488
- end
489
486
  opts.on('--storage [AMOUNT]', String, "Storage size is required. Assumes GB unless optional modifier specified, ex: 512MB" ) do |val|
490
487
  bytes = parse_bytes_param(val, '--storage', 'GB')
491
488
  params['maxStorage'] = bytes[:bytes]
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "4.2.1"
4
+ VERSION = "4.2.2"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.1
4
+ version: 4.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2020-03-11 00:00:00.000000000 Z
14
+ date: 2020-03-14 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -194,6 +194,7 @@ files:
194
194
  - lib/morpheus/api/custom_instance_types_interface.rb
195
195
  - lib/morpheus/api/cypher_interface.rb
196
196
  - lib/morpheus/api/dashboard_interface.rb
197
+ - lib/morpheus/api/datastores_interface.rb
197
198
  - lib/morpheus/api/deploy_interface.rb
198
199
  - lib/morpheus/api/deployments_interface.rb
199
200
  - lib/morpheus/api/environments_interface.rb
@@ -210,6 +211,7 @@ files:
210
211
  - lib/morpheus/api/instance_types_interface.rb
211
212
  - lib/morpheus/api/instances_interface.rb
212
213
  - lib/morpheus/api/integrations_interface.rb
214
+ - lib/morpheus/api/invoices_interface.rb
213
215
  - lib/morpheus/api/jobs_interface.rb
214
216
  - lib/morpheus/api/key_pairs_interface.rb
215
217
  - lib/morpheus/api/library_cluster_layouts_interface.rb
@@ -255,6 +257,8 @@ files:
255
257
  - lib/morpheus/api/prices_interface.rb
256
258
  - lib/morpheus/api/processes_interface.rb
257
259
  - lib/morpheus/api/provision_types_interface.rb
260
+ - lib/morpheus/api/provisioning_license_types_interface.rb
261
+ - lib/morpheus/api/provisioning_licenses_interface.rb
258
262
  - lib/morpheus/api/provisioning_settings_interface.rb
259
263
  - lib/morpheus/api/reports_interface.rb
260
264
  - lib/morpheus/api/roles_interface.rb
@@ -340,6 +344,7 @@ files:
340
344
  - lib/morpheus/cli/instance_types.rb
341
345
  - lib/morpheus/cli/instances.rb
342
346
  - lib/morpheus/cli/integrations_command.rb
347
+ - lib/morpheus/cli/invoices_command.rb
343
348
  - lib/morpheus/cli/jobs_command.rb
344
349
  - lib/morpheus/cli/key_pairs.rb
345
350
  - lib/morpheus/cli/library.rb
@@ -391,6 +396,7 @@ files:
391
396
  - lib/morpheus/cli/price_sets_command.rb
392
397
  - lib/morpheus/cli/prices_command.rb
393
398
  - lib/morpheus/cli/processes_command.rb
399
+ - lib/morpheus/cli/provisioning_licenses_command.rb
394
400
  - lib/morpheus/cli/provisioning_settings_command.rb
395
401
  - lib/morpheus/cli/recent_activity_command.rb
396
402
  - lib/morpheus/cli/remote.rb