morpheus-cli 5.4.0 → 5.4.1

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/account_users_interface.rb +68 -0
  4. data/lib/morpheus/api/api_client.rb +51 -9
  5. data/lib/morpheus/api/audit_interface.rb +9 -0
  6. data/lib/morpheus/api/instances_interface.rb +21 -0
  7. data/lib/morpheus/api/load_balancer_monitors_interface.rb +9 -0
  8. data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
  9. data/lib/morpheus/api/load_balancer_profiles_interface.rb +4 -5
  10. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +13 -4
  11. data/lib/morpheus/api/load_balancers_interface.rb +5 -0
  12. data/lib/morpheus/api/network_routers_interface.rb +9 -0
  13. data/lib/morpheus/api/network_static_routes_interface.rb +36 -0
  14. data/lib/morpheus/api/read_interface.rb +4 -3
  15. data/lib/morpheus/api/rest_interface.rb +3 -3
  16. data/lib/morpheus/api/secondary_read_interface.rb +1 -1
  17. data/lib/morpheus/api/secondary_rest_interface.rb +19 -19
  18. data/lib/morpheus/api/storage_server_types_interface.rb +14 -0
  19. data/lib/morpheus/api/storage_servers_interface.rb +9 -0
  20. data/lib/morpheus/api/storage_volume_types_interface.rb +9 -0
  21. data/lib/morpheus/api/storage_volumes_interface.rb +9 -0
  22. data/lib/morpheus/api/users_interface.rb +16 -63
  23. data/lib/morpheus/cli/cli_command.rb +253 -5
  24. data/lib/morpheus/cli/cli_registry.rb +1 -1
  25. data/lib/morpheus/cli/commands/alias_command.rb +1 -1
  26. data/lib/morpheus/cli/commands/apps.rb +14 -78
  27. data/lib/morpheus/cli/commands/audit.rb +188 -0
  28. data/lib/morpheus/cli/commands/blueprints_command.rb +1 -1
  29. data/lib/morpheus/cli/commands/change_password_command.rb +4 -4
  30. data/lib/morpheus/cli/commands/clusters.rb +37 -12
  31. data/lib/morpheus/cli/commands/hosts.rb +15 -15
  32. data/lib/morpheus/cli/commands/instances.rb +109 -2
  33. data/lib/morpheus/cli/commands/load_balancer_monitors.rb +71 -0
  34. data/lib/morpheus/cli/commands/load_balancer_pools.rb +30 -50
  35. data/lib/morpheus/cli/commands/load_balancer_profiles.rb +65 -0
  36. data/lib/morpheus/cli/commands/load_balancer_types.rb +9 -4
  37. data/lib/morpheus/cli/commands/load_balancer_virtual_servers.rb +77 -57
  38. data/lib/morpheus/cli/commands/load_balancers.rb +93 -6
  39. data/lib/morpheus/cli/commands/network_firewalls_command.rb +22 -5
  40. data/lib/morpheus/cli/commands/network_routers_command.rb +96 -45
  41. data/lib/morpheus/cli/commands/network_static_routes_command.rb +446 -0
  42. data/lib/morpheus/cli/commands/network_transport_zones_command.rb +4 -4
  43. data/lib/morpheus/cli/commands/open_command.rb +30 -0
  44. data/lib/morpheus/cli/commands/options.rb +98 -0
  45. data/lib/morpheus/cli/commands/policies_command.rb +1 -1
  46. data/lib/morpheus/cli/commands/prices_command.rb +7 -7
  47. data/lib/morpheus/cli/commands/remote.rb +4 -2
  48. data/lib/morpheus/cli/commands/roles.rb +1 -1
  49. data/lib/morpheus/cli/commands/shell.rb +2 -2
  50. data/lib/morpheus/cli/commands/storage_server_types.rb +50 -0
  51. data/lib/morpheus/cli/commands/storage_servers.rb +122 -0
  52. data/lib/morpheus/cli/commands/storage_volume_types.rb +50 -0
  53. data/lib/morpheus/cli/commands/storage_volumes.rb +103 -0
  54. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  55. data/lib/morpheus/cli/commands/user_groups_command.rb +1 -1
  56. data/lib/morpheus/cli/commands/user_settings_command.rb +2 -1
  57. data/lib/morpheus/cli/commands/user_sources_command.rb +1 -1
  58. data/lib/morpheus/cli/commands/users.rb +28 -28
  59. data/lib/morpheus/cli/commands/view.rb +102 -0
  60. data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -5
  61. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +24 -4
  62. data/lib/morpheus/cli/mixins/print_helper.rb +50 -18
  63. data/lib/morpheus/cli/mixins/processes_helper.rb +1 -2
  64. data/lib/morpheus/cli/mixins/provisioning_helper.rb +15 -5
  65. data/lib/morpheus/cli/mixins/rest_command.rb +145 -73
  66. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +174 -81
  67. data/lib/morpheus/cli/mixins/storage_servers_helper.rb +156 -0
  68. data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +119 -0
  69. data/lib/morpheus/cli/option_types.rb +45 -24
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli.rb +1 -0
  72. data/lib/morpheus/ext/string.rb +29 -6
  73. data/lib/morpheus/routes.rb +238 -0
  74. data/lib/morpheus/util.rb +6 -1
  75. metadata +29 -8
