morpheus-cli 2.11.1 → 2.11.3

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
  SHA1:
3
- metadata.gz: 7a9c598b82ebbf8eea995f58a454bf8a52defef0
4
- data.tar.gz: 6a81fff1ddc710ca3d56488670586caf7844ab93
3
+ metadata.gz: 6d561294737f605bb14714d84f21984797f012b2
4
+ data.tar.gz: 857aa6f016e8f4db4cfaea280703a7f71adac83a
5
5
  SHA512:
6
- metadata.gz: 5821cf3be29b11830eab7cf6e87f15ebb3f0e783d3d49664c5dbd6b824e775e6546b2da09a52983edf1dfd60058c51f40c9ae3cd591ea61fb5fb730b2766da6a
7
- data.tar.gz: 356f77e67812b6f8b63e85f3c1a95c0206e5a5f456c912a65aae311f0040ad1eea4fae2d77d840a85be1a03bbc342db0b1097dfa07eb623ef1bb71e699b21786
6
+ metadata.gz: 941e2fd2a2478c7af97e604f3bbea977874a585b5a4dcd0d526bce39434b4461b9859556f509054239b0fd957919f36b5606304e314f2befda86667d888aec15
7
+ data.tar.gz: 1fe0c7f8d07df32dd4ea2b771ef2b21106831115d3d3c0a51358e8b9c640be215a3057b12f8f3608524fb46aee02b8f6fa5eef2f60599b6653e1553df6e3cb16
@@ -3,13 +3,14 @@ require 'uri'
3
3
  require 'rest-client'
4
4
 
5
5
  class Morpheus::APIClient
6
- def initialize(access_token, refresh_token=nil,expires_in = nil, base_url=nil)
6
+ def initialize(access_token, refresh_token=nil,expires_in = nil, base_url=nil, verify_ssl=true)
7
7
  @access_token = access_token
8
8
  @refresh_token = refresh_token
9
9
  @base_url = base_url
10
10
  if expires_in != nil
11
11
  @expires_at = DateTime.now + expires_in.seconds
12
12
  end
13
+ set_ssl_verification_enabled(verify_ssl)
13
14
  end
14
15
 
15
16
  def dry_run(val=true)
@@ -21,7 +22,23 @@ class Morpheus::APIClient
21
22
  dry_run(true)
22
23
  end
23
24
 
25
+ def ssl_verification_enabled?
26
+ @verify_ssl
27
+ end
28
+
29
+ def set_ssl_verification_enabled(val)
30
+ @verify_ssl = !!val
31
+ end
32
+
24
33
  def execute(opts, parse_json=true)
34
+ # @verify_ssl is not used atm
35
+ # todo: finish this and use it instead of the global variable RestClient.ssl_verification_enabled?
36
+ # gotta clean up all APIClient subclasses new() methods to support this
37
+ # the CliCommand subclasses should be changed to @users_interface = @api_client.users
38
+ # also.. Credentials.new()
39
+ if @verify_ssl == false
40
+ opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
41
+ end
25
42
  if @dry_run
26
43
  # JD: could return a Request object instead...
27
44
  return opts
@@ -20,7 +20,7 @@ class Morpheus::DashboardInterface < Morpheus::APIClient
20
20
  end
21
21
 
22
22
  def recent_activity(account_id=nil, options={})
23
- url = "#{@base_url}/api/dashboard/recentActivity"
23
+ url = "#{@base_url}/api/dashboard/recent-activity"
24
24
  headers = { params: {}, authorization: "Bearer #{@access_token}" }
25
25
  headers[:params].merge!(options)
26
26
  headers[:params]['accountId'] = account_id if account_id
@@ -547,6 +547,20 @@ module Morpheus
547
547
  @appliance_name = appliance[:name]
548
548
  @appliance_url = appliance[:host] || appliance[:url] # it's :host in the YAML..heh
549
549
 
