Update code from https://github.com/dmi-try/marionette-collective
[packages/precise/mcollective.git] / website / reference / plugins / data.md
1 ---
2 layout: default
3 title: Data Plugins
4 ---
5 [DDL]: /mcollective/reference/plugins/ddl.html
6 [DiscoveryPlugins]: /mcollective/reference/plugins/discovery.html
7
8 ## Overview
9 Up to MCollective 2.0 the discovery system could only discover against
10 installed agents, configuration management classes or facts and the node
11 identities. We're extending this to support discovery against many
12 sources through a simple plugin system.
13
14 *NOTE:* This feature is available since version 2.1.0
15
16 The basic idea is that you could do discovery statements like the ones
17 below:
18
19 {% highlight console %}
20 % mco find -S "fstat('/etc/rsyslog.conf').md5=/4edff591f6e38/"
21 {% endhighlight %}
22
23 {% highlight console %}
24 % mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1"
25 {% endhighlight %}
26
27 {% highlight console %}
28 % mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1 and % location=dc1"
29 {% endhighlight %}
30
31 You could also use these data sources in your own agents or other
32 plugins:
33
34 {% highlight ruby %}
35 action "query" do
36    reply[:value] = Data.sysctl(request[:sysctl_name]).value
37 end
38 {% endhighlight %}
39
40 *NOTE:* As opposed to the [DiscoveryPlugins] which are used by the client   
41 to communicate to the nodes using direct addressing, data plugins on the other   
42 hand refer to data that the nodes can provide, and hence this uses the normal  
43 broadcast discovery paradigm.   
44
45 These new data sources are plugins so you can provide via the plugin
46 system and they require DDL documents.  The DDL will be used on both the
47 client and the server to provide strict validation and configuration.
48
49 The DDL for these plugins will affect the client libraries in the
50 following ways:
51
52  * You will get errors if you try to discover using unknown functions
53  * Your input argument values will be validated against the DDL
54  * You will only be able to use output properties that are known in the DDL
55  * If a plugin DDL says it needs 5 seconds to run your discovery and maximum run times will increase by 5 seconds automatically
56
57 On the servers the DDL will:
58
59  * be used to validate known plugins
60  * be used to validate input arguments
61  * be used to validate requests for known output values
62
63 ## Viewing or retrieving results from a data plugin
64
65 You can view the output from a data plugin using the *rpcutil* agent:
66
67 {% highlight console %}
68 % mco rpc rpcutil get_data source=fstat query=/etc/hosts
69 .
70 .
71 your.node.net
72            atime: 2012-06-14 21:41:54
73        atime_age: 54128
74    atime_seconds: 1339706514
75            ctime: 2012-01-18 20:28:34
76        ctime_age: 12842128
77    ctime_seconds: 1326918514
78              gid: 0
79              md5: 54fb6627dbaa37721048e4549db3224d
80             mode: 100644
81            mtime: 2010-01-12 13:28:22
82        mtime_age: 76457740
83    mtime_seconds: 1263302902
84             name: /etc/hosts
85           output: present
86          present: 1
87             size: 158
88             type: file
89              uid: 0
90 {% endhighlight %}
91
92 The same action can be used to retrieve data programatically.
93
94 ## Writing a data plugin
95
96 ### The Ruby logic for the plugin
97 The data plugins should not change the system in anyway, you should take
98 care to create plugins that only reads the state of the system.  If you
99 want to affect the status of the system you should write Agents.
100
101 These plugins are kept simple as they will be typed on the command line
102 so the following restrictions are present:
103
104  * They can only take 1 input argument
105  * They can only return simple String, Numeric or Booleans no Hashes or complex data types
106  * They should be fast as these will impact discovery times and agent run times.
107
108 Writing data plugins is easy and mimic the basics of writing agents,
109 below we have a simple *sysctl* plugin that was used in the examples
110 earlier:
111
112 {% highlight ruby linenos %}
113 module MCollective
114   module Data
115     class Sysctl_data<Base
116       activate_when { File.executable?("/sbin/sysctl") && Facter["kernel"] == "Linux" }
117
118       query do |sysctl|
119         shell = Shell.new("/sbin/sysctl %s" % sysctl)
120         shell.runcommand
121
122         if shell.status.exitstatus == 0
123           value = shell.stdout.chomp.split(/\s*=\s*/)[1]
124
125           if value
126             value = Integer(value) if value =~ /^\d+$/
127             value = Float(value) if value =~ /^\d+\.\d+$/
128           end
129
130           result[:value] = value
131         end
132       end
133     end
134   end
135 end
136 {% endhighlight %}
137
138 The class names have to be *Something_data* and they must inherit from
139 *Base* as in the example here. The file would be saved in the *libdir*
140 as *data/sysctl_data.rb* and *data/sysctl_data.ddl*.
141
142 This plugin will only be activated if the file */sbin/sysctl* exist, is
143 executable and if the system is a Linux server. This allow us to install
144 it on a Windows machine where it will just be disabled and those
145 machines will never be discovered using this function.
146
147 We then create a block that would be the main body of the query.  We use
148 the *MCollective::Shell* class to run sysctl, parse the output and save
149 it into the *result* hash.
150
151 The result hash is the only way to return values from these plugins. You
152 can only save simple strings, numbers or booleans in the result.
153
154 ### The DDL for the plugin
155 As mentioned every data plugin requires a DDL.  These DDL files mimic
156 those of the [SimpleRPC Agents][DDL].
157
158 Below you'll find a DDL for the above sysctl data plugin:
159
160 {% highlight ruby linenos %}
161 metadata    :name        => "Sysctl values",
162             :description => "Retrieve values for a given sysctl",
163             :author      => "R.I.Pienaar <rip@devco.net>",
164             :license     => "ASL 2.0",
165             :version     => "1.0",
166             :url         => "http://marionette-collective.org/",
167             :timeout     => 1
168
169 dataquery :description => "Sysctl values" do
170     input :query,
171           :prompt => "Variable Name",
172           :description => "Valid Variable Name",
173           :type => :string,
174           :validation => /^[\w\-\.]+$/,
175           :maxlength => 120
176
177     output :value,
178            :description => "Kernel Parameter Value",
179            :display_as => "Value"
180 end
181 {% endhighlight %}
182
183 The *timeout* must be set correctly, if your data source is slow you
184 need to reflect that in the timeout here.  The timeout will be used on
185 the clients to decide how long to wait for discovery responses from the
186 network so getting this wrong will result in nodes not being discovered.
187
188 Each data plugin can only have one *dataquery* block with exactly 1
189 *input* block but could have multiple *output* blocks.
190
191 It's important to get the validation correct, here we only accept the
192 characters we know are legal in sysctl variables on Linux.  We will
193 specifically never allow backticks to be used in arguments to avoid
194 accidental shell exploits.
195
196 Note the correlation between output names and the use in discovery and
197 agents here we create an output called *value* this means we would use
198 it in discovery as:
199
200 {% highlight console %}
201 % mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1"
202 {% endhighlight %}
203
204 And we would output the result from our plugin code as:
205
206 {% highlight ruby linenos %}
207 result[:value] = value
208 {% endhighlight %}
209
210 And in any agent where we might use the data source:
211
212 {% highlight ruby linenos %}
213 something = Data.sysctl('net.ipv4.conf.all.forwarding').value
214 {% endhighlight %}
215
216 These have to match everywhere, you cannot reference undeclared data and
217 you cannot use input that does not validate against the DDL declared
218 validations.
219
220 Refer to the full [DDL] documentation for details on all possible values
221 of the *metadata*, *input* and *output* blocks.
222
223 ## Auto generated documentation
224 As with agents the DDL can be used to generate documentation, if you
225 wanted to know what the input and output values are for a specific
226 plugin you can use *mco plugin doc* to see generated documentation.
227
228 {% highlight console %}
229 % mco plugin doc sysctl
230 Sysctl values
231 =============
232
233 Retrieve values for a given sysctl
234
235       Author: R.I.Pienaar <rip@devco.net>
236      Version: 1.0
237      License: ASL 2.0
238      Timeout: 1
239    Home Page: http://marionette-collective.org/
240
241 QUERY FUNCTION INPUT:
242
243               Description: Valid Variable Name
244                    Prompt: Variable Name
245                      Type: string
246                Validation: (?-mix:^[\w\-\.]+$)
247                    Length: 120
248
249 QUERY FUNCTION OUTPUT:
250
251            value:
252               Description: Kernel Parameter Value
253                Display As: Value
254
255 {% endhighlight %}
256
257 ## Available plugins for a node You can use the *mco inventory*
258 application to see remotely what plugins a node has available:
259
260 {% highlight console %}
261 % mco inventory your.node
262 Inventory for your.node:
263
264    .
265    .
266    .
267
268    Data Plugins:
269       fstat           sysctl
270
271 {% endhighlight %}