module Morpheus::Cli::SecondaryRestCommand
SecondaryRestCommand
is a mixin for Morpheus::Cli
command classes. for resources that are secondary to some parent resource. Provides basic CRUD commands: list, get, add, update, remove The parent resource is specified as the first argument for all the comments.
Example of a SecondaryRestCommand
for ‘morpheus load-balancer-virtual-servers`.
class Morpheus::Cli::LoadBalancerVirtualServers
include Morpheus::Cli::CliCommand include Morpheus::Cli::RestCommand include Morpheus::Cli::SecondaryRestCommand include Morpheus::Cli::LoadBalancersHelper set_command_name :'load-balancer-virtual-servers' register_subcommands :list, :get, :add, :update, :remove register_interfaces :load_balancer_virtual_servers, :load_balancers, :load_balancer_types set_rest_parent_name :load_balancers
end
Public Class Methods
included(base)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 26 def self.included(base) base.extend ClassMethods end
Public Instance Methods
_get(parent_id, id, params, options)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 324 def _get(parent_id, id, params, options) parent_record = rest_parent_find_by_name_or_id(parent_id) if parent_record.nil? return 1, "#{rest_parent_label} not found for '#{parent_id}" end parent_id = parent_record['id'] if id !~ /\A\d{1,}\Z/ record = rest_find_by_name_or_id(parent_id, id) if record.nil? return 1, "#{rest_label} not found for '#{id}" end id = record['id'] end rest_interface.setopts(options) if options[:dry_run] print_dry_run rest_interface.dry.get(parent_id, id, params) return end json_response = rest_interface.get(parent_id, id, params) render_response_for_get(json_response, options.merge({:parent_record => parent_record})) return 0, nil end
add(args)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 367 def add(args) parent_id, parent_record = nil, nil record_type_id = nil options = {:options => {:context_map => rest_option_context_map}} option_types = respond_to?("add_#{rest_key}_option_types", true) ? send("add_#{rest_key}_option_types") : [] advanced_option_types = respond_to?("add_#{rest_key}_advanced_option_types", true) ? send("add_#{rest_key}_advanced_option_types") : [] type_option_type = option_types.find {|it| it['fieldName'] == 'type'} optparse = Morpheus::Cli::OptionParser.new do |opts| if rest_has_name opts.banner = subcommand_usage("[#{rest_parent_arg}] [name]") else opts.banner = subcommand_usage("[#{rest_parent_arg}]") end if rest_has_type && type_option_type.nil? opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val| record_type_id = val end end build_option_type_options(opts, options, option_types) build_option_type_options(opts, options, advanced_option_types) build_standard_add_options(opts, options) opts.footer = <<-EOT Create a new #{rest_label.downcase}. [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}. [name] is required. This is the name of the new #{rest_label.downcase}. EOT end optparse.parse!(args) verify_args!(args:args, optparse:optparse, min:1, max: 2) # todo: make supporting args[0] optional and more flexible # for now args[0] is assumed to be the 'name' record_name = nil parent_id = args[0] if rest_has_name if args[1] record_name = args[1] end verify_args!(args:args, optparse:optparse, min:1, max: 2) else verify_args!(args:args, optparse:optparse, count: 1) end connect(options) # load parent record # todo: prompt instead of error parent_record = rest_parent_find_by_name_or_id(parent_id) if parent_record.nil? return 1, "#{rest_parent_label} not found for '#{parent_id}" end parent_id = parent_record['id'] # load or prompt for type if rest_has_type && type_option_type.nil? if record_type_id.nil? #raise_command_error "#{rest_type_label} is required.\n#{optparse}" type_list = rest_type_interface.list({max:10000, creatable: true})[rest_type_list_key] type_dropdown_options = type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} } record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type'] end record_type = rest_type_find_by_name_or_id(record_type_id) if record_type.nil? return 1, "#{rest_type_label} not found for '#{record_type_id}" end end passed_options = parse_passed_options(options) options[:params] ||= {} options[:params][rest_parent_param] = parent_id options[:options]['_object_key'] = rest_object_key payload = {} if options[:payload] payload = options[:payload] payload.deep_merge!({rest_object_key => passed_options}) else record_payload = {} if record_name record_payload['name'] = record_name options[:options]['name'] = record_name # injected for prompt options[:options][rest_arg] = record_name end if rest_has_type && record_type # record_payload['type'] = {'code' => record_type['code']} record_payload['type'] = record_type['code'] options[:options]['type'] = record_type['code'] # injected for prompt # initialize params for loading optionSource data options[:params]['type'] = record_type['code'] end record_payload.deep_merge!(passed_options) if option_types && !option_types.empty? v_prompt = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params]) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end # options by type if rest_has_type && record_type.nil? type_value = record_payload['type'].is_a?(Hash) ? record_payload['type']['id'] : record_payload['type'] if type_value record_type = rest_type_find_by_name_or_id(type_value) if record_type.nil? return 1, "#{rest_type_label} not found for '#{type_value}" end end # reload the type by id to get all the details ie. optionTypes if record_type && record_type['optionTypes'].nil? record_type = rest_type_find_by_name_or_id(record_type['id']) end end if respond_to?("load_option_types_for_#{rest_key}", true) my_option_types = send("load_option_types_for_#{rest_key}", record_type, parent_record) else my_option_types = record_type ? record_type['optionTypes'] : nil end if my_option_types && !my_option_types.empty? # remove redundant fieldContext my_option_types.each do |option_type| if option_type['fieldContext'] == rest_object_key option_type['fieldContext'] = nil end end api_params = (options[:params] || {}).merge(record_payload) v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end # advanced options (uses no_prompt) if advanced_option_types && !advanced_option_types.empty? v_prompt = Morpheus::Cli::OptionTypes.no_prompt(advanced_option_types, options[:options], @api_client, options[:params]) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end if respond_to?("#{rest_key}_add_prompt", true) record_payload = send("#{rest_key}_add_prompt", record_payload, record_type, parent_record, options) end # permissions if rest_perms_config[:enabled] if rest_perms_config[:version] == 2 perms = prompt_permissions_v2(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || []) else perms = prompt_permissions(options.deep_merge(rest_perms_config[:options] || {}), rest_perms_config[:excludes] || []) end unless rest_perms_config[:name].nil? perms.transform_keys! {|k| k == 'resourcePermissions' ? rest_perms_config[:name] : k} end unless rest_perms_config[:context].nil? perms_context = {} perms_context[rest_perms_config[:context]] = perms perms = perms_context end record_payload.merge!(perms) end payload[rest_object_key] = record_payload end rest_interface.setopts(options) if options[:dry_run] print_dry_run rest_interface.dry.create(parent_id, payload) return end json_response = rest_interface.create(parent_id, payload) if json_response['success'] render_response(json_response, options, rest_object_key) do record = json_response[rest_object_key] print_green_success "Added #{rest_label.downcase} #{record.nil? ? json_response['id'] : record['name'] || record['id']}" return _get(parent_id, record.nil? ? json_response['id'] : record['id'], {}, options) end else print red print json_response['msg'] print "\n" end return 0, nil end
get(args)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 305 def get(args) params = {} options = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]") build_get_options(opts, options, params) opts.footer = <<-EOT Get details about #{a_or_an(rest_label)} #{rest_label.downcase}. [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}. [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}. EOT end optparse.parse!(args) verify_args!(args:args, optparse:optparse, min:2) connect(options) parse_get_options!(args.count > 1 ? args[1..-1] : [], options, params) _get(args[0], args[1..-1].join(" "), params, options) end
list(args)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 262 def list(args) parent_id, parent_record = nil, nil params = {} options = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[#{rest_parent_arg}] [search]") build_list_options(opts, options, params) opts.footer = <<-EOT List #{rest_label_plural.downcase}. [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}. [search] is optional. This is a search phrase to filter the results. EOT end optparse.parse!(args) verify_args!(args:args, optparse:optparse, min:1) connect(options) parent_id = args[0] parent_record = rest_parent_find_by_name_or_id(parent_id) if parent_record.nil? return 1, "#{rest_parent_label} not found for '#{parent_id}" end parent_id = parent_record['id'] parse_list_options!(args.count > 1 ? args[1..-1] : [], options, params) rest_interface.setopts(options) if options[:dry_run] print_dry_run rest_interface.dry.list(parent_id, params) return end json_response = rest_interface.list(parent_id, params) render_response(json_response, options, rest_list_key) do records = json_response[rest_list_key] print_h1 "Morpheus #{rest_label_plural}" if records.nil? || records.empty? print cyan,"No #{rest_label_plural.downcase} found.",reset,"\n" else print as_pretty_table(records, rest_list_column_definitions(options.merge({:parent_record => parent_record})).upcase_keys!, options) print_results_pagination(json_response) if json_response['meta'] end print reset,"\n" end return 0, nil end
registered_interfaces()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 258 def registered_interfaces self.class.registered_interfaces end
remove(args)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 658 def remove(args) params = {} options = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]") build_standard_remove_options(opts, options) opts.footer = <<-EOT Delete an existing #{rest_label.downcase}. [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}. [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}. EOT end optparse.parse!(args) verify_args!(args:args, optparse:optparse, count:2) connect(options) parent_id = args[0] id = args[1] parent_record = rest_parent_find_by_name_or_id(parent_id) if parent_record.nil? return 1, "#{rest_parent_label} not found for '#{parent_id}" end record = rest_find_by_name_or_id(parent_record['id'], id) if record.nil? return 1, "#{rest_name} not found for '#{id}'" end unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the #{rest_label.downcase} #{record['name'] || record['id']}?") return 9, "aborted" end params.merge!(parse_query_options(options)) rest_interface.setopts(options) if options[:dry_run] print_dry_run rest_interface.dry.destroy(parent_id, record['id'], params) return 0, nil end json_response = rest_interface.destroy(parent_id, record['id'], params) render_response(json_response, options) do print_green_success "Removed #{rest_label.downcase} #{record['name'] || record['id']}" end return 0, nil end
render_response_details_for_get(record, options)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 363 def render_response_details_for_get(record, options) # override for custom details end
render_response_for_get(json_response, options)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 347 def render_response_for_get(json_response, options) render_response(json_response, options, rest_object_key) do record = json_response[rest_object_key] print_h1 rest_label, [], options print cyan print_description_list(rest_column_definitions(options), record, options) # show config settings... if record['optionTypes'] && record['optionTypes'].size > 0 print_h2 "Option Types", options print format_option_types_table(record['optionTypes'], options, rest_object_key) end render_response_details_for_get(record, options) print reset,"\n" end end
rest_find_by_name_or_id(parent_id, val)
click to toggle source
override RestCommand
method to include parent_id parameter
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 240 def rest_find_by_name_or_id(parent_id, val) # use explicitly defined finders # else default to new generic CliCommand find_by methods if rest_has_name if respond_to?("find_#{rest_key}_by_name_or_id", true) send("find_#{rest_key}_by_name_or_id", parent_id, val) else find_by_name_or_id(rest_key, parent_id, val) end else if respond_to?("find_#{rest_key}_by_id", true) send("find_#{rest_key}_by_id", parent_id, val) else find_by_id(rest_key, parent_id, val) end end end
rest_parent_arg()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 177 def rest_parent_arg self.class.rest_parent_arg end
rest_parent_column_definitions(options)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 213 def rest_parent_column_definitions(options) send("#{rest_parent_key}_column_definitions", options) end
rest_parent_find_by_name_or_id(val)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 221 def rest_parent_find_by_name_or_id(val) # use explicitly defined finders # else default to new generic CliCommand find_by methods if rest_parent_has_name if respond_to?("find_#{rest_parent_key}_by_name_or_id", true) send("find_#{rest_parent_key}_by_name_or_id", val) else find_by_name_or_id(rest_parent_key, val) end else if respond_to?("find_#{rest_parent_key}_by_id", true) send("find_#{rest_parent_key}_by_id", val) else find_by_id(rest_parent_key, val) end end end
rest_parent_has_name()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 185 def rest_parent_has_name self.class.rest_parent_has_name end
rest_parent_interface()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 201 def rest_parent_interface instance_variable_get("@#{rest_parent_interface_name}_interface") end
rest_parent_interface_name()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 197 def rest_parent_interface_name self.class.rest_parent_interface_name # || "@#{rest_parent_name}_interface" end
rest_parent_key()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 173 def rest_parent_key self.class.rest_parent_key end
rest_parent_label()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 189 def rest_parent_label self.class.rest_parent_label end
rest_parent_label_plural()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 193 def rest_parent_label_plural self.class.rest_parent_label_plural end
rest_parent_list_column_definitions(options)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 217 def rest_parent_list_column_definitions(options) send("#{rest_parent_key}_list_column_definitions", options) end
rest_parent_list_key()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 209 def rest_parent_list_key send("#{rest_parent_key}_list_key") end
rest_parent_name()
click to toggle source
duplicated the rest_* settings with rest_parent, for the parents resource
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 169 def rest_parent_name self.class.rest_parent_name end
rest_parent_object_key()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 205 def rest_parent_object_key send("#{rest_parent_key}_object_key") end
rest_parent_param()
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 181 def rest_parent_param self.class.rest_parent_param end
update(args)
click to toggle source
# File lib/morpheus/cli/mixins/secondary_rest_command.rb, line 539 def update(args) record_type = nil record_type_id = nil options = {} option_types = respond_to?("update_#{rest_key}_option_types", true) ? send("update_#{rest_key}_option_types") : [] advanced_option_types = respond_to?("update_#{rest_key}_advanced_option_types", true) ? send("update_#{rest_key}_advanced_option_types") : [] optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}] [options]") build_standard_update_options(opts, options) opts.footer = <<-EOT Update an existing #{rest_label.downcase}. [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}. [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}. EOT end optparse.parse!(args) verify_args!(args:args, optparse:optparse, count:2) connect(options) parent_id = args[0] id = args[1] parent_record = rest_parent_find_by_name_or_id(parent_id) if parent_record.nil? return 1, "#{rest_parent_label} not found for '#{parent_id}" end parent_id = parent_record['id'] connect(options) record = rest_find_by_name_or_id(parent_id, id) if record.nil? return 1, "#{rest_name} not found for '#{id}'" end # load type so we can prompt for those option types if rest_has_type record_type_id = record['type']['id'] record_type = rest_type_find_by_name_or_id(record_type_id) if record_type.nil? return 1, "#{rest_type_label} not found for '#{record_type_id}" end # reload the type by id to get all the details ie. optionTypes if record_type['optionTypes'].nil? record_type = rest_type_find_by_name_or_id(record_type['id']) end end passed_options = parse_passed_options(options) payload = {} if options[:payload] payload = options[:payload] payload.deep_merge!({rest_object_key => passed_options}) unless passed_options.empty? else record_payload = passed_options if rest_has_type && record_type # inject type to options for prompting # record_payload['type'] = record_type['code'] # options[:options]['type'] = record_type['code'] # initialize params for loading optionSource data options[:params] ||= {} options[:params]['type'] = record_type['code'] end # update options without prompting by default if false && option_types && !option_types.empty? api_params = (options[:params] || {}).merge(record_payload) # need to merge in values from record too, ughhh v_prompt = Morpheus::Cli::OptionTypes.no_prompt(option_types, options[:options], @api_client, api_params) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end # options by type my_option_types = nil if respond_to?("load_option_types_for_#{rest_key}", true) my_option_types = send("load_option_types_for_#{rest_key}", record_type, parent_record) else my_option_types = record_type ? record_type['optionTypes'] : nil end if false && my_option_types && !my_option_types.empty? # remove redundant fieldContext # make them optional for updates # todo: use current value as default instead of just making things optioanl # maybe new prompt() options like {:mode => :edit, :object => storage_server} or something my_option_types.each do |option_type| if option_type['fieldContext'] == rest_object_key option_type['fieldContext'] = nil end option_type.delete('required') option_type.delete('defaultValue') end v_prompt = Morpheus::Cli::OptionTypes.no_prompt(my_option_types, options[:options], @api_client, options[:params]) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end # advanced options if false && advanced_option_types && !advanced_option_types.empty? v_prompt = Morpheus::Cli::OptionTypes.no_prompt(advanced_option_types, options[:options], @api_client, options[:params]) v_prompt.deep_compact! v_prompt.booleanize! # 'on' => true record_payload.deep_merge!(v_prompt) end # remove empty config, compact could hanlde this if record_payload['config'] && record_payload['config'].empty? record_payload.delete('config') end # prevent updating with empty payload if record_payload.empty? raise_command_error "Specify at least one option to update.\n#{optparse}" end payload[rest_object_key] = record_payload end rest_interface.setopts(options) if options[:dry_run] print_dry_run rest_interface.dry.update(parent_id, record['id'], payload) return end json_response = rest_interface.update(parent_id, record['id'], payload) render_response(json_response, options, rest_object_key) do print_green_success "Updated #{rest_label.downcase} #{record['name'] || record['id']}" _get(parent_id, record["id"], {}, options) end return 0, nil end