@@ -1011,7 +1011,7 @@ module Morpheus
1011
1011
  # meh, could deprecate and make subcommand define handle() itself
1012
1012
  # if args.count == 0 && default_subcommand
1013
1013
  # # p "using default subcommand #{default_subcommand}"
1014
- # return self.send(default_subcommand, args || [])
1014
+ # return send(default_subcommand, args || [])
1015
1015
  # end
1016
1016
  subcommand_name = args[0]
1017
1017
  if args.empty?
@@ -1032,7 +1032,7 @@ module Morpheus
1032
1032
  error_msg = "'#{command_name} #{subcommand_name}' is not a #{prog_name} command.\n#{full_command_usage}"
1033
1033
  raise CommandNotFoundError.new(error_msg)
1034
1034
  end
1035
- self.send(cmd_method, args[1..-1])
1035
+ send(cmd_method, args[1..-1])
1036
1036
  end
1037
1037
 
1038
1038
  def handle(args)
@@ -1230,6 +1230,72 @@ module Morpheus
1230
1230
  true
1231
1231
  end
1232
1232
 
1233
+ # The default way to build options for the list command
1234
+ # @param [OptionParser] opts
1235
+ # @param [Hash] options
1236
+ # @param [Hash] params
1237
+ def build_list_options(opts, options, params)
1238
+ build_standard_list_options(opts, options)
1239
+ end
1240
+
1241
+ # The default way to parse options for the list command
1242
+ # @param [Array] args
1243
+ # @param [Hash] options
1244
+ # @param [Hash] params
1245
+ def parse_list_options!(args, options, params)
1246
+ if args.count > 0
1247
+ options[:phrase] = args.join(" ")
1248
+ # params['phrase'] = = args.join(" ")
1249
+ end
1250
+ params.merge!(parse_list_options(options))
1251
+ end
1252
+
1253
+ # The default way to build options for the list command
1254
+ # @param [OptionParser] opts
1255
+ # @param [Hash] options
1256
+ # @param [Hash] params
1257
+ def build_get_options(opts, options, params)
1258
+ build_standard_get_options(opts, options)
1259
+ end
1260
+
1261
+ # The default way to parse options for the get command
1262
+ # @param [OptionParser] opts
1263
+ # @param [Hash] options
1264
+ # @param [Hash] params
1265
+ def parse_get_options!(args, options, params)
1266
+ params.merge!(parse_query_options(options))
1267
+ end
1268
+
1269
+ # The default way to parse options for the get command
1270
+ # @param type [string]
1271
+ # @param options [Hash] The command options
1272
+ # @param params [Hash] The query parameters the output is being appended to
1273
+ # @param param_name [String]
1274
+ # @param lookup_ids [Boolean] Also lookup ids to make sure they exist or else error
1275
+ # @return
1276
+ def parse_parameter_as_resource_id!(type, options, params, param_name=nil, lookup_ids=false)
1277
+ # type = type.to_s.singularize
1278
+ if options.key?(type)
1279
+ val = options[type].to_s
1280
+ param_name ||= "#{type.to_s.camelcase}Id"
1281
+ if val
1282
+ if val.to_s !~ /\A\d{1,}\Z/ || lookup_ids
1283
+ record = find_by_name(type, val)
1284
+ if record.nil?
1285
+ # avoid double error render by exiting here, ew
1286
+ exit 1
1287
+ raise_command_error "Storage Server not found for '#{val}'"
1288
+ end
1289
+ params[param_name] = record['id']
1290
+ else
1291
+ params[param_name] = val
1292
+ end
1293
+ return params[param_name]
1294
+ end
1295
+ end
1296
+ return nil
1297
+ end
1298
+
1233
1299
  # parse the parameters provided by the common :list options