550
+ # instead of toggling this global value
551
+ # this should just be an attribute of the api client
552
+ # for now, this fixes the issue where passing --insecure or --remote
553
+ # would then apply to all subsequent commands...
554
+ if options[:insecure]
555
+ Morpheus::RestClient.enable_ssl_verification = false
556
+ else
557
+ if appliance[:insecure] && Morpheus::RestClient.ssl_verification_enabled?
558
+ Morpheus::RestClient.enable_ssl_verification = false
559
+ elsif !appliance[:insecure] && !Morpheus::RestClient.ssl_verification_enabled?
560
+ Morpheus::RestClient.enable_ssl_verification = true
561
+ end
562
+ end
563
+
550
564
  # todo: support old way of accepting --username and --password on the command line
551
565
  # it's probably better not to do that tho, just so it stays out of history files
552
566
 
@@ -277,7 +277,7 @@ class Morpheus::Cli::Groups
277
277
  print JSON.pretty_generate(json_response)
278
278
  print "\n"
279
279
  else
280
- print_green_success "Added cloud #{cloud["id"]} to group #{group['name']}"
280
+ print_green_success "Added cloud #{cloud["name"]} to group #{group['name']}"
281
281
  #list([])
282
282
  get([group["id"]])
283
283
  end
@@ -93,13 +93,18 @@ class Morpheus::Cli::InstanceTypes
93
93
 
94
94
  if options[:json]
95
95
  print JSON.pretty_generate(json_response), "\n"
96
- return
96
+ return 0
97
97
  end
98
98
 
99
99
  instance_types = json_response['instanceTypes']
100
- print_h1 "Morpheus Instance Types"
100
+ title = "Morpheus Instance Types"
101
+ subtitles = []
102
+ if params[:phrase]
103
+ subtitles << "Search: #{params[:phrase]}".strip
104
+ end
105
+ print_h1 title, subtitles
101
106
  if instance_types.empty?
102
- puts yellow,"No instance types currently configured.",reset
107
+ print yellow,"No instance types found.",reset,"\n"
103
108
  else
104
109
  instance_types.each do |instance_type|
105
110
  versions = instance_type['versions'].join(', ')
@@ -113,12 +118,12 @@ class Morpheus::Cli::InstanceTypes
113
118
  # end
114
119
  #print JSON.pretty_generate(instance_type['instanceTypeLayouts'].first), "\n"
115
120
  end
116
-
117
121
  end
118
122
  print reset,"\n"
123
+ return 0
119
124
  rescue RestClient::Exception => e
120
125
  print_rest_exception(e, options)
121
- exit 1
126
+ return 1
122
127
  end
123
128
  end
124
129
  end
@@ -171,7 +171,8 @@ class Morpheus::Cli::Instances
171
171
  def add(args)
172
172
  options = {}
173
173
  optparse = OptionParser.new do|opts|
174
- opts.banner = subcommand_usage("[type] [name]")
174
+ # opts.banner = subcommand_usage("[type] [name]")
175
+ opts.banner = subcommand_usage("[name] -c CLOUD -t TYPE")
175
176
  opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
176
177
  options[:group] = val
177
178
  end
@@ -187,32 +188,73 @@ class Morpheus::Cli::Instances
187
188
  opts.on("--layout-size NUMBER", Integer, "Apply a multiply factor of containers/vms within the instance") do |val|
188
189
  options[:layout_size] = val.to_i
189
190
  end
191
+ opts.on("--workflow ID", String, "Automation: Workflow ID") do |val|
192
+ options[:workflow_id] = val.to_i
193
+ end
194
+ # opts.on('-L', "--lb", "Enable Load Balancer") do
195
+ # options[:enable_load_balancer] = true
196
+ # end
197
+ opts.on("--shutdown-days NUMBER", Integer, "Automation: Shutdown Days") do |val|
198
+ options[:expire_days] = val.to_i
199
+ end
200
+ opts.on("--expire-days NUMBER", Integer, "Automation: Expiration Days") do |val|
201
+ options[:expire_days] = val.to_i
202
+ end
203
+ opts.on("--create-backup on|off", String, "Automation: Create Backups. Default is off") do |val|
204
+ options[:create_backup] = ['on','true','1'].include?(val.to_s.downcase) ? 'on' : 'off'
205
+ end
190
206
  build_common_options(opts, options, [:options, :json, :dry_run, :remote])
191
207
  end
192
208
 
193
209
  optparse.parse!(args)
194
210
  connect(options)
195
211
 
