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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/account_users_interface.rb +68 -0
- data/lib/morpheus/api/api_client.rb +51 -9
- data/lib/morpheus/api/audit_interface.rb +9 -0
- data/lib/morpheus/api/instances_interface.rb +21 -0
- data/lib/morpheus/api/load_balancer_monitors_interface.rb +9 -0
- data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
- data/lib/morpheus/api/load_balancer_profiles_interface.rb +4 -5
- data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +13 -4
- data/lib/morpheus/api/load_balancers_interface.rb +5 -0
- data/lib/morpheus/api/network_routers_interface.rb +9 -0
- data/lib/morpheus/api/network_static_routes_interface.rb +36 -0
- data/lib/morpheus/api/read_interface.rb +4 -3
- data/lib/morpheus/api/rest_interface.rb +3 -3
- data/lib/morpheus/api/secondary_read_interface.rb +1 -1
- data/lib/morpheus/api/secondary_rest_interface.rb +19 -19
- data/lib/morpheus/api/storage_server_types_interface.rb +14 -0
- data/lib/morpheus/api/storage_servers_interface.rb +9 -0
- data/lib/morpheus/api/storage_volume_types_interface.rb +9 -0
- data/lib/morpheus/api/storage_volumes_interface.rb +9 -0
- data/lib/morpheus/api/users_interface.rb +16 -63
- data/lib/morpheus/cli/cli_command.rb +253 -5
- data/lib/morpheus/cli/cli_registry.rb +1 -1
- data/lib/morpheus/cli/commands/alias_command.rb +1 -1
- data/lib/morpheus/cli/commands/apps.rb +14 -78
- data/lib/morpheus/cli/commands/audit.rb +188 -0
- data/lib/morpheus/cli/commands/blueprints_command.rb +1 -1
- data/lib/morpheus/cli/commands/change_password_command.rb +4 -4
- data/lib/morpheus/cli/commands/clusters.rb +37 -12
- data/lib/morpheus/cli/commands/hosts.rb +15 -15
- data/lib/morpheus/cli/commands/instances.rb +109 -2
- data/lib/morpheus/cli/commands/load_balancer_monitors.rb +71 -0
- data/lib/morpheus/cli/commands/load_balancer_pools.rb +30 -50
- data/lib/morpheus/cli/commands/load_balancer_profiles.rb +65 -0
- data/lib/morpheus/cli/commands/load_balancer_types.rb +9 -4
- data/lib/morpheus/cli/commands/load_balancer_virtual_servers.rb +77 -57
- data/lib/morpheus/cli/commands/load_balancers.rb +93 -6
- data/lib/morpheus/cli/commands/network_firewalls_command.rb +22 -5
- data/lib/morpheus/cli/commands/network_routers_command.rb +96 -45
- data/lib/morpheus/cli/commands/network_static_routes_command.rb +446 -0
- data/lib/morpheus/cli/commands/network_transport_zones_command.rb +4 -4
- data/lib/morpheus/cli/commands/open_command.rb +30 -0
- data/lib/morpheus/cli/commands/options.rb +98 -0
- data/lib/morpheus/cli/commands/policies_command.rb +1 -1
- data/lib/morpheus/cli/commands/prices_command.rb +7 -7
- data/lib/morpheus/cli/commands/remote.rb +4 -2
- data/lib/morpheus/cli/commands/roles.rb +1 -1
- data/lib/morpheus/cli/commands/shell.rb +2 -2
- data/lib/morpheus/cli/commands/storage_server_types.rb +50 -0
- data/lib/morpheus/cli/commands/storage_servers.rb +122 -0
- data/lib/morpheus/cli/commands/storage_volume_types.rb +50 -0
- data/lib/morpheus/cli/commands/storage_volumes.rb +103 -0
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_groups_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +2 -1
- data/lib/morpheus/cli/commands/user_sources_command.rb +1 -1
- data/lib/morpheus/cli/commands/users.rb +28 -28
- data/lib/morpheus/cli/commands/view.rb +102 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -5
- data/lib/morpheus/cli/mixins/load_balancers_helper.rb +24 -4
- data/lib/morpheus/cli/mixins/print_helper.rb +50 -18
- data/lib/morpheus/cli/mixins/processes_helper.rb +1 -2
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +15 -5
- data/lib/morpheus/cli/mixins/rest_command.rb +145 -73
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +174 -81
- data/lib/morpheus/cli/mixins/storage_servers_helper.rb +156 -0
- data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +119 -0
- data/lib/morpheus/cli/option_types.rb +45 -24
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/ext/string.rb +29 -6
- data/lib/morpheus/routes.rb +238 -0
- data/lib/morpheus/util.rb +6 -1
- 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
|
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
|
-
|
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)}
|
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)}
|
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
|
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 :
|
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
|
-
@
|
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
|
-
|
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
|
-
|
923
|
-
|
924
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
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
|
-
@
|
39
|
+
@account_users_interface = @api_client.account_users
|
40
40
|
@library_layouts_interface = @api_client.library_layouts
|
41
41
|
end
|
42
42
|
|