1234
1300
  # this includes the :query options too via parse_query_options().
1235
1301
  # returns Hash of params the format {"phrase": => "foobar", "max": 100}
@@ -1244,7 +1310,11 @@ module Morpheus
1244
1310
  end
1245
1311
  # arbitrary filters
1246
1312
  list_params.merge!(parse_query_options(options))
1247
-
1313
+ # ok, any string keys in options can become query parameters, eg. options['name'] = 'foobar'
1314
+ # do it!
1315
+ # options.each do |k, v|
1316
+ # list_params[k] = v
1317
+ # end
1248
1318
  return list_params
1249
1319
  end
1250
1320
 
@@ -1365,7 +1435,7 @@ module Morpheus
1365
1435
  full_outfile = File.expand_path(options[:outfile])
1366
1436
  if output
1367
1437
  print_to_file(output, options[:outfile], options[:overwrite])
1368
- print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(full_outfile)} B)\n" unless options[:quiet]
1438
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{format_bytes(File.size(full_outfile))})\n" unless options[:quiet]
1369
1439
  else
1370
1440
  # uhhh ok lets try this
1371
1441
  Morpheus::Logging::DarkPrinter.puts "using experimental feature: --out without a common format like json, yml or csv" if Morpheus::Logging.debug?
@@ -1373,7 +1443,7 @@ module Morpheus
1373
1443
  if result && result != 0
1374
1444
  return result
1375
1445
  end
1376
- print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(full_outfile)} B)\n" unless options[:quiet]
1446
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{format_bytes(File.size(full_outfile))})\n" unless options[:quiet]
1377
1447
  return 0, nil
1378
1448
  end
1379
1449
  else
@@ -1404,6 +1474,184 @@ module Morpheus
1404
1474
 
1405
1475
  alias :render_with_format :render_response
1406
1476
 
