class Occi::Cli::OcciOpts

Constants

ACTIONS
AUTH_METHODS
ENTITY_TYPES
LOG_LEVELS
LOG_OUTPUTS
MEDIA_TYPES
REQ_CREATE_ATTRS

Public Class Methods

parse(args, test_env = false) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 21
def self.parse(args, test_env = false)

  @@quiet = test_env

  options = Hashie::Mash.new
  set_defaults(options)

  opts = OptionParser.new do |opts|
    opts.banner = %Q{Usage: occi [OPTIONS]}

    opts.separator ""
    opts.separator "Options:"

    opts.on("-e",
            "--endpoint URI",
            String,
            "OCCI server URI, defaults to #{options.endpoint.inspect}") do |endpoint|
      options.endpoint = URI(endpoint).to_s
    end

    opts.on("-n",
            "--auth METHOD",
            AUTH_METHODS,
            "Authentication method, only: [#{AUTH_METHODS.join('|')}], defaults "                  "to #{options.auth.type.inspect}") do |auth|
      options.auth.type = auth.to_s
    end

    opts.on("-k",
            "--timeout SEC",
            Integer,
            "Default timeout for all HTTP connections, in seconds") do |timeout|
      raise "Timeout has to be a number larger than 0!" if timeout < 1
      options.timeout = timeout
    end

    opts.on("-u",
            "--username USER",
            String,
            "Username for basic or digest authentication, defaults to "                  "#{options.auth.username.inspect}") do |username|
      options.auth.username = username
    end

    opts.on("-p",
            "--password PASSWORD",
            String,
            "Password for basic, digest and x509 authentication") do |password|
      options.auth.password = password
      options.auth.user_cert_password = password
    end

    opts.on("-c",
            "--ca-path PATH",
            String,
            "Path to CA certificates directory, defaults to #{options.auth.ca_path.inspect}") do |ca_path|
      raise ArgumentError, "Path specified in --ca-path is not a directory!" unless File.directory? ca_path
      raise ArgumentError, "Path specified in --ca-path is not readable!" unless File.readable? ca_path

      options.auth.ca_path = ca_path
    end

    opts.on("-f",
            "--ca-file PATH",
            String,
            "Path to CA certificates in a file") do |ca_file|
      raise ArgumentError, "File specified in --ca-file is not a file!" unless File.file? ca_file
      raise ArgumentError, "File specified in --ca-file is not readable!" unless File.readable? ca_file

      options.auth.ca_file = ca_file
    end

    opts.on("-s",
            "--skip-ca-check",
            "Skip server certificate verification [NOT recommended]") do
      silence_warnings { OpenSSL::SSL.const_set(:VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE) }
    end

    opts.on("-F",
            "--filter CATEGORY",
            String,
            "Category type identifier to filter categories from model, must "                  "be used together with the -m option") do |filter|
      options.filter = filter
    end

    opts.on("-x",
            "--user-cred FILE",
            String,
            "Path to user's x509 credentials, defaults to #{options.auth.user_cert.inspect}") do |user_cred|
      raise ArgumentError, "File specified in --user-cred is not a file!" unless File.file? user_cred
      raise ArgumentError, "File specified in --user-cred is not readable!" unless File.readable? user_cred

      options.auth.user_cert = user_cred
    end

    opts.on("-X",
            "--voms",
            "Using VOMS credentials; modifies behavior of the X509 authN module") do |voms|

      options.auth.voms = true
    end

    opts.on("-q",
            "--token TOKEN",
            String,
            "A pre-generated token to be used for authentication purposes") do |token|
      raise ArgumentError, "Token cannot be blank!" if token.blank?

      options.auth.token = token
    end

    opts.on("-y",
            "--media-type MEDIA_TYPE",
            MEDIA_TYPES,
            "Media type for client <-> server communication, only: [#{MEDIA_TYPES.join('|')}], "                  "defaults to #{options.media_type.inspect}") do |media_type|
      options.media_type = media_type
    end

    opts.on("-r",
            "--resource RESOURCE",
            String,
            "Term, identifier or URI of a resource to be queried, required") do |resource|
      options.resource = resource
    end

    opts.on("-t",
            "--attribute ATTR",
            Array,
            "An \"attribute='value'\" pair, mandatory attrs for creating new resource instances: "                  "[#{REQ_CREATE_ATTRS.join(', ')}]") do |attributes|
      options.attributes ||= Occi::Core::Attributes.new

      attributes.each do |attribute|
        key, value = Occi::Cli::OcciOpts::Helper.parse_attribute(attribute)
        options.attributes[key] = value
      end
    end

    opts.on("-T",
            "--context CTX_VAR",
            Array,
            "A \"context_variable='value'\" pair for new 'compute' resource instances, "                  "only: [#{Occi::Cli::OcciOpts::Helper::ALLOWED_CONTEXT_VARS.join(', ')}]") do |context|
      options.context_vars ||= {}

      context.each do |ctx|
        key, value = Occi::Cli::OcciOpts::Helper.parse_context_variable(ctx)
        options.context_vars[key] = value
      end
    end

    opts.on("-a",
            "--action ACTION",
            ACTIONS,
            "Action to be performed on a resource instance, required") do |action|
      options.action = action
    end

    opts.on("-M",
            "--mixin IDENTIFIER",
            Array,
            "Identifier of a mixin, formatted as SCHEME#TERM or SHORT_SCHEME#TERM") do |mixins|
      options.mixins ||= Occi::Core::Mixins.new

      mixins.each do |mixin|
        options.mixins << Occi::Cli::OcciOpts::Helper.parse_mixin(mixin)
      end
    end

    opts.on("-j",
            "--link URI",
            Array,
            "URI of an instance to be linked with the given resource, applicable only for action 'link'") do |links|
      options.links ||= []

      links.each do |link|
        link_relative_path = URI(link).path
        raise ArgumentError, "Specified link URI is not valid!" unless link_relative_path.start_with? '/'
        options.links << link_relative_path
      end
    end

    opts.on("-g",
            "--trigger-action ACTION",
            String,
            "Action to be triggered on the resource, formatted as SCHEME#TERM or TERM") do |trigger_action|
      options.trigger_action = Occi::Cli::OcciOpts::Helper.parse_action(trigger_action)
    end

    opts.on("-i",
            "--entity-type TYPE",
            ENTITY_TYPES,
            "Entity types to perform discovery on, only: [#{ENTITY_TYPES.join('|')}]") do |entity_type|
      options.entity_type = entity_type
    end

    opts.on("-l",
            "--log-to OUTPUT",
            LOG_OUTPUTS,
            "Log to the specified device, only: [#{LOG_OUTPUTS.join('|')}], defaults to 'stderr'") do |log_to|
      options.log.out = STDOUT if log_to.to_s == "stdout"
    end

    opts.on("-o",
            "--output-format FORMAT",
            Occi::Cli::ResourceOutputFactory.allowed_formats,
            "Output format, only: [#{Occi::Cli::ResourceOutputFactory.allowed_formats.join('|')}], "                  "defaults to #{options.output_format.to_s.inspect}") do |output_format|
      options.output_format = output_format
    end

    opts.on("-b",
            "--log-level LEVEL",
            LOG_LEVELS,
            "Set the specified logging level, only: [#{LOG_LEVELS.join('|')}]") do |log_level|
      unless options.log.level == Occi::Cli::Log::DEBUG
        options.log.level = Occi::Cli::Log.const_get(log_level.to_s.upcase)
      end
    end

    opts.on_tail("-z",
                 "--examples",
                 "Show usage examples") do |examples|
      if examples
        if @@quiet
          exit true
        else
          file = "#{File.expand_path('..', __FILE__)}/occi_opts/cli_examples.erb"
          template = ERB.new(File.new(file).read, nil, '-')

          puts template.result(binding)
          exit! true
        end
      end
    end

    opts.on_tail("-m",
                 "--dump-model",
                 "Contact the endpoint and dump its model") do |dump_model|
      options.dump_model = dump_model
    end

    opts.on_tail("-d",
                 "--debug",
                 "Enable debugging messages") do |debug|
      options.debug = debug
      options.log.level = Occi::Cli::Log::DEBUG
    end

    opts.on_tail("-h",
                 "--help",
                 "Show this message") do
      if @@quiet
        exit true
      else
        puts opts
        exit! true
      end
    end

    opts.on_tail("-v",
                 "--version",
                 "Show version") do
      if @@quiet
        exit true
      else
        if options.debug
          puts "CLI:  #{Occi::Cli::VERSION}"
          puts "API:  #{Occi::Api::VERSION}"
          puts "Core: #{Occi::VERSION}"
        else
          puts Occi::Cli::VERSION
        end
        exit! true
      end
    end
  end

  begin
    opts.parse!(args)
  rescue Exception => ex
    if @@quiet
      exit false
    else
      puts ex.message.capitalize
      puts opts
      exit!
    end
  end

  check_restrictions options, opts

  options
