Class: MCollective::RPC::DDL
- Inherits:
-
Object
- Object
- MCollective::RPC::DDL
- Defined in:
- lib/mcollective/rpc/ddl.rb
Overview
A class that helps creating data description language files for agents. You can define meta data, actions, input and output describing the behavior of your agent.
Later you can access this information to assist with creating of user interfaces or online help
A sample DDL can be seen below, you’d put this in your agent dir as <agent name>.ddl
:name => "SimpleRPC Service Agent",
:description => "Agent to manage services using the Puppet service provider",
:author => "R.I.Pienaar",
:license => "GPLv2",
:version => "1.1",
:url => "http://mcollective-plugins.googlecode.com/",
:timeout => 60
action "status", :description => "Gets the status of a service" do
display :always
input "service",
:prompt => "Service Name",
:description => "The service to get the status for",
:type => :string,
:validation => '^[a-zA-Z\-_\d]+$',
:optional => true,
:maxlength => 30
output "status",
:description => "The status of service",
:display_as => "Service Status"
end
Instance Attribute Summary collapse
-
#meta ⇒ Object
readonly
Returns the value of attribute meta.
Instance Method Summary collapse
-
#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_interface(name) ⇒ Object
Returns the interface for a specific action.
-
#actions ⇒ Object
Returns an array of actions this agent support.
-
#display(pref) ⇒ Object
Sets the display preference to either :ok, :failed, :flatten or :always operates on action level.
- #findddlfile(agent) ⇒ Object
-
#help(template) ⇒ Object
Generates help using the template based on the data created with metadata and input.
-
#initialize(agent, loadddl = true) ⇒ DDL
constructor
A new instance of DDL.
-
#input(argument, properties) ⇒ Object
Registers an input argument for a given action.
-
#metadata(meta) ⇒ Object
Registers meta data for the introspection hash.
-
#output(argument, properties) ⇒ Object
Registers an output argument for a given action.
-
#validate_request(action, arguments) ⇒ Object
Helper to use the DDL to figure out if the remote call should be allowed based on action name and inputs.
Constructor Details
#initialize(agent, loadddl = true) ⇒ DDL
Returns a new instance of DDL.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/mcollective/rpc/ddl.rb', line 39 def initialize(agent, loadddl=true) @actions = {} @meta = {} @config = MCollective::Config.instance @agent = agent if loadddl if ddlfile = findddlfile(agent) instance_eval(File.read(ddlfile)) else raise("Can't find DDL for agent '#{agent}'") end end end |
Instance Attribute Details
#meta ⇒ Object (readonly)
Returns the value of attribute meta.
37 38 39 |
# File 'lib/mcollective/rpc/ddl.rb', line 37 def @meta 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
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/mcollective/rpc/ddl.rb', line 92 def action(name, input, &block) raise "Action needs a :description" unless input.include?(:description) unless @actions.include?(name) @actions[name] = {} @actions[name][:action] = name @actions[name][:input] = {} @actions[name][:output] = {} @actions[name][:display] = :failed @actions[name][:description] = input[:description] end # if a block is passed it might be creating input methods, call it # we set @current_action so the input block can know what its talking # to, this is probably an epic hack, need to improve. @current_action = name block.call if block_given? @current_action = nil end |
#action_interface(name) ⇒ Object
Returns the interface for a specific action
187 188 189 |
# File 'lib/mcollective/rpc/ddl.rb', line 187 def action_interface(name) @actions[name] || {} end |
#actions ⇒ Object
Returns an array of actions this agent support
182 183 184 |
# File 'lib/mcollective/rpc/ddl.rb', line 182 def actions @actions.keys end |
#display(pref) ⇒ Object
Sets the display preference to either :ok, :failed, :flatten or :always operates on action level
160 161 162 163 164 165 166 167 168 |
# File 'lib/mcollective/rpc/ddl.rb', line 160 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_action @actions[action][:display] = pref end |
#findddlfile(agent) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/mcollective/rpc/ddl.rb', line 54 def findddlfile(agent) @config.libdir.each do |libdir| ddlfile = File.join([libdir, "mcollective", "agent", "#{agent}.ddl"]) if File.exist?(ddlfile) Log.debug("Found #{agent} ddl at #{ddlfile}") return ddlfile end end return false end |
#help(template) ⇒ Object
Generates help using the template based on the data created with metadata and input
172 173 174 175 176 177 178 179 |
# File 'lib/mcollective/rpc/ddl.rb', line 172 def help(template) template = IO.read(template) = @meta actions = @actions erb = ERB.new(template, 0, '%') erb.result(binding) end |
#input(argument, properties) ⇒ Object
Registers an input argument for a given action
See the documentation for action for how to use this
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/mcollective/rpc/ddl.rb', line 115 def input(argument, properties) raise "Cannot figure out what action input #{argument} belongs to" unless @current_action action = @current_action [:prompt, :description, :type, :optional].each do |arg| raise "Input needs a :#{arg}" unless properties.include?(arg) end @actions[action][:input][argument] = {:prompt => properties[:prompt], :description => properties[:description], :type => properties[:type], :optional => properties[:optional]} case properties[:type] when :string raise "Input type :string needs a :validation argument" unless properties.include?(:validation) raise "Input type :string needs a :maxlength argument" unless properties.include?(:maxlength) @actions[action][:input][argument][:validation] = properties[:validation] @actions[action][:input][argument][:maxlength] = properties[:maxlength] when :list raise "Input type :list needs a :list argument" unless properties.include?(:list) @actions[action][:input][argument][:list] = properties[:list] end end |
#metadata(meta) ⇒ Object
Registers meta data for the introspection hash
67 68 69 70 71 72 73 |
# File 'lib/mcollective/rpc/ddl.rb', line 67 def () [:name, :description, :author, :license, :version, :url, :timeout].each do |arg| raise "Metadata needs a :#{arg}" unless .include?(arg) end @meta = end |
#output(argument, properties) ⇒ Object
Registers an output argument for a given action
See the documentation for action for how to use this
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/mcollective/rpc/ddl.rb', line 147 def output(argument, properties) raise "Cannot figure out what action input #{argument} belongs to" unless @current_action raise "Output #{argument} needs a description argument" unless properties.include?(:description) raise "Output #{argument} needs a display_as argument" unless properties.include?(:display_as) action = @current_action @actions[action][:output][argument] = {:description => properties[:description], :display_as => properties[:display_as]} end |
#validate_request(action, arguments) ⇒ Object
Helper to use the DDL to figure out if the remote call should be allowed based on action name and inputs.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/mcollective/rpc/ddl.rb', line 193 def validate_request(action, arguments) # is the action known? unless actions.include?(action) raise DDLValidationError, "Attempted to call action #{action} for #{@agent} 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 # validate strings, lists and booleans, we'll add more types of validators when # all the use cases are clear # # only does validation for arguments actually given, since some might # be optional. We validate the presense of the argument earlier so # this is a safe assumption, just to skip them. # # :string can have maxlength and regex. A maxlength of 0 will bypasss checks # :list has a array of valid values if arguments.keys.include?(key) case input[key][:type] when :string raise DDLValidationError, "Input #{key} should be a string" unless arguments[key].is_a?(String) if input[key][:maxlength].to_i > 0 if arguments[key].size > input[key][:maxlength].to_i raise DDLValidationError, "Input #{key} is longer than #{input[key][:maxlength]} character(s)" end end unless arguments[key].match(Regexp.new(input[key][:validation])) raise DDLValidationError, "Input #{key} does not match validation regex #{input[key][:validation]}" end when :list unless input[key][:list].include?(arguments[key]) raise DDLValidationError, "Input #{key} doesn't match list #{input[key][:list].join(', ')}" end when :boolean unless [TrueClass, FalseClass].include?(arguments[key].class) raise DDLValidationError, "Input #{key} should be a boolean" end when :integer raise DDLValidationError, "Input #{key} should be a integer" unless arguments[key].is_a?(Fixnum) when :float raise DDLValidationError, "Input #{key} should be a floating point number" unless arguments[key].is_a?(Float) when :number raise DDLValidationError, "Input #{key} should be a number" unless arguments[key].is_a?(Numeric) end end end true end |