1477
+ # Dynamic find methods to load a record of any type
1478
+ # def find_by_name_or_id(type, val)
1479
+ # interface = instance_variable_get "@#{type}s_interface"
1480
+ # typeCamelCase = type.gsub(/(?:^|_)([a-z])/) do $1.upcase end
1481
+ # typeCamelCase = typeCamelCase[0, 1].downcase + typeCamelCase[1..-1]
1482
+ # (val.to_s =~ /\A\d{1,}\Z/) ? interface.get(val.to_i)[typeCamelCase] : interface.list({'name' => val})["#{typeCamelCase}s"].first
1483
+ # end
1484
+
1485
+ # Find a resource by type and name or id
1486
+ # @param type [String of Symbol] Type of resource formatted as singular, lowerscore with underscores.
1487
+ # @param *id [String or Numeric] ID of resource, multiple arguments may be passed when using a secondary interface where parent_id, id is required.
1488
+ # Example: find_by_name_or_id("instance", "K2")
1489
+ # find_by_name_or_id("storage_volume", 42)
1490
+ # find_by_name_or_id("instance", "My Instance")
1491
+ # find_by_name_or_id("load_balancer_pool", load_balancer_id, id)
1492
+ def find_by_name_or_id(*args)
1493
+ val = args.last
1494
+ if val.to_s =~ /\A\d{1,}\Z/
1495
+ return find_by_id(*args)
1496
+ else
1497
+ return find_by_name(*args)
1498
+ end
1499
+ end
1500
+
1501
+
1502
+ # Find a resource by type and id
1503
+ # Usage: find_by_name_or_id("app", 3)
1504
+ def find_by_id(*args)
1505
+ #Morpheus::Logging::DarkPrinter.puts "find_by_id(#{args.join(', ')})" if Morpheus::Logging.debug?
1506
+ # type, ids = args.first, args[1..-1]
1507
+ type, *ids = args
1508
+ type = type.to_s.singularize.underscore
1509
+ # still relying on the command or helper to define these _label and _key methods
1510
+ label = send("#{type}_label")
1511
+ object_key = send("#{type}_object_key")
1512
+ interface_name = "@#{type.pluralize}_interface"
1513
+ interface = instance_variable_get(interface_name)
1514
+ if interface.nil?
1515
+ raise "#{self.class} has not defined interface #{interface_name}"
1516
+ end
1517
+ begin
1518
+ json_response = interface.get(*ids)
1519
+ return json_response[object_key]
1520
+ rescue RestClient::Exception => e
1521
+ if e.response && e.response.code == 404
1522
+ print_red_alert "#{label} not found by id #{ids.last}"
1523
+ return nil
1524
+ else
1525
+ raise e
1526
+ end
1527
+ end
1528
+ end
1529
+
1530
+ # Find a record by type and name
1531
+ # Usage: find_by_name_or_id("network", "Skynet")
1532
+ def find_by_name(*args)
1533
+ #Morpheus::Logging::DarkPrinter.puts "find_by_name(#{args.join(', ')})" if Morpheus::Logging.debug?
1534
+ # type, ids = args.first, args[1..-1]
1535
+ type, *ids = args
1536
+ type = type.to_s.singularize.underscore
1537
+ val = ids.pop
1538
+ params = {}
1539
+ name_property = 'name'
1540
+ if type == 'user'
1541
+ name_property = 'username'
1542
+ params['global'] = 'true'
1543
+ end
1544
+ params[name_property] = val.to_s
1545
+ request_args = ids + [params]
1546
+ request_args.unshift(type)
1547
+ records = find_all(*request_args)
1548
+ # still relying on the command or helper to define these _label and _key methods
1549
+ label = respond_to?("#{type}_label", true) ? send("#{type}_label") : type.titleize
1550
+ if records.empty?
1551
+ print_red_alert "#{label} not found by name '#{val}'"
1552
+ return nil
1553
+ elsif records.size > 1
1554
+ print_red_alert "More than one #{label.downcase} found by #{name_property} '#{val}'"
1555
+ print_error "\n"
1556
+ if type == "user"
1557
+ puts_error as_pretty_table(records, [:id, :username, {"FIRST NAME" => "firstName"}, {"LAST NAME" => "lastName"}, {"TENANT" => lambda {|it| it['account']['name'] rescue ''}}], {color:red})
1558
+ else
1559
+ puts_error as_pretty_table(records, [:id, :name], {color:red})
1560
+ end
1561
+ print_red_alert "Try using ID instead"
1562
+ print_error reset,"\n"
1563
+ return nil
1564
+ else
1565
+ return records[0]
1566
+ end
1567
+ end
1568
+
1569
+ # Load a list of records by type
1570
+ # @example Find an app by id
1571
+ # find_record(:app, 1)
1572
+ # report_types = find_all("reportTypes", {phrase:"amazon"})
1573
+ # pools = find_all("loadBalancerPool", load_balancer_id)
1574
+ # @return Array of records
1575
+ def find_all(*args)
1576
+ #Morpheus::Logging::DarkPrinter.puts "find_all(#{args.join(', ')})" if Morpheus::Logging.debug?
1577
+ type, *request_args = args
1578
+ type = type.to_s.singularize.underscore
1579
+ list_key = respond_to?("#{type}_list_key", true) ? send("#{type}_list_key") : type.camelcase.pluralize
1580
+ json_response = find_all_json(*args)
1581
+ if !json_response.key?(list_key)
1582
+ # maybe just use the first key like this:
1583
+ # list_key = json_response.keys.find { |k| json_response[k].is_a?(Array) }
1584
+ # print_error(json_response) if Morpheus::Logging.debug?
1585
+ raise "API response is missing list property '#{list_key}'"
1586
+ end
1587
+ return json_response[list_key]
1588
+ end
1589
+
1590
+ # Load json response for a list of records by type
1591
+ # @return Hash of JSON data
1592
+ def find_all_json(*args)
1593
+ type, *request_args = args
1594
+ get_interface(type).list(*request_args)
1595
+ end
1596
+
1597
+ alias :find_all_records :find_all
1598
+ alias :find_all_records_json :find_all_json
1599
+
1600
+ # Load a single record (Hash) by type and id and optional parameters
1601
+ # Examples:
1602
+ # apps = find_record(:app)
1603
+ # report_types = find_record("reportTypes", {phrase:"amazon"})
1604
+ # pools = find_record("loadBalancerPool", balancer_id)
1605
+ # @return [Hash] of the object that was found or raises an exception if 404 not found encountered.
1606
+ def find_record(*args)
1607
+ #Morpheus::Logging::DarkPrinter.puts "find_record(#{args.join(', ')})" if Morpheus::Logging.debug?
1608
+ type, *request_args = args
1609
+ type = type.to_s.singularize.underscore
1610
+ object_key = respond_to?("#{type}_object_key", true) ? send("#{type}_object_key") : type.camelcase.singularize
1611
+ json_response = find_record_json(*args)
1612
+ if !json_response.key?(object_key)
1613
+ # maybe just use the first key like this:
1614
+ # object_key = json_response.keys.find { |k| json_response[k].is_a?(Hash) }
1615
+ # print_error(json_response) if Morpheus::Logging.debug?
1616
+ raise "API response is missing object property '#{object_key}'"
1617
+ end
1618
+ return json_response[object_key]
1619
+ end
1620
+
1621
+ # Load list of records by type and (optional) parameters
1622
+ # Examples:
1623
+ # apps = find_record(:app, 1)
1624
+ # report_types = find_record("reportType", 1)
1625
+ # pools = find_all("loadBalancerPool", load_balancer_id)
1626
+ def find_record_json(*args)
1627
+ #Morpheus::Logging::DarkPrinter.puts "find_record_json(#{args.join(', ')})" if Morpheus::Logging.debug?
1628
+ type, *request_args = args
1629
+ get_interface(type).get(*request_args)
1630
+ end
1631
+
1632
+ # Load json response for a list of records by type
1633
+ def get_interface(type)
1634
+ #Morpheus::Logging::DarkPrinter.puts "get_interface(#{type})" if Morpheus::Logging.debug?
1635
+ # todo: can probably just do @api_client ? @api_client.interface(interface_name) : nil
1636
+ type = type.to_s.singularize.underscore
1637
+ interface_name = "@#{type.pluralize}_interface"
1638
+ interface = nil
1639
+ if instance_variable_defined?(interface_name)
1640
+ interface = instance_variable_get(interface_name)
1641
+ if interface.nil?
1642
+ # Fix is to update connect() to do @apps_interface = @api_client.apps
1643
+ raise "API Interface #{interface_name} is nil"
1644
+ end
1645
+ else
1646
+ if @api_client.is_a?(Morpheus::APIClient)
1647
+ interface = @api_client.interface(type.pluralize)
1648
+ else
1649
+ raise "#{self.class} has not initalized @api_client"
1650
+ end
1651
+ end
1652
+ return interface
1653
+ end
1654
+
1407
1655
  module ClassMethods
