morpheus-cli 5.4.0 → 5.4.1

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