196
- # support old format of `instance add TYPE NAME`
212
+ # this is the old format of `instance add TYPE NAME`
213
+ # JD: it seems confusing, let's deprecate and go with `instances add [NAME] -t TYPE`
197
214
  if args[0]
198
215
  options[:instance_type_code] = args[0]
199
216
  end
200
217
  if args[1]
201
218
  options[:instance_name] = args[1]
202
219
  end
220
+
221
+ # if args.count > 1
222
+ # print_error Morpheus::Terminal.angry_prompt
223
+ # puts_error "#{command_name} add has just 1 (optional) argument: NAME. Got #{args.count} arguments: #{args.join(', ')}\n#{optparse}"
224
+ # return 1
225
+ # end
226
+ # if args[0]
227
+ # options[:instance_name] = args[0]
228
+ # end
229
+
203
230
  # use active group by default
204
231
  options[:group] ||= @active_group_id
205
232
 
206
233
  options[:name_required] = true
207
234
  begin
208
-
235
+ # this provisioning helper method handles all (most) of the parsing and prompting
236
+ # and it relies on the method to exit non-zero on error, like a bad CLOUD or TYPE value
209
237
  payload = prompt_new_instance(options)
238
+
239
+ # other stuff
210
240
  payload[:copies] = options[:copies] if options[:copies] && options[:copies] > 0
211
241
  payload[:layoutSize] = options[:layout_size] if options[:layout_size] && options[:layout_size] > 0 # aka Scale Factor
242
+ payload[:createBackup] = options[:create_backup] ? 'on' : 'off' if options[:create_backup] == true
243
+ payload['instance']['expireDays'] = options[:expire_days] if options[:expire_days]
244
+ payload['instance']['shutdownDays'] = options[:shutdown_days] if options[:shutdown_days]
245
+ if options[:workflow_id]
246
+ payload['taskSetId'] = options[:workflow_id]
247
+ end
248
+ if options[:enable_load_balancer]
249
+ lb_payload = prompt_instance_load_balancer(payload['instance'], nil, options)
250
+ payload.deep_merge!(lb_payload)
251
+ end
252
+
212
253
  if options[:dry_run]
213
254
  print_dry_run @instances_interface.dry.create(payload)
214
- return
255
+ return 0
215
256
  end
257
+
216
258
  json_response = @instances_interface.create(payload)
217
259
  if options[:json]
218
260
  puts as_json(json_response, options)
@@ -221,9 +263,10 @@ class Morpheus::Cli::Instances
221
263
  print_green_success "Provisioning instance #{instance_name}"
222
264
  #list([])
223
265
  end
266
+ return 0
224
267
  rescue RestClient::Exception => e
225
268
  print_rest_exception(e, options)
226
- exit 1
269
+ return 1
227
270
  end
228
271
  end
229
272
 
@@ -598,6 +641,20 @@ class Morpheus::Cli::Instances
598
641
  }
599
642
  print_description_list(description_cols, instance)
600
643
 
644
+ if instance['statusMessage']
645
+ print_h2 "Status Message"
646
+ if instance['status'] == 'failed'
647
+ print red, instance['statusMessage'], reset
648
+ else
649
+ print instance['statusMessage']
650
+ end
651
+ print "\n"
652
+ end
653
+ if instance['errorMessage']
654
+ print_h2 "Error Message"
655
+ print red, instance['errorMessage'], reset
656
+ print "\n"
657
+ end
601
658
  if stats
602
659
  print_h2 "Instance Usage"
603
660
  print_stats_usage(stats)
@@ -650,15 +707,16 @@ class Morpheus::Cli::Instances
650
707
  if current_instance_lb
651
708
  print_h2 "Load Balancer"
652
709
  print cyan