1408
1656
 
1409
1657
  def prog_name
@@ -298,7 +298,7 @@ module Morpheus
298
298
  every_command = cached_command_list
299
299
  guess = command_name
300
300
  suggestions = []
301
- while guess.size >= 3
301
+ while suggestions.empty? && guess.size >= 3
302
302
  plural_guess = guess.pluralize
303
303
  if every_command.include?(guess)
304
304
  suggestions << guess
@@ -270,7 +270,7 @@ EOT
270
270
  out << "\n"
271
271
  out << cyan
272
272
  out << as_pretty_table(my_aliases, alias_columns, {:border_style => :thin}.merge(options))
273
- out << format_results_pagination({size:my_aliases.size,total:my_aliases.size.to_i})
273
+ out << format_results_pagination({'size' => my_aliases.size, 'total' => my_aliases.size.to_i})
274
274
  out << reset
275
275
  out << "\n"
276
276
  end
@@ -10,9 +10,7 @@ class Morpheus::Cli::Apps
10
10
  set_command_name :apps
11
11
  set_command_description "View and manage apps."
12
12
  register_subcommands :list, :count, :get, :view, :add, :update, :remove, :cancel_removal, :add_instance, :remove_instance, :logs, :security_groups, :apply_security_groups, :history
13
- register_subcommands :'prepare-apply' => :prepare_apply
14
- register_subcommands :apply
15
- register_subcommands :refresh
13
+ register_subcommands :refresh, :apply
16
14
  register_subcommands :stop, :start, :restart
