Class: MCollective::DDL::AgentDDL

Inherits:
Base
  • Object
show all
Defined in:
lib/mcollective/ddl/agentddl.rb

Overview

A DDL class specific to agent plugins.

A full DDL can be seen below with all the possible bells and whistles present.

metadata :name => “Utilities and Helpers for SimpleRPC Agents”,

:description => "General helpful actions that expose stats and internals to SimpleRPC clients",
:author      => "R.I.Pienaar <[email protected]>",
:license     => "Apache License, Version 2.0",
:version     => "1.0",
:url         => "http://marionette-collective.org/",
:timeout     => 10

action “get_fact”, :description => “Retrieve a single fact from the fact store” do

 display :always

 input :fact,
       :prompt      => "The name of the fact",
       :description => "The fact to retrieve",
       :type        => :string,
       :validation  => '^[\w\-\.]+$',
       :optional    => false,
       :maxlength   => 40

 output :fact,
        :description => "The name of the fact being returned",
        :display_as  => "Fact"

 output :value,
        :description => "The value of the fact",
        :display_as  => "Value",
        :default     => ""

summarize do
    aggregate summary(:value)
end

end

Instance Attribute Summary

Attributes inherited from Base

#entities, #meta, #pluginname, #plugintype, #requirements, #usage

Instance Method Summary collapse

Methods inherited from Base

#findddlfile, #help, #loadddlfile, #metadata, #output, #requires, #template_for_plugintype, #validate_input_argument, #validate_requirements

Constructor Details

#initialize(plugin, plugintype = :agent, loadddl = true) ⇒ AgentDDL

Returns a new instance of AgentDDL.



40
41
42
43
44
# File 'lib/mcollective/ddl/agentddl.rb', line 40

def initialize(plugin, plugintype=:agent, loadddl=true)
  @process_aggregate_functions = nil

  super
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

If the method name matches a # aggregate function, we return the function with args as a hash. This will only be active if the @process_aggregate_functions is set to true which only happens in the #summarize block



134
135
136
137
138
139
140
# File 'lib/mcollective/ddl/agentddl.rb', line 134

def method_missing(name, *args, &block)
  unless @process_aggregate_functions || is_function?(name)
    raise NoMethodError, "undefined local variable or method `#{name}'", caller
  end

  return {:function => name, :args => args}
end

Instance Method Details

#action(name, input, &block) ⇒ Object

Creates the definition for an action, you can nest input definitions inside the action to attach inputs and validation to the actions

action "status", :description => "Restarts a Service" do
   display :always

   input  "service",
          :prompt      => "Service Action",
          :description => "The action to perform",
          :type        => :list,
          :optional    => true,
          :list        => ["start", "stop", "restart", "status"]

   output "status",
          :description => "The status of the service after the action"

end


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/mcollective/ddl/agentddl.rb', line 111

def action(name, input, &block)
  raise "Action needs a :description property" unless input.include?(:description)

  unless @entities.include?(name)
    @entities[name] = {}
    @entities[name][:action] = name
    @entities[name][:input] = {}
    @entities[name][:output] = {}
    @entities[name][:display] = :failed
    @entities[name][:description] = input[:description]
  end

  # if a block is passed it might be creating input methods, call it
  # we set @current_entity so the input block can know what its talking
  # to, this is probably an epic hack, need to improve.
  @current_entity = name
  block.call if block_given?
  @current_entity = nil
end

#action_interface(name) ⇒ Object

Returns the interface for a specific action



175
176
177
# File 'lib/mcollective/ddl/agentddl.rb', line 175

def action_interface(name)
  @entities[name] || {}
end

#actionsObject

Returns an array of actions this agent support



180
181
182
# File 'lib/mcollective/ddl/agentddl.rb', line 180

def actions
  @entities.keys
end

#aggregate(function, format = {:format => nil}) ⇒ Object

Sets the aggregate array for the given action

Raises:



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/mcollective/ddl/agentddl.rb', line 69

def aggregate(function, format = {:format => nil})
  raise(DDLValidationError, "Formats supplied to aggregation functions should be a hash") unless format.is_a?(Hash)
  raise(DDLValidationError, "Formats supplied to aggregation functions must have a :format key") unless format.keys.include?(:format)
  raise(DDLValidationError, "Functions supplied to aggregate should be a hash") unless function.is_a?(Hash)

  unless (function.keys.include?(:args)) && function[:args]
    raise DDLValidationError, "aggregate method for action '%s' missing a function parameter" % entities[@current_entity][:action]
  end

  entities[@current_entity][:aggregate] ||= []
  entities[@current_entity][:aggregate] << (format[:format].nil? ? function : function.merge(format))
end

#display(pref) ⇒ Object

Sets the display preference to either :ok, :failed, :flatten or :always operates on action level



84
85
86
87
88
89
90
91
92
# File 'lib/mcollective/ddl/agentddl.rb', line 84

def display(pref)
  # defaults to old behavior, complain if its supplied and invalid
  unless [:ok, :failed, :flatten, :always].include?(pref)
    raise "Display preference #{pref} is not valid, should be :ok, :failed, :flatten or :always"
  end

  action = @current_entity
  @entities[action][:display] = pref
end

#input(argument, properties) ⇒ Object



46
47
48
49
50
# File 'lib/mcollective/ddl/agentddl.rb', line 46

def input(argument, properties)
  raise "Input needs a :optional property" unless properties.include?(:optional)

  super
end

#is_function?(method_name) ⇒ Boolean

Checks if a method name matches a aggregate plugin. This is used by method missing so that we dont greedily assume that every method_missing call in an agent ddl has hit a aggregate function.

Returns:

  • (Boolean)


145
146
147
# File 'lib/mcollective/ddl/agentddl.rb', line 145

def is_function?(method_name)
  PluginManager.find("aggregate").include?(method_name.to_s)
end

#summarize(&block) ⇒ Object

Calls the summarize block defined in the ddl. Block will not be called if the ddl is getting processed on the server side. This means that aggregate plugins only have to be present on the client side.

The @process_aggregate_functions variable is used by the method_missing block to determine if it should kick in, this way we very tightly control where we activate the method_missing behavior turning it into a noop otherwise to maximise the chance of providing good user feedback



60
61
62
63
64
65
66
# File 'lib/mcollective/ddl/agentddl.rb', line 60

def summarize(&block)
  unless @config.mode == :server
    @process_aggregate_functions = true
    block.call
    @process_aggregate_functions = nil
  end
end

#validate_rpc_request(action, arguments) ⇒ Object

Helper to use the DDL to figure out if the remote call to an agent should be allowed based on action name and inputs.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/mcollective/ddl/agentddl.rb', line 151

def validate_rpc_request(action, arguments)
  # is the action known?
  unless actions.include?(action)
    raise DDLValidationError, "Attempted to call action #{action} for #{@pluginname} but it's not declared in the DDL"
  end

  input = action_interface(action)[:input] || {}

  input.keys.each do |key|
    unless input[key][:optional]
      unless arguments.keys.include?(key)
        raise DDLValidationError, "Action #{action} needs a #{key} argument"
      end
    end

    if arguments.keys.include?(key)
      validate_input_argument(input, key, arguments[key])
    end
  end

  true
end