morpheus-cli 4.2.1 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.
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