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