710
+ # this api response is going to change again.. port is no longer returned atm.
653
711
  description_cols = {
654
712
  "LB ID" => lambda {|it| it['loadBalancer']['id'] },
655
713
  "Name" => lambda {|it| it['loadBalancer']['name'] },
656
714
  "Type" => lambda {|it| it['loadBalancer']['type'] ? it['loadBalancer']['type']['name'] : '' },
657
715
  "Host Name" => lambda {|it| it['loadBalancer']['host'] }, # instance.hostName ?
658
- "Port" => lambda {|it| it['port']['port'] },
659
- "Protocol" => lambda {|it| it['port']['proxyProtocol'] },
660
- "SSL Enabled" => lambda {|it| format_boolean it['port']['sslEnabled'] },
661
- "Cert" => lambda {|it| it['port']['sslCert'] ? it['port']['sslCert']['name'] : 'N/A' } # api needs to return this too..
716
+ "Port" => lambda {|it| it['port'] ? it['port']['port'] : '' },
717
+ "Protocol" => lambda {|it| it['port'] ? it['port']['proxyProtocol'] : '' },
718
+ "SSL Enabled" => lambda {|it| it['port'] ? format_boolean(it['port']['sslEnabled']) : '' },
719
+ "Cert" => lambda {|it| (it['port'] && it['port']['sslCert']) ? it['port']['sslCert']['name'] : '' }
662
720
  }
663
721
  print_description_list(description_cols, current_instance_lb)
664
722
  print "\n", reset
@@ -2054,20 +2112,20 @@ class Morpheus::Cli::Instances
2054
2112
  }
2055
2113
 
2056
2114
  cur_host_name = instance['hostName']
2057
- #host_name = params = Morpheus::Cli::OptionTypes.prompt([{fieldName:'hostName'}], options[:options], @api_client, {})
2115
+ #host_name = params = Morpheus::Cli::OptionTypes.prompt([{'fieldName'=>'hostName', 'label'=>'Host Name', 'defaultValue'=>cur_host_name}], options[:options], @api_client, {})
2058
2116
  payload['instance']['hostName'] = instance['hostName']
2059
2117
 
2060
- payload['loadBalancerId'] = 9999
2118
+ #payload['loadBalancerId'] = params['loadBalancerId']
2061
2119
 
2062
2120
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to update the load balancer for instance '#{instance['name']}'?", options)
2063
2121
  return 9, "aborted command"
2064
2122
  end
2065
2123
 
2066
2124
  if options[:dry_run]
2067
- print_dry_run @instances_interface.dry.update_threshold(instance['id'], payload)
2125
+ print_dry_run @instances_interface.dry.update_load_balancer(instance['id'], payload)
2068
2126
  return
2069
2127
  end
2070
- json_response = @instances_interface.update_threshold(instance['id'], payload)
2128
+ json_response = @instances_interface.update_load_balancer(instance['id'], payload)
2071
2129
  if options[:json]
2072
2130
  puts as_json(json_response, options)
2073
2131
  else
@@ -29,7 +29,7 @@ class Morpheus::Cli::LoadBalancers
29
29
  options = {}
30
30
  optparse = OptionParser.new do|opts|
31
31
  opts.banner = subcommand_usage()
32
- build_common_options(opts, options, [:list, :json, :dry_run, :remote])
32
+ build_common_options(opts, options, [:list, :json, :csv, :yaml, :fields, :dry_run, :remote])
33
33
  end
34
34
  optparse.parse!(args)
35
35
  connect(options)
@@ -44,24 +44,41 @@ class Morpheus::Cli::LoadBalancers
44
44
  end
45
45
  json_response = @load_balancers_interface.get(params)
46
46
  if options[:json]
47
- print JSON.pretty_generate(json_response)
47
+ if options[:include_fields]
48
+ json_response = {"loadBalancers" => filter_data(json_response["loadBalancers"], options[:include_fields]) }
49
+ end
50
+ puts as_json(json_response, options)
51
+ return 0
52
+ elsif options[:csv]
53
+ puts records_as_csv(json_response["loadBalancers"], options)
54
+ return 0
55
+ elsif options[:yaml]
56
+ if options[:include_fields]
57
+ json_response = {"loadBalancers" => filter_data(json_response["loadBalancers"], options[:include_fields]) }
58
+ end
59
+ puts as_yaml(json_response, options)
60
+ return 0
48
61
  else
49
62
  lbs = json_response['loadBalancers']
50
63
  print_h1 "Morpheus Load Balancers"
51
64
  if lbs.empty?
52
- print cyan,"No load balancers found.",reset,"\n"
65
+ print yellow,"No load balancers found.",reset,"\n"
53
66
  else
