class Morpheus::Cli::JobsCommand
Public Instance Methods
_get(job_id, options = {}, max_execs = 3)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 143 def _get(job_id, options = {}, max_execs = 3) @jobs_interface.setopts(options) if !(job_id.to_s =~ /\A\d{1,}\Z/) job = find_by_name_or_id('job', job_id) if !job print_red_alert "Job #{job_id} not found" exit 1 end job_id = job['id'] end max_execs = 3 if max_execs.nil? params = {'includeExecCount' => max_execs} if options[:dry_run] print_dry_run @jobs_interface.dry.get(job_id, params) return end json_response = @jobs_interface.get(job_id, params) render_response(json_response, options, 'job') do job = json_response['job'] schedule_name = '' if !job['scheduleMode'].nil? if job['scheduleMode'] == 'manual' schedule_name = 'Manual' elsif job['scheduleMode'].to_s.downcase == 'datetime' schedule_name = ("Date and Time - " + (format_local_dt(job['dateTime']).to_s rescue 'n/a')) elsif job['scheduleMode'].to_s == '' schedule_name = 'n/a' # should not happen else begin schedule = @execute_schedules_interface.get(job['scheduleMode'])['schedule'] schedule_name = schedule ? schedule['name'] : '' rescue => ex Morpheus::Logging::DarkPrinter.puts "Failed to load schedule name" if Morpheus::Logging.debug? schedule_name = 'n/a' end end end title = "Morpheus Job" subtitles = [] subtitles += parse_list_subtitles(options) print_h1 title, subtitles print cyan description_cols = [ {"ID" => lambda {|it| it['id'] } }, {"Name" => lambda {|it| it['name']} }, {"Labels" => lambda {|it| format_list(it['labels'], '', 3) rescue '' } }, {"Job Type" => lambda {|it| it['type']['name']} }, {"Enabled" => lambda {|it| format_boolean(it['enabled'])} }, job['workflow'] ? {'Task' => lambda {|it| (it['workflow']['name'] rescue nil) || it['jobSummary']} } : nil, job['task'] ? {'Workflow' => lambda {|it| (it['task']['name'] rescue nil) || it['jobSummary']} } : nil, job['securityPackage'] ? {'Security Package' => lambda {|it| (it['securityPackage']['name'] rescue nil) || it['jobSummary']} } : nil, job['securityPackage'] ? {'Scan Checklist' => lambda {|it| (it['scanPath'] rescue nil)} } : nil, job['securityPackage'] ? {'Security Profile' => lambda {|it| (it['securityProfile'] rescue nil)} } : nil, {"Schedule" => lambda {|it| schedule_name} } ].compact if job['targetType'] description_cols << {"Context Type" => lambda {|it| it['targetType'] == 'appliance' ? 'None' : it['targetType'] } } if job['targetType'] != 'appliance' description_cols << {"Context #{job['targetType'].capitalize}#{job['targets'].count > 1 ? 's' : ''}" => lambda {|it| it['targets'].collect {|it| it['name']}.join(', ')} } end end print_description_list(description_cols, job) # config = job['config'] # # prune config of stuff that user does not xare about # config = config.reject! {|k,v| ["configList","workflowId","taskId","securitySpecId", "customConfig"].include?(k) } # if config && !config.empty? # print_h2 "Configuration" # print_description_list(config.keys, config) # print reset,"\n" # end if max_execs != 0 print_h2 "Recent Executions" print_job_executions(json_response['executions']['jobExecutions'], options) if json_response['executions']['meta'] && json_response['executions']['meta']['total'] > max_execs print_results_pagination(json_response['executions']) end end print reset,"\n" end return 0, nil end
add(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 236 def add(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage( "[name]") opts.on("--name NAME", String, "Updates job name") do |val| params['name'] = val.to_s end opts.on('-l', '--labels [LIST]', String, "Labels") do |val| params['labels'] = parse_labels(val) end opts.on('-a', '--active [on|off]', String, "Can be used to enable / disable the job. Default is on") do |val| params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == '' end opts.on('--type [TYPE]', String, "Job Type Name, Code or ID. The available types are \"Security Scan Job\", \"Task Job\", and \"Workflow Job\"") do |val| options[:type] = val end opts.on('-t', '--task [TASK]', String, "Task ID or name, assigns task to job. This sets the job type to \"Task Job\".") do |val| if options[:workflow] raise_command_error "Options --task and --workflow are incompatible" elsif options[:security_package] raise_command_error "Options --task and --security-package are incompatible" else options[:task] = val end options[:type] = 'morpheus.task' end opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or name, assigns workflow to job. This sets the job type to \"Workflow Job\".") do |val| if options[:task] raise_command_error "Options --workflow and --task are incompatible" elsif options[:security_package] raise_command_error "Options --workflow and --security-package are incompatible" else options[:workflow] = val end options[:type] = 'morpheus.workflow' end opts.on('--security-package [PACKAGE]', String, "Security Package ID or name, assigns security package to job. This sets the job type to \"Security Scan Job\".") do |val| if options[:workflow] raise_command_error "Options --security-package and --workflow are incompatible" elsif options[:task] raise_command_error "Options --security-package and --workflow are incompatible" else options[:security_package] = val end options[:type] = 'morpheus.securityScan' end opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val| val = val.to_s.downcase params['targetType'] = (val == 'none' ? 'appliance' : val) end opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val| val = val.to_s.downcase params['targetType'] = (val == 'none' ? 'appliance' : val) end opts.add_hidden_option('--target-type') opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list| params['targetType'] = 'instance' params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}} end opts.on('--instance-label LABEL', String, "Instance Label") do |val| if params['targetType'] && params['targetType'] != 'instance-label' raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})") end params['targetType'] = 'instance-label' params['instanceLabel'] = val end opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list| params['targetType'] = 'server' params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}} end opts.on('--server-label LABEL', String, "Server Label") do |val| if params['targetType'] && params['targetType'] != 'server-label' raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})") end params['targetType'] = 'server-label' params['serverLabel'] = val end opts.on('-S', '--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val| options[:schedule] = val end opts.on('--config [TEXT]', String, "Custom config") do |val| params['customConfig'] = val.to_s end opts.on('-R', '--run [on|off]', String, "Can be used to run the job now.") do |val| params['run'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == '' end opts.on('--date-time DATETIME', String, "Can be used to run schedule at a specific date and time. Use UTC time in the format 2020-02-15T05:00:00Z. This sets scheduleMode to 'dateTime'.") do |val| options[:schedule] = 'dateTime' params['dateTime'] = val.to_s end opts.on('--scan-checklist [VALUE]', String, "Scan Checklist. Only applicable to the security scan job type") do |val| # params['config'] ||= {} # params['config']['scanPath'] = val.to_s params['scanPath'] = val.to_s end opts.on('--security-profile [VALUE]', String, "Security Profile. Only applicable to the security scan job type") do |val| # params['config'] ||= {} # params['config']['securityProfile'] = val.to_s params['securityProfile'] = val.to_s end build_standard_add_options(opts, options) opts.footer = "Create job." end optparse.parse!(args) connect(options) verify_args!(args:args, optparse:optparse, max:1) if options[:payload] payload = parse_payload(options, 'job') else apply_options(params, options) # name params['name'] = params['name'] || args[0] || name = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Job Name', 'required' => true, 'description' => 'Job Name.'}],options[:options],@api_client,{})['name'] # type id is needed to load the options right now.. job_type = nil job_types = get_available_job_types() if options[:task].nil? && options[:workflow].nil? && options[:security_package].nil? if options[:type] # match on value (id) or code or name job_type = job_types.find {|it| it['value'].to_s.downcase == options[:type].to_s.downcase } || job_types.find {|it| it['code'].to_s.downcase == options[:type].to_s.downcase } || job_types.find {|it| it['name'].to_s.downcase == options[:type].to_s.downcase || it['name'].to_s.downcase == "#{options[:type]} Job".to_s.downcase } if job_type.nil? raise_command_error "Job Type not found for '#{options[:type]}'" end else # prompt job_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'jobType', 'type' => 'select', 'fieldLabel' => 'Job Type', 'selectOptions' => job_types, 'required' => true, 'description' => 'Select Job Type.'}],options[:options],@api_client,{})['jobType'] job_type = job_types.find {|it| it['value'] == job_type_id} end # prompt task / workflow / securityPackage if job_type['code'] == 'morpheus.task' params['task'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'task.id', 'fieldLabel' => 'Task', 'type' => 'select', 'required' => true, 'optionSource' => 'tasks'}], options[:options], @api_client, {})['task'] elsif job_type['code'] == 'morpheus.workflow' params['workflow'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'workflow.id', 'fieldLabel' => 'Workflow', 'type' => 'select', 'required' => true, 'optionSource' => 'operationTaskSets'}], options[:options], @api_client, {})['workflow'] elsif job_type['code'] == 'morpheus.securityScan' params['securityPackage'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'securityPackage.id', 'fieldLabel' => 'Security Package', 'type' => 'select', 'required' => true, 'optionSource' => 'securityPackages'}], options[:options], @api_client, {})['securityPackage'] end # type is not even included in the payload? lol # params["type"] = {"code" => job_type["code"]} end # task if !options[:task].nil? task = find_by_name_or_id('task', options[:task]) if task.nil? print_red_alert "Task #{options[:task]} not found" exit 1 end params['task'] = {'id' => task['id']} job_type = job_types.find {|it| it['code'] == 'morpheus.task' } # || raise_command_error "Unable to find job type for 'morpheus.task'" end # workflow task_set = nil if !options[:workflow].nil? task_set = find_by_name_or_id('task_set', options[:workflow]) if task_set.nil? print_red_alert "Workflow #{options[:workflow]} not found" exit 1 end params['workflow'] = {'id' => task_set['id']} job_type = job_types.find {|it| it['code'] == 'morpheus.workflow' } # || raise_command_error "Unable to find job type for 'morpheus.workflow'" end # load workflow if we havent yet if (params['workflow'] && params['workflow']['id']) && task_set.nil? task_set = find_by_name_or_id('task_set', params['workflow']['id']) if task_set.nil? print_red_alert "Workflow #{params['workflow']['id']} not found" exit 1 end end # prompt for custom options for workflow custom_option_types = task_set ? task_set['optionTypes'] : nil if custom_option_types && custom_option_types.size() > 0 # they are all returned in a single array right now, so skip prompting for the jobType optionTypes custom_option_types.reject! { |it| it['code'] && it['code'].include?('job.type') } custom_option_types = custom_option_types.collect {|it| it['fieldContext'] = 'customOptions' it } custom_options = Morpheus::Cli::OptionTypes.prompt(custom_option_types, options[:options], @api_client, {}) params['customOptions'] = custom_options['customOptions'] end # security package if !options[:security_package].nil? # task = find_by_name_or_id('securityPackage', options[:security_package]) security_package = nil if (options[:security_package].to_s =~ /\A\d{1,}\Z/) security_package = @options_interface.options_for_source('securityPackages', {})['data'].find {|it| it['value'] == options[:security_package].to_i }['value'] else security_package = @options_interface.options_for_source('securityPackages', {})['data'].find {|it| it['name'].to_s.downcase == options[:security_package].to_s.downcase }['value'] end if security_package.nil? print_red_alert "Security Package #{options[:security_package]} not found" exit 1 end params['securityPackage'] = {'id' => security_package['id']} job_type = job_types.find {|it| it['code'] == 'morpheus.securityScan' } # || raise_command_error "Unable to find job type for 'morpheus.workflow'" end # load options based upon job type + task / workflow / securityPackage job_options = @jobs_interface.options(job_type['id'], {'taskId' => params['task'] ? params['task']['id'] : nil, 'workflowId' => params['workflow'] ? params['workflow']['id'] : nil, 'securityPackageId' => params['securityPackage'] ? params['securityPackage']['id'] : nil}) # securityScan options if job_type['code'] == 'morpheus.securityScan' job_type_option_types = [ {'fieldName' => 'scanPath', 'fieldLabel' => "Scan Checklist", 'type' => 'text', 'required' => true, 'displayOrder' => 20}, {'fieldName' => 'securityProfile', 'fieldLabel' => "Security Profile", 'type' => 'text', 'required' => true, 'displayOrder' => 21} ] #job_type_option_types = job_options['optionTypes'] || [] # reject securitySpecId because already prompted for securityPackage above, # and it is passed as securityPackage.id instead of config.securitySpecId # also remove config context because jobs return this configuration at the level too #job_type_option_types.reject! {|it| it['fieldName'] == "securitySpecId" } #job_type_option_types = job_type_option_types.collect {|it| it.delete("fieldContext") if it["fieldContext"] == "config" } v_prompt = Morpheus::Cli::OptionTypes.prompt(job_type_option_types, options[:options], @api_client, {}) v_prompt.deep_compact! params.deep_merge!(v_prompt) end # context type if params['targetType'].nil? params['targetType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'required' => true, 'selectOptions' => job_options['targetTypes'], 'defaultValue' => job_options['targetTypes'].first['name']}], options[:options], @api_client, {})['targetType'] end # contexts if ['instance', 'server'].include?(params['targetType']) && (params['targets'].nil? || params['targets'].empty?) targets = [] if params['targetType'] == 'instance' avail_targets = @instances_interface.list({max:10000})['instances'].collect {|it| {'name' => it['name'], 'value' => it['id']}} else avail_targets = @servers_interface.list({max:10000, 'vmHypervisor' => nil, 'containerHypervisor' => nil})['servers'].collect {|it| {'name' => it['name'], 'value' => it['id']}} end target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => true, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target'] targets << target_id avail_targets.reject! {|it| it['value'] == target_id} while !target_id.nil? && !avail_targets.empty? && Morpheus::Cli::OptionTypes.confirm("Add another context #{params['targetType']}?", {:default => false}) target_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'target', 'fieldLabel' => "Context #{params['targetType'].capitalize}", 'type' => 'select', 'required' => false, 'selectOptions' => avail_targets}], options[:options], @api_client, {}, options[:no_prompt], true)['target'] if !target_id.nil? targets << target_id avail_targets.reject! {|it| it['value'] == target_id} end end params['targets'] = targets.collect {|it| {'refId' => it}} elsif params['targetType'] == 'instance-label' if params['instanceLabel'].nil? params['instanceLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel'] end elsif params['targetType'] == 'server-label' if params['serverLabel'].nil? params['serverLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel'] end end # schedule if options[:schedule].nil? options[:schedule] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'scheduleMode', 'fieldLabel' => "Schedule", 'type' => 'select', 'required' => true, 'selectOptions' => job_options['schedules'], 'defaultValue' => job_options['schedules'].first['name']}], options[:options], @api_client, {})['scheduleMode'] params['scheduleMode'] = options[:schedule] end if options[:schedule] == 'manual' # cool elsif options[:schedule].to_s.downcase == 'datetime' # prompt for dateTime if params['dateTime'].nil? params['dateTime'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'dateTime', 'fieldLabel' => "Date and Time", 'type' => 'text', 'required' => true}], options[:options], @api_client, {}, options[:no_prompt], true)['dateTime'] end elsif options[:schedule].to_s != '' # ok they passed a schedule name or id schedule = job_options['schedules'].find {|it| it['name'] == options[:schedule] || it['value'] == options[:schedule].to_i} if schedule.nil? print_red_alert "Schedule #{options[:schedule]} not found" exit 1 end options[:schedule] = schedule['value'] end params['scheduleMode'] = options[:schedule] # custom config if params['customConfig'].nil? && job_options['allowCustomConfig'] params['customConfig'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'config', 'fieldLabel' => "Custom Config", 'type' => 'text', 'required' => false}], options[:options], @api_client, {})['config'] end payload = {'job' => params} end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.create(payload) return end json_response = @jobs_interface.create(payload) job = json_response['job'] || payload["job"] # api only recently started returning job! render_response(json_response, options, 'job') do # print_green_success "Job created" print_green_success "Added job #{job['name']}" _get(job['id'], options) end end
connect(opts)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 14 def connect(opts) @api_client = establish_remote_appliance_connection(opts) @jobs_interface = @api_client.jobs @options_interface = @api_client.options @tasks_interface = @api_client.tasks @task_sets_interface = @api_client.task_sets @instances_interface = @api_client.instances @servers_interface = @api_client.servers @containers_interface = @api_client.containers @execute_schedules_interface = @api_client.execute_schedules end
execute(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 748 def execute(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage( "[job]") opts.on('--config [TEXT]', String, "Custom config") do |val| params['customConfig'] = val.to_s end build_common_options(opts, options, [:json, :dry_run, :remote]) opts.footer = "Run job.\n" + "[job] is required. Job ID or name" end optparse.parse!(args) connect(options) if args.count != 1 raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}" return 1 end begin job = find_by_name_or_id('job', args[0]) if job.nil? print_red_alert "Job #{args[0]} not found" exit 1 end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.execute_job(job['id'], params) return end json_response = @jobs_interface.execute_job(job['id'], params) if options[:json] puts as_json(json_response, options) elsif !options[:quiet] if json_response['success'] print_green_success "Job queued for execution" _get(job['id'], options) else print_red_alert "Error executing job: #{json_response['msg'] || json_response['errors']}" end end return 0 rescue RestClient::Exception => e print_rest_exception(e, options) exit 1 end end
get(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 126 def get(args) options = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[job] [max-exec-count]") build_standard_get_options(opts, options) opts.footer = "Get details about a job.\n" + "[job] is required. Job ID or name.\n" + "[max-exec-count] is optional. Specified max # of most recent executions. Defaults is 3" end optparse.parse!(args) if args.count < 1 raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}" end connect(options) return _get(args[0], options, args.count > 1 ? args[1].to_i : nil) end
get_execution(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 893 def get_execution(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[id]") build_standard_get_options(opts, options, [:details]) opts.footer = "Get details about a job.\n" + "[id] is required. Job execution ID." end optparse.parse!(args) if args.count != 1 raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}" end connect(options) @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.get_execution(args[0], params) return end json_response = @jobs_interface.get_execution(args[0], params) render_response(json_response, options, 'jobExecution') do print_h1 "Morpheus Job Execution", [], options print_job_execution(json_response['jobExecution'], options) end return 0, nil end
get_execution_event(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 921 def get_execution_event(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[id] [event]") build_standard_get_options(opts, options) opts.footer = "Get details about a job.\n" + "[id] is required. Job execution ID.\n" + "[event] is required. Process event ID." end optparse.parse!(args) if args.count != 2 raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}" end connect(options) begin @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.get_execution_event(args[0].to_i, args[1].to_i, params) return end json_response = @jobs_interface.get_execution_event(args[0].to_i, args[1].to_i, params) render_result = render_with_format(json_response, options, 'processEvent') return 0 if render_result title = "Morpheus Job Execution Event" subtitles = [] subtitles += parse_list_subtitles(options) print_h1 title, subtitles event = json_response['processEvent'] event_data = get_process_event_data(event) description_cols = { "ID" => lambda {|it| it[:id]}, "Description" => lambda {|it| it[:description]}, "Start Date" => lambda {|it| it[:start_date]}, "Created By" => lambda {|it| it[:created_by]}, "Duration" => lambda {|it| it[:duration]}, "Status" => lambda {|it| it[:status]} } print_description_list(description_cols, event_data) if event_data[:output] && event_data[:output].strip.length > 0 print_h2 "Output" print event['output'] end if event_data[:error] && event_data[:error].strip.length > 0 print_h2 "Error" print red print event['message'] || event['error'] print reset end print reset,"\n" return 0 rescue RestClient::Exception => e print_rest_exception(e, options) exit 1 end end
handle(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 26 def handle(args) handle_subcommand(args) end
list(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 30 def list(args) options = {} options[:show_stats] = true params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage() opts.on("--source [all|user|discovered]", String, "Filters job based upon specified source. Default is all") do |val| options[:source] = val.to_s end opts.on("--internal [true|false]", String, "Filters job based on internal flag. Internal jobs are excluded by default.") do |val| params["internalOnly"] = (val.to_s != "false") end opts.on("--stats [true|false]", String, "Hide Execution Stats. Job statistics are displayed by default.") do |val| options[:show_stats] = (val.to_s != "false") end opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val| add_query_parameter(params, 'labels', parse_labels(val)) end opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val| add_query_parameter(params, 'allLabels', parse_labels(val)) end build_standard_list_options(opts, options) opts.footer = "List jobs." end optparse.parse!(args) connect(options) # verify_args!(args:args, optparse:optparse, count:0) if args.count > 0 options[:phrase] = args.join(" ") end params.merge!(parse_list_options(options)) if !options[:source].nil? if !['all', 'user', 'discovered', 'sync'].include?(options[:source]) raise_command_error("Invalid source filter #{options[:source]}", args, optparse) end params['itemSource'] = options[:source] == 'discovered' ? 'sync' : options[:source] end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.list(params) return end json_response = @jobs_interface.list(params) jobs = json_response['jobs'] render_response(json_response, options, 'jobs') do title = "Morpheus Jobs" subtitles = [] subtitles += parse_list_subtitles(options) if params["internalOnly"] subtitles << "internalOnly: #{params['internalOnly']}" end print_h1 title, subtitles, options if jobs.empty? print cyan,"No jobs found.",reset,"\n" else columns = { "ID" => 'id', "Type" => lambda {|job| job['type'] ? job['type']['name'] : '' }, "Name" => 'name', "Labels" => lambda {|it| format_list(it['labels'], '', 3) rescue '' }, "Details" => lambda {|job| job['jobSummary'] }, "Enabled" => lambda {|job| "#{job['enabled'] ? '' : yellow}#{format_boolean(job['enabled'])}#{cyan}" }, # "Date Created" => lambda {|job| format_local_dt(job['dateCreated']) }, # "Last Updated" => lambda {|job| format_local_dt(job['lastUpdated']) }, "Last Run" => lambda {|job| format_local_dt(job['lastRun']) }, "Next Run" => lambda {|job| job['enabled'] && job['scheduleMode'] && job['scheduleMode'] != 'manual' ? format_local_dt(job['nextFire']) : '' }, "Last Result" => lambda {|job| format_job_status(job['lastResult']) }, } print as_pretty_table(jobs, columns.upcase_keys!, options) print_results_pagination(json_response) if options[:show_stats] if stats = json_response['stats'] label_width = 17 print_h2 "Execution Stats - Last 7 Days" print cyan print "Jobs".rjust(label_width, ' ') + ": #{stats['jobCount']}\n" print "Executions Today".rjust(label_width, ' ') + ": #{stats['todayCount']}\n" print "Daily Executions".rjust(label_width, ' ') + ": " + stats['executionsPerDay'].join(' | ') + "\n" print "Total Executions".rjust(label_width, ' ') + ": #{stats['execCount']}\n" print "Completed".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['execSuccessRate'].to_f, 100, {bar_color:green}) + "#{stats['execSuccess']}".rjust(15, ' ') + " of " + "#{stats['execCount']}".ljust(15, ' ') + "\n#{cyan}" print "Failed".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['execFailedRate'].to_f, 100, {bar_color:red}) + "#{stats['execFailed']}".rjust(15, ' ') + " of " + "#{stats['execCount']}".ljust(15, ' ') + "\n#{cyan}" end print reset,"\n" end end print reset,"\n" end if jobs.empty? return 1, "no jobs found" else return 0, nil end end
list_executions(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 832 def list_executions(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage("[job]") opts.on('--job JOB', String, "Filter by Job ID or name.") do |val| options[:job] = val end opts.on("--internal [true|false]", String, "Filters executions based on internal flag. Internal executions are excluded by default.") do |val| params["internalOnly"] = (val.to_s != "false") end opts.on("--automation [true|false]", String, "Filters executions based on automation flag. Non-automation executions include ansible and kubernetes job types.") do |val| params["automation"] = (val.to_s != "false") end build_standard_list_options(opts, options, [:details]) opts.footer = "List job executions.\n" + "[job] is optional. Job ID or name to filter executions." end optparse.parse!(args) connect(options) # verify_args!(args:args, optparse:optparse, max:1) if args.count > 0 options[:job] = args.join(" ") end params.merge!(parse_list_options(options)) if options[:job] job = find_by_name_or_id('job', options[:job]) if job.nil? raise_command_error "Job #{options[:job]} not found" end params['jobId'] = job['id'] end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.list_executions(params) return end json_response = @jobs_interface.list_executions(params) job_executions = json_response['jobExecutions'] render_response(json_response, options, 'jobExecutions') do title = "Morpheus Job Executions" subtitles = job ? ["Job: #{job['name']}"] : [] subtitles += parse_list_subtitles(options) if params["internalOnly"] subtitles << "internalOnly: #{params['internalOnly']}" end if !params["automation"].nil? subtitles << "automation: #{params['automation']}" end print_h1 title, subtitles, options print_job_executions(job_executions, options) print_results_pagination(json_response) unless job_executions.size == 0 print reset,"\n" end return 0, nil end
remove(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 800 def remove(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage( "[job]") build_standard_remove_options(opts, options) opts.footer = "Remove job.\n" + "[job] is required. Job ID or name" end optparse.parse!(args) connect(options) verify_args!(args:args, optparse:optparse, count:1) job = find_by_name_or_id('job', args[0]) if job.nil? print_red_alert "Job #{args[0]} not found" exit 1 end unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the job '#{job['name']}'?", options) return 9, "aborted command" end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.destroy(job['id'], params) return end json_response = @jobs_interface.destroy(job['id'], params) render_response(json_response, options) do print_green_success "Job #{job['name']} removed" end return 0, nil end
update(args)
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 543 def update(args) options = {} params = {} optparse = Morpheus::Cli::OptionParser.new do |opts| opts.banner = subcommand_usage( "[job]") opts.on("--name NAME", String, "Updates job name") do |val| params['name'] = val.to_s end opts.on('-l', '--labels [LIST]', String, "Labels") do |val| params['labels'] = parse_labels(val) end opts.on('-a', '--active [on|off]', String, "Can be used to enable / disable the job. Default is on") do |val| params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == '' end opts.on('-t', '--task [TASK]', String, "Task ID or name, assigns task to job. Only compatible with workflow job type.") do |val| if options[:workflow] raise_command_error "Options --task and --workflow are incompatible" elsif options[:security_package] raise_command_error "Options --task and --security-package are incompatible" else options[:task] = val end end opts.on('-w', '--workflow [WORKFLOW]', String, "Workflow ID or name, assigns workflow to job. Only compatible with security scan job type.") do |val| if options[:task] raise_command_error "Options --workflow and --task are incompatible" elsif options[:security_package] raise_command_error "Options --workflow and --security-package are incompatible" else options[:workflow] = val end end opts.on('--security-package [PACKAGE]', String, "Security Package ID or name, assigns security package to job. Only compatible with security scan job type.") do |val| if options[:workflow] raise_command_error "Options --security-package and --workflow are incompatible" elsif options[:task] raise_command_error "Options --security-package and --workflow are incompatible" else options[:security_package] = val end end opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val| val = val.to_s.downcase params['targetType'] = (val == 'none' ? 'appliance' : val) end opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val| val = val.to_s.downcase params['targetType'] = (val == 'none' ? 'appliance' : val) end opts.add_hidden_option('--target-type') opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list| params['targetType'] = 'instance' options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}} end opts.on('--instance-label LABEL', String, "Instance Label") do |val| if params['targetType'] && params['targetType'] != 'instance-label' raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})") end params['targetType'] = 'instance-label' params['instanceLabel'] = val end opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list| params['targetType'] = 'server' options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}} end opts.on('--server-label LABEL', String, "Server Label") do |val| if params['targetType'] && params['targetType'] != 'server-label' raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})") end params['targetType'] = 'server-label' params['serverLabel'] = val end opts.on('--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val| options[:schedule] = val end opts.on('--config [TEXT]', String, "Custom config") do |val| params['customConfig'] = val.to_s end opts.on('-R', '--run [on|off]', String, "Can be used to run the job now.") do |val| params['run'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == '' end opts.on('--date-time DATETIME', String, "Can be used to run schedule at a specific date and time. Use UTC time in the format 2020-02-15T05:00:00Z. This sets scheduleMode to 'dateTime'.") do |val| options[:schedule] = 'dateTime' params['dateTime'] = val.to_s end build_standard_update_options(opts, options) opts.footer = "Update job.\n" + "[job] is required. Job ID or name" end optparse.parse!(args) connect(options) verify_args!(args:args, optparse:optparse, count:1) job = find_by_name_or_id('job', args[0]) if job.nil? print_red_alert "Job #{args[0]} not found" exit 1 end if options[:payload] payload = parse_payload(options, 'job') else apply_options(params, options) job_type_id = job['type']['id'] if !options[:task].nil? task = find_by_name_or_id('task', options[:task]) if task.nil? print_red_alert "Task #{options[:task]} not found" exit 1 end params['task'] = {'id': task['id']} job_type_id = load_job_type_id_by_code('morpheus.task') end if !options[:workflow].nil? task_set = find_by_name_or_id('task_set', options[:workflow]) if task_set.nil? print_red_alert "Workflow #{options[:workflow]} not found" exit 1 end params['workflow'] = {'id': task_set['id']} job_type_id = load_job_type_id_by_code('morpheus.workflow') end if !options[:targets].nil? && ['instance', 'server'].include?(params['targetType']) params['targets'] = [] target_type = params['targetType'] || job['targetType'] options[:targets].collect do |it| target = find_by_name_or_id(target_type, it['refId']) if target.nil? print_red_alert "Context #{target_type} #{it['refId']} not found" exit 1 end params['targets'] << it end end if !options[:schedule].nil? if options[:schedule] != 'manual' && options[:schedule].to_s.downcase != 'datetime' job_options = @jobs_interface.options(job_type_id) schedule = job_options['schedules'].find {|it| it['name'] == options[:schedule] || it['value'] == options[:schedule].to_i} if schedule.nil? print_red_alert "Schedule #{options[:schedule]} not found" exit 1 end options[:schedule] = schedule['value'] end params['scheduleMode'] = options[:schedule] end # schedule if !options[:schedule].nil? if options[:schedule] == 'manual' # cool elsif options[:schedule].to_s.downcase == 'datetime' # prompt for dateTime if params['dateTime'].nil? raise_command_error "--date-time is required for schedule '#{options[:schedule]}'\n#{optparse}" end elsif options[:schedule].to_s != '' job_options = @jobs_interface.options(job_type_id) options[:schedule] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'schedule', 'fieldLabel' => "Schedule", 'type' => 'select', 'required' => true, 'selectOptions' => job_options['schedules'], 'defaultValue' => job_options['schedules'].first['name']}], options[:options], @api_client, {})['schedule'] params['scheduleMode'] = options[:schedule] # ok they passed a schedule name or id schedule = job_options['schedules'].find {|it| it['name'] == options[:schedule] || it['value'] == options[:schedule].to_i} if schedule.nil? print_red_alert "Schedule #{options[:schedule]} not found" exit 1 end options[:schedule] = schedule['value'] end end payload = {'job' => params} end if payload['job'].nil? || payload['job'].empty? print_green_success "Nothing to update" exit 1 end @jobs_interface.setopts(options) if options[:dry_run] print_dry_run @jobs_interface.dry.update(job['id'], payload) return end json_response = @jobs_interface.update(job['id'], payload) job = json_response["job"] || job # api only recently started returning job! render_response(json_response, options, 'job') do print_green_success "Updated job #{job['name']}" _get(job['id'], options) end end
Private Instance Methods
get_available_job_types()
click to toggle source
# File lib/morpheus/cli/commands/jobs_command.rb, line 987 def get_available_job_types() job_types = @options_interface.options_for_source('jobTypes', {})['data'] # the jobType code has this ".jobType" added to the end for ui translation or something.. job_types.each do |jt| # jt['msgCode'] = jt['code'] jt['code'] = jt['code'].sub(/\.jobType\Z/,'') # set id for convenience jt['id'] = jt['value'] if !jt['id'] && jt['value'] end job_types end