end

Private Class Methods

check_attributes(attributes, mandatory, opts) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 425
def self.check_attributes(attributes, mandatory, opts)
  missing = []
  attributes = Occi::Core::Attributes.new(attributes)

  mandatory.each do |attribute|
    begin
      attributes[attribute]
      raise Occi::Errors::AttributeMissingError,
            "Attribute #{attribute.inspect} is empty!" unless attributes[attribute]
    rescue Occi::Errors::AttributeMissingError
      missing << attribute
    end
  end

  report_missing missing, opts
end
check_hash(hash, mandatory, opts) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 416
def self.check_hash(hash, mandatory, opts)
  unless hash.is_a?(Hash)
    hash = hash.marshal_dump
  end

  missing = mandatory.select { |param| hash[param].blank? }
  report_missing missing, opts
end
check_incompatible_args(options, opts) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 366
def self.check_incompatible_args(options, opts)
  if !options.dump_model && options.filter
    if @@quiet
      exit false
    else
      puts "You cannot use '--filter' without '--dump-model'!"
      puts opts
      exit!
    end
  end

  if options.voms && options.auth.type != "x509"
    if @@quiet
      exit false
    else
      puts "You cannot use '--voms' without '--auth x509'!"
      puts opts
      exit!
    end
  end
end
check_restrictions(options, opts) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 355
def self.check_restrictions(options, opts)
  check_incompatible_args(options, opts)

  return if options.dump_model

  mandatory = get_mandatory_args(options)
  check_hash(options, mandatory, opts)

  check_attributes(options.attributes, REQ_CREATE_ATTRS, opts) if options.action == :create