54
- print cyan
55
- lb_table_data = lbs.collect do |lb|
56
- {name: lb['name'], id: lb['id'], type: lb['type']['name']}
57
- end
58
- tp lb_table_data, :id, :name, :type
67
+ columns = [
68
+ {"ID" => 'id'},
69
+ {"Name" => 'name'},
70
+ {"Type" => lambda {|it| it['type'] ? it['type']['name'] : '' } },
71
+ {"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' } },
72
+ {"Host" => lambda {|it| it['host'] } },
73
+ ]
74
+ print as_pretty_table(lbs, columns, options)
59
75
  end
60
76
  print reset,"\n"
77
+ return 0
61
78
  end
62
- rescue RestClient::Exception => e
79
+ rescue RestClient::Exception => e
63
80
  print_rest_exception(e, options)
64
- exit 1
81
+ return 1
65
82
  end
66
83
  end
67
84
 
@@ -69,7 +86,7 @@ class Morpheus::Cli::LoadBalancers
69
86
  options = {}
70
87
  optparse = OptionParser.new do|opts|
71
88
  opts.banner = subcommand_usage("[name]")
72
- build_common_options(opts, options, [:json, :dry_run, :remote])
89
+ build_common_options(opts, options, [:json, :csv, :yaml, :fields, :dry_run, :remote])
73
90
  end
74
91
  optparse.parse!(args)
75
92
  if args.count < 1
@@ -89,15 +106,62 @@ class Morpheus::Cli::LoadBalancers
89
106
  end
90
107
  lb = find_lb_by_name_or_id(lb_name)
91
108
  exit 1 if lb.nil?
109
+ # refetch
110
+ json_response = @load_balancers_interface.get(lb['id'])
92
111
  lb_type = load_balancer_type_for_name_or_id(lb['type']['code'])
112
+ #puts "LB TYPE: #{lb_type}"
93
113
  if options[:json]
94
114
  puts JSON.pretty_generate({loadBalancer: lb})
115
+ if options[:include_fields]
116
+ json_response = {"loadBalancer" => filter_data(json_response["loadBalancer"], options[:include_fields]) }
117
+ end
118
+ puts as_json(json_response, options)
119
+ return 0
120
+ elsif options[:csv]
121
+ puts records_as_csv(json_response["loadBalancer"], options)
122
+ return 0
123
+ elsif options[:yaml]
124
+ if options[:include_fields]
125
+ json_response = {"loadBalancer" => filter_data(json_response["loadBalancer"], options[:include_fields]) }
126
+ end
127
+ puts as_yaml(json_response, options)
128
+ return 0
95
129
  else
96
- print "\n", cyan, "Lb #{lb['name']} - #{lb['type']['name']}\n\n"
97
- # lb_type['optionTypes'].sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.each do |optionType|
98
- # puts " #{optionType['fieldLabel']} : " + (optionType['type'] == 'password' ? "#{task['taskOptions'][optionType['fieldName']] ? '************' : ''}" : "#{task['taskOptions'][optionType['fieldName']] || optionType['defaultValue']}")
99
- # end
100
- print reset,"\n\n"
130
+ print_h1 "Load Balancer Details"
131
+ description_cols = {
132
+ "ID" => 'id',
133
+ "Name" => 'name',
134
+ "Description" => 'description',
135
+ "Type" => lambda {|it| it['type'] ? it['type']['name'] : '' },
136
+ "Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
137
+ "Visibility" => 'visibility',
138
+ "IP" => 'ip',
139
+ "Host" => 'host',
140
+ "Port" => 'port',
141
+ "Username" => 'username',
142
+ # "SSL Enabled" => lambda {|it| format_boolean it['sslEnabled'] },
143
+ # "SSL Cert" => lambda {|it| it['sslCert'] ? it['sslCert']['name'] : '' },
144
+ # "SSL" => lambda {|it| it['sslEnabled'] ? "Yes (#{it['sslCert'] ? it['sslCert']['name'] : 'none'})" : "No" },
145
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
146
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
147
+ }
148
+ print_description_list(description_cols, lb)
149
+
150
+
151
+ if lb['ports'] && lb['ports'].size > 0
152
+ print_h2 "LB Ports"
153
+ columns = [
154
+ {"ID" => 'id'},
155
+ {"Name" => 'name'},
156
+ #{"Description" => 'description'},
157
+ {"Port" => lambda {|it| it['port'] } },
158
+ {"Protocol" => lambda {|it| it['proxyProtocol'] } },
159
+ {"SSL" => lambda {|it| it['sslEnabled'] ? "Yes (#{it['sslCert'] ? it['sslCert']['name'] : 'none'})" : "No" } },
160
+ ]
161
+ print as_pretty_table(lb['ports'], columns, options)
162
+ end
163
+ print reset,"\n"
164
+ return 0
101
165
  end
102
166
  rescue RestClient::Exception => e
103
167
  print_rest_exception(e, options)
@@ -482,7 +482,7 @@ module Morpheus::Cli::PrintHelper
482
482
  max_label_width = 0
483
483
  justify = opts.key?(:justify) ? opts[:justify] : "right"
484
484
  do_wrap = opts.key?(:wrap) ? !!opts[:wrap] : true
485
-
485
+ color = opts.key?(:color) ? opts[:color] : cyan
486
486
  rows = []
487
487
 
488
488
  columns.flatten.each do |column_def|
@@ -501,6 +501,7 @@ module Morpheus::Cli::PrintHelper
501
501
  end
502
502
 
503
503
  out = ""
504
+ out << color if color
504
505
  rows.each do |row|
505
506
  value = row[:value].to_s
506
507
  if do_wrap
@@ -511,6 +512,7 @@ module Morpheus::Cli::PrintHelper
511
512
  end
512
513
  out << format_dt_dd(row[:label], value, label_width, justify) + "\n"
513
514
  end
515
+ out << reset if color
514
516
  return out
515
517
  end
516
518
 
@@ -324,6 +324,12 @@ module Morpheus::Cli::ProvisioningHelper
324
324
  payload['evars'] = evars
325
325
  end
326
326
 
327
+ # prompt for metadata variables
328
+ metadata = prompt_metadata(options)
329
+ if !metadata.empty?
330
+ payload['metadata'] = metadata
331
+ end
332
+
327
333
  return payload
328
334
  end
329
335
 
@@ -806,6 +812,10 @@ module Morpheus::Cli::ProvisioningHelper
806
812
  # puts JSON.pretty_generate(zone_network_options_json)
807
813
  zone_network_data = zone_network_options_json['data'] || {}
808
814
  networks = zone_network_data['networks']
815
+ network_groups = zone_network_data['networkGroups']
816
+ if network_groups
817
+ networks = network_groups + networks
818
+ end
809
819
  network_interface_types = (zone_network_data['networkTypes'] || []).sort { |x,y| x['displayOrder'] <=> y['displayOrder'] }
810
820
  enable_network_type_selection = (zone_network_data['enableNetworkTypeSelection'] == 'on' || zone_network_data['enableNetworkTypeSelection'] == true)
811
821
  has_networks = zone_network_data["hasNetworks"] == true
@@ -853,8 +863,8 @@ module Morpheus::Cli::ProvisioningHelper
853
863
  # choose network
854
864
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'networkId', 'type' => 'select', 'fieldLabel' => "Network", 'selectOptions' => network_options, 'required' => true, 'skipSingleOption' => false, 'description' => 'Choose a network for this interface.', 'defaultValue' => network_interface['networkId']}], options[:options])
855
865
  network_interface['network'] = {}