17
15
  register_subcommands :wiki, :update_wiki
18
16
  #register_subcommands :firewall_disable, :firewall_enable
@@ -27,7 +25,7 @@ class Morpheus::Cli::Apps
27
25
  def connect(opts)
28
26
  @api_client = establish_remote_appliance_connection(opts)
29
27
  @accounts_interface = @api_client.accounts
30
- @users_interface = @api_client.users
28
+ @account_users_interface = @api_client.account_users
31
29
  @apps_interface = @api_client.apps
32
30
  @blueprints_interface = @api_client.blueprints
33
31
  @instance_types_interface = @api_client.instance_types
@@ -892,9 +890,7 @@ This is only supported by certain types of apps.
892
890
  EOT
893
891
  end
894
892
  optparse.parse!(args)
895
- if args.count != 1
896
- raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
897
- end
893
+ verify_args!(args:args, optparse:optparse, count:1)
898
894
  connect(options)
899
895
 
900
896
  begin
@@ -919,65 +915,11 @@ EOT
919
915
  return
920
916
  end
921
917
  json_response = @apps_interface.refresh(app["id"], params, payload)
922
- render_result = render_with_format(json_response, options)
923
- return 0 if render_result
924
- print_green_success "Refreshed app #{app['name']}"
925
- return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
926
- rescue RestClient::Exception => e
927
- print_rest_exception(e, options)
928
- exit 1
929
- end
930
- end
931
-
932
- def prepare_apply(args)
933
- params, payload, options = {}, {}, {}
934
- optparse = Morpheus::Cli::OptionParser.new do |opts|
935
- opts.banner = subcommand_usage("[app] [options]")
936
- build_standard_update_options(opts, options, [:auto_confirm])
937
- opts.footer = <<-EOT
938
- Prepare to apply an app.
939
- [app] is required. This is the name or id of an app.
940
- Template parameter values can be applied with -O templateParameter.foo=bar
941
- This only prints the app configuration that would be applied.
942
- It does not make any updates.
943
- This is only supported by certain types of apps.
944
- EOT
945
- end
946
- optparse.parse!(args)
947
- if args.count != 1
948
- raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
949
- end
950
- connect(options)
951
-
952
- begin
953
- app = find_app_by_name_or_id(args[0])
954
- return 1 if app.nil?
955
- # construct request
956
- params.merge!(parse_query_options(options))
957
- payload = {}
958
- if options[:payload]
959
- payload = options[:payload]
960
- payload.deep_merge!(parse_passed_options(options))
961
- else
962
- payload.deep_merge!(parse_passed_options(options))
963
- # raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
918
+ render_response(json_response, options) do
919
+ print_green_success "Refreshing app #{app['name']}"
920
+ # return _get(app['id'], options)
964
921
  end
965
- @apps_interface.setopts(options)
966
- if options[:dry_run]
967
- print_dry_run @apps_interface.dry.prepare_apply(app["id"], params, payload)
968
- return
969
- end
970
- json_response = @apps_interface.prepare_apply(app["id"], params, payload)
971
- render_result = render_with_format(json_response, options)
972
- return 0 if render_result
973
- # print_green_success "Prepared to apply app: #{app['name']}"
974
- print_h1 "Prepared App: #{app['name']}"
975
- app_config = json_response['data']
976
- # app_config = json_response if app_config.nil?
977
- puts as_yaml(app_config, options)
978
- #return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
979
- print "\n", reset
980
- return 0
922
+ return 0, nil
981
923
  rescue RestClient::Exception => e
982
924
  print_rest_exception(e, options)
983
925
  exit 1
@@ -992,17 +934,11 @@ EOT
992
934
  opts.footer = <<-EOT
993
935
  Apply an app.
994
936
  [app] is required. This is the name or id of an app.