end
get_mandatory_args(options) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 388
def self.get_mandatory_args(options)
  mandatory = []

  if options.action == :trigger
    mandatory << :trigger_action
  end

  if options.action == :create
    if options.mixins.blank? && options.resource == 'compute'
      mandatory << :links
    end

    mandatory << :attributes
  end

  if options.action == :link
    mandatory << :links
  end

  if options.action == :discover
    mandatory.concat [:entity_type]
  else
    mandatory.concat [:resource, :action]
  end

  mandatory
end
report_missing(missing, opts) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 442
def self.report_missing(missing, opts)
  unless missing.empty?
    if @@quiet
      exit false
    else
      puts "Missing required arguments: #{missing.join(', ')}"
      puts opts
      exit!
    end
  end
end
set_defaults(options) click to toggle source
# File lib/occi/cli/occi_opts.rb, line 320
def self.set_defaults(options)
  options.debug = false

  options.log = {}
  options.log.out = STDERR
  options.log.level = Occi::Cli::Log::ERROR

  options.filter = nil
  options.dump_model = false

  options.endpoint = "http://localhost:3000"
  options.timeout = nil

  options.auth = {}
  options.auth.type = "none"
  options.auth.user_cert = "#{ENV['HOME']}/.globus/usercred.pem"
  options.auth.ca_path = "/etc/grid-security/certificates"
  options.auth.username = "anonymous"
  options.auth.ca_file = nil
  options.auth.voms = nil

  options.output_format = :plain

  options.mixins = Occi::Core::Mixins.new
  options.links = nil
  options.attributes = Occi::Core::Attributes.new
  options.context_vars = nil

  # TODO: change media type back to occi+json after the rOCCI-server update
  #options.media_type = "application/occi+json"
  options.media_type = "text/plain,text/occi"

  options
end