856
- network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_i
857
- selected_network = networks.find {|it| it["id"] == network_interface['network']['id'] }
866
+ network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_s
867
+ selected_network = networks.find {|it| it["id"].to_s == network_interface['network']['id'] }
858
868
 
859
869
  if !selected_network
860
870
  print_red_alert "Network not found by id #{network_interface['network']['id']}!"
@@ -868,7 +878,9 @@ module Morpheus::Cli::ProvisioningHelper
868
878
  end
869
879
 
870
880
  # choose IP unless network has a pool configured
871
- if selected_network['pool']
881
+ if selected_network['id'].to_s.include?('networkGroup')
882
+ puts "IP Address: Using network group." if !no_prompt
883
+ elsif selected_network['pool']
872
884
  puts "IP Address: Using pool '#{selected_network['pool']['name']}'" if !no_prompt
873
885
  elsif selected_network['dhcpServer']
874
886
  puts "IP Address: Using DHCP" if !no_prompt
@@ -917,6 +929,73 @@ module Morpheus::Cli::ProvisioningHelper
917
929
  return evars
918
930
  end
919
931
 
932
+ # Prompts user for environment variables for new instance
933
+ # returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
934
+ def prompt_metadata(options={})
935
+ #puts "Configure Environment Variables:"
936
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
937
+ metadata_array = []
938
+ metadata_index = 0
939
+ has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
940
+ add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add a metadata tag?", {default: false}))
941
+ while add_another_metadata do
942
+ field_context = "metadata#{metadata_index}"
943
+ metadata = {}
944
+ metadata['id'] = nil
945
+ metadata_label = metadata_index == 0 ? "Metadata Tag" : "Metadata Tag [#{metadata_index+1}]"
946
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Name", 'required' => true, 'description' => 'Metadata Tag Name.', 'defaultValue' => metadata['name']}], options[:options])
947
+ # todo: metadata.type ?
948
+ metadata['name'] = v_prompt[field_context]['name']
949
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'value', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Value", 'required' => true, 'description' => 'Metadata Tag Value', 'defaultValue' => metadata['value']}], options[:options])
950
+ metadata['value'] = v_prompt[field_context]['value']
951
+ metadata_array << metadata
952
+ metadata_index += 1
953
+ has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
954
+ add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another metadata tag?", {default: false}))
955
+ end
956
+
957
+ return metadata_array
958
+ end
959
+
960
+ # Prompts user for load balancer settings
961
+ # returns Hash of parameters like {loadBalancerId: "-1", etc}
962
+ def prompt_instance_load_balancer(instance, default_lb_id, options)
963
+ #puts "Configure Environment Variables:"
964
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
965
+ payload = {}
966
+ api_params = {}
967
+ if instance['id']
968
+ api_params['instanceId'] = instance['id']
969
+ end
970
+ if instance['zone']
971
+ api_params['zoneId'] = instance['zone']['id']
972
+ elsif instance['cloud']
973
+ api_params['zoneId'] = instance['cloud']['id']
974
+ end
975
+ if instance['group']
976
+ api_params['siteId'] = instance['group']['id']
977
+ elsif instance['site']
978
+ api_params['siteId'] = instance['site']['id']
979
+ end
980
+ if instance['plan']
981
+ api_params['planId'] = instance['plan']['id']
982
+ end
983
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'loadBalancerId', 'type' => 'select', 'fieldLabel' => "Load Balancer", 'optionSource' => 'loadBalancers', 'required' => true, 'description' => 'Select Load Balancer for instance', 'defaultValue' => default_lb_id || ''}], options[:options], api_client, api_params)
984
+ lb_id = v_prompt['loadBalancerId']
985
+ payload['loadBalancerId'] = lb_id
986
+
987
+ # todo: finish implmenting this
988
+
989
+ # loadBalancerId
990
+ # loadBalancerProxyProtocol
991
+ # loadBalancerName
992
+ # loadBalancerDescription
993
+ # loadBalancerSslCert
994
+ # loadBalancerScheme
995
+
996
+ return payload
997
+ end
998
+
920
999
  # reject old volume option types