995
- Template parameter values can be applied with -O templateParameter.foo=bar
996
- This is a way to apply an app with new configuration parameters to an app.
997
- This prints the app configuration that would be applied.
998
- It does not make any updates.
999
- This is only supported by certain types of apps.
937
+ This is only supported by certain types of apps such as terraform.
1000
938
  EOT
1001
939
  end
1002
940
  optparse.parse!(args)
1003
- if args.count != 1
1004
- raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
1005
- end
941
+ verify_args!(args:args, optparse:optparse, count:1)
1006
942
  connect(options)
1007
943
 
1008
944
  begin
@@ -1027,11 +963,11 @@ EOT
1027
963
  return
1028
964
  end
1029
965
  json_response = @apps_interface.apply(app["id"], params, payload)
1030
- render_result = render_with_format(json_response, options)
1031
- return 0 if render_result
1032
- print_green_success "Applied app #{app['name']}"
1033
- #return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
1034
- return 0
966
+ render_response(json_response, options) do
967
+ print_green_success "Applying app #{app['name']}"
968
+ # return _get(app['id'], options)
969
+ end
970
+ return 0, nil
1035
971
  rescue RestClient::Exception => e
1036
972
  print_rest_exception(e, options)
1037
973
  exit 1
@@ -0,0 +1,188 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::Audit
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::LogsHelper
6
+ include Morpheus::Cli::RestCommand
7
+ include Morpheus::Cli::OptionSourceHelper
8
+
9
+ set_command_description "View audit log records."
10
+ set_command_name :'audit'
11
+ register_subcommands :list, :get
12
+
13
+ # audit is not published yet
14
+ set_command_hidden
15
+
16
+ # RestCommand settings
17
+
18
+ # interfaces
19
+ register_interfaces :audit
20
+ set_rest_interface_name :audit
21
+
22
+ # resource name is "Audit Log"
23
+ set_rest_name :audit_log
24
+
25
+ # display argument as [id] instead of [audit log]
26
+ set_rest_has_name false
27
+ set_rest_arg "id"
28
+
29
+ # def connect(opts)
30
+ # @api_client = establish_remote_appliance_connection(opts)
31
+ # @audit_interface = @api_client.audit # @api_client.rest("audit")
32
+ # end
33
+
34
+ # def handle(args)
35
+ # handle_subcommand(args)
36
+ # end
37
+
38
+ def list(args)
39
+ options = {}
40
+ params = {}
41
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
42
+ opts.banner = subcommand_usage("[search]")
43
+ opts.on('--user USER', String, "Filter by User Username or ID") do |val|
44
+ params['user'] = params['user'] ? [params['user'], val].flatten : [val]
45
+ end
46
+ opts.on('--level VALUE', String, "Log Level. DEBUG|INFO|WARN|ERROR") do |val|
47
+ params['level'] = params['level'] ? [params['level'], val].flatten : [val]
48
+ end
49
+ opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start date timestamp in standard iso8601 format.") do |val|
50
+ params['startDate'] = val # parse_time(val).utc.iso8601
51
+ end
52
+ opts.on('--end TIMESTAMP','--end TIMESTAMP', "End date timestamp in standard iso8601 format.") do |val|
53
+ params['endDate'] = val # parse_time(val).utc.iso8601
54
+ end
55
+ build_standard_list_options(opts, options)
56
+ opts.footer = "List audit logs records."
57
+ end
58
+ optparse.parse!(args)
59
+ if args.count > 0
60
+ options[:phrase] = args.join(" ")
61
+ end
62
+ connect(options)
63
+ params.merge!(parse_list_options(options))
64
+ # parse --user id,name
65
+ if params['user']
66
+ user_ids = parse_user_id_list(params['user'])
67
+ return 1 if user_ids.nil?
68
+ params['user'] = user_ids
69
+ end
70
+ # api works with level=INFO|WARN
71
+ if params['level']
72
+ params['level'] = [params['level']].flatten.collect {|it| it.to_s.upcase }.join('|')
73
+ end
74
+ # could find_by_name_or_id for params['servers'] and params['containers']
75
+ @audit_interface.setopts(options)
76
+ if options[:dry_run]
77
+ print_dry_run @audit_interface.dry.list(params)
78
+ return
79
+ end
80
+ json_response = @audit_interface.list(params)
81
+
82
+ render_response(json_response, options, rest_list_key) do
83
+ records = json_response[rest_list_key]
84
+ print_h1 "Morpheus Audit Log", parse_list_subtitles(options), options
85
+ if records.nil? || records.empty?
86
+ print cyan,"No #{rest_label_plural.downcase} found.",reset,"\n"
87
+ else
88
+ print as_pretty_table(records, rest_list_column_definitions(options).upcase_keys!, options)
89
+ print_results_pagination(json_response) if json_response['meta']
90
+ end
91
+ print reset,"\n"
92
+ end
93
+ return 0, nil
94
+ end
95
+
96
+ protected
97
+
98
+ # custom rendering to print Message below description list
99
+ def render_response_for_get(json_response, options)
100
+ render_response(json_response, options, rest_object_key) do
101
+ record = json_response[rest_object_key]
102
+ print_h1 rest_label, [], options
103
+ print cyan
104
+ print_description_list(rest_column_definitions(options), record, options)
105
+ # show log message settings...
106
+ print_h2 "Message", options
107
+ print cyan
108
+ puts record['message']
109
+ print reset,"\n"
110
+ end
111
+ end
112
+
113
+ def audit_log_object_key
114
+ "auditLog"
115
+ end
116
+
117
+ def audit_log_list_key
118
+ "auditLogs"
119
+ end
120
+
121
+ def audit_log_list_column_definitions(options={})
122
+ {
123
+ "ID" => 'id',
124
+ "Level" => lambda {|it| format_log_level(it['level']) },
125
+ "Message" => {display_method:'message', max_width: (options[:wrap] ? nil : 75)},
126
+ "Event Type" => 'eventType',
127
+ "Object" => lambda {|it| "#{it['objectClass']} #{it['objectId']}".strip },
128
+ # "Object Type" => 'objectClass',
129
+ # "Object ID" => 'objectId',
130
+ "User" => lambda {|it|
131
+ if it['actualUser'] && it['user'] && it['actualUser']['username'] != it['user']['username']
132
+ it['user']['username'] + '(' + it['actualUser']['username'].to_s + ')'
133
+ elsif it['user']
134
+ it['user']['username']
135
+ else
136
+ # system or deleted user maybe?
137
+ end
138
+ },
139
+ # "Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
140
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
141
+ }
142
+ end
143
+
144
+ def audit_log_column_definitions(options={})
145
+ {
146
+ "ID" => 'id',
147
+ "Level" => lambda {|it| format_log_level(it['level']) },
148
+ #"Message" => 'message',
149
+ "Event Type" => 'eventType',
150
+ "Object Type" => 'objectClass',
151
+ "Object ID" => 'objectId',
152
+ "User" => lambda {|it|
153
+ if it['actualUser'] && it['user'] && it['actualUser']['username'] != it['user']['username']
154
+ it['user']['username'] + '(' + it['actualUser']['username'].to_s + ')'
155
+ elsif it['user']
156
+ it['user']['username']
157
+ else
158
+ # system or deleted user maybe?
159
+ end
160
+ },
161
+ # "Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
162
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
163
+ }
164
+ end
165
+
166
+ def find_audit_log_by_name_or_id(val)
167
+ return find_audit_log_by_id(val)
168
+ end
169
+
170
+ def find_audit_log_by_id(id)
171
+ begin
172
+ json_response = @audit_interface.get(id)
173
+ return json_response[audit_log_object_key]
174
+ rescue RestClient::Exception => e
175
+ if e.response && e.response.code == 404
176
+ print_red_alert "Audit Log not found by id #{id}"
177
+ return nil
178
+ else
179
+ raise e
180
+ end
181
+ end
182
+ end
183
+
184
+ def find_audit_log_by_name(name)
185
+ raise_command_error "finding audit log by name not supported"
186
+ end
187
+
188
+ end
@@ -36,7 +36,7 @@ class Morpheus::Cli::BlueprintsCommand
36
36
  @options_interface = @api_client.options
37
37
  @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
38
38
  @clouds_interface = @api_client.clouds
39
- @users_interface = @api_client.users
39
+ @account_users_interface = @api_client.account_users
40
40
  @library_layouts_interface = @api_client.library_layouts
41
41
  end
42
42