921
1000
  # these will eventually get removed from the associated optionTypes
922
1001
  def reject_volume_option_types(option_types)
@@ -8,7 +8,6 @@ class Morpheus::Cli::RecentActivityCommand
8
8
  include Morpheus::Cli::AccountsHelper
9
9
 
10
10
  set_command_name :'recent-activity'
11
- set_command_hidden # remove once this is done
12
11
 
13
12
  def initialize()
14
13
  # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
@@ -37,7 +36,7 @@ class Morpheus::Cli::RecentActivityCommand
37
36
  opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
38
37
  options[:end] = parse_time(val).iso8601
39
38
  end
40
- build_common_options(opts, options, [:account, :list, :json, :dry_run])
39
+ build_common_options(opts, options, [:account, :list, :json, :yaml, :csv, :fields, :dry_run, :remote])
41
40
  end
42
41
  optparse.parse!(args)
43
42
  connect(options)
@@ -54,22 +53,90 @@ class Morpheus::Cli::RecentActivityCommand
54
53
  end
55
54
  json_response = @dashboard_interface.recent_activity(account_id, params)
56
55
  if options[:json]
56
+ if options[:include_fields]
57
+ json_response = {"activity" => filter_data(json_response["activity"], options[:include_fields]) }
58
+ end
57
59
  print JSON.pretty_generate(json_response)
58
60
  print "\n"
59
- else
60
-
61
- # todo: impersonate command and show that info here
61
+ return 0
62
+ end
63
+ if options[:csv]
64
+ puts records_as_csv(json_response["activity"], options)
65
+ return 0
66
+ end
67
+ if options[:yaml]
68
+ if options[:include_fields]
69
+ json_response = {"activity" => filter_data(json_response["activity"], options[:include_fields]) }
70
+ end
71
+ puts as_yaml(json_response, options)
72
+ return 0
73
+ end
62
74
 
63
- print_h1 "Recent Activity"
64
- print cyan
65
- puts "Coming soon... see --json"
75
+ print_h1 "Recent Activity"
76
+ print cyan
77
+ items = json_response["activity"]
78
+ if items.empty?
79
+ puts "No activity found."
66
80
  print reset,"\n"
67
-
81
+ return 0
68
82
  end
83
+ # JD: this api response is funky, no meta and it includes date objects
84
+ items = items.select { |item| item['_id'] || item['name'] }
85
+ print_recent_activity_table(items, options)
86
+ print reset,"\n"
87
+ return 0
88
+
69
89
  rescue RestClient::Exception => e
70
90
  print_rest_exception(e, options)
71
- exit 1
91
+ return 1
92
+ end
93
+ end
94
+
95
+ def print_recent_activity_table(items, opts={})
96
+ columns = [
97
+ # {"ID" => lambda {|item| item['id'] } },
98
+ # {"SEVERITY" => lambda {|item| format_activity_severity(item['severity']) } },
99
+ {"TYPE" => lambda {|item| item['activityType'] } },
100
+ {"AUTHOR" => lambda {|item| item['userName'] || '' } },
101
+ {"MESSAGE" => lambda {|item| item['message'] || '' } },
102
+ # {"NAME" => lambda {|item| item['name'] } },
103
+ {"OBJECT" => lambda {|item| format_activity_display_object(item) } },
104
+ {"WHEN" => lambda {|item| format_local_dt(item['ts']) } }
105
+ # {"WHEN" => lambda {|item| "#{format_duration(item['ts'])} ago" } }
106
+ ]
107
+ if opts[:include_fields]
108
+ columns = opts[:include_fields]
109
+ end
110
+ print as_pretty_table(items, columns, opts)
111
+ end
112
+
113
+ def format_activity_severity(severity, return_color=cyan)
114
+ out = ""
115
+ status_string = severity
116
+ if status_string == 'critical'
117
+ out << "#{red}#{status_string.capitalize}#{return_color}"
118
+ elsif status_string == 'warning'
119
+ out << "#{yellow}#{status_string.capitalize}#{return_color}"
120
+ elsif status_string == 'info'
121
+ out << "#{cyan}#{status_string.capitalize}#{return_color}"
122
+ else
123
+ out << "#{cyan}#{status_string}#{return_color}"
124
+ end
125
+ out
126
+ end
127
+
128
+ def format_activity_display_object(item)
129
+ out = ""
130
+ if item['name']
131
+ out << item['name']
132
+ end
133
+ if item['objectType']
134
+ out << " (#{item['objectType']} #{item['objectId']})"
135
+ end
136
+ if item['deleted']
137
+ out << " [deleted]"
72
138
  end
139
+ out
73
140
  end
74
141
 
75
142
  end
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "2.11.1"
4
+ VERSION = "2.11.3"
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- require 'rest_client'
1
+ require 'rest-client'
2
2
 
3
3
  module Morpheus
4
4
  # A wrapper around rest_client so we can more easily deal with passing options (like turning on/off SSL verification)
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: 2.11.1
4
+ version: 2.11.3
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: 2017-04-24 00:00:00.000000000 Z
14
+ date: 2017-06-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler