diff --git a/moduledev.html b/moduledev.html new file mode 100644 index 00000000000..16ea2f1837c --- /dev/null +++ b/moduledev.html @@ -0,0 +1,416 @@ + + + + +
+ + +See also
+Ansible modules are reusable units of magic that can be used by the Ansible API, +or by the ansible or ansible-playbook programs.
+Modules can be written in any language and are found in the path specified +by ANSIBLE_LIBRARY_PATH or the –module-path command line option.
+Let’s build a module to get and set the system time. For starters, let’s build +a module that just outputs the current time.
+We are going to use Python here but any language is possible. Only File I/O and outputing to standard +out are required. So, bash, C++, clojure, Python, Ruby, whatever you want +is fine.
+It’s obvious that you would never really need to build a module to set the system time, +the ‘command’ module could already be used to do this. However, it makes for a decent example. +Reading the modules that come with ansible (linked above) is a great way to learn how to write +modules. Keep in mind, though, that some modules in ansible’s source tree are internalisms, +so look at service or yum, and don’t stare too close into things like async_wrapper or +you’ll turn to stone. Nobody ever executes async_wrapper directly.
+Ok, let’s get going with an example. We’ll use Python. For starters, save this as a file named time:
+#!/usr/bin/python
+
+import datetime
+import json
+
+date = str(datetime.datetime.now())
+print json.dumps({
+ "time" : date
+})
+
There’s a useful test script in the source checkout for ansible:
+git clone git@github.com:ansible/ansible.git
+chmod +x ansible/hacking/test-module
+Let’s run the script you just wrote with that:
+ansible/hacking/test-module ./time
+You should see output that looks something like this:
+{u'time': u'2012-03-14 22:13:48.539183'}
+
If you did not, you might have a typo in your module, so recheck it and try again
+Let’s modify the module to allow setting the current time. We’ll do this by seeing +if a key value pair in the form time=<string> is passed in to the module.
+Ansible internally saves arguments to a arguments file. So we must read the file +and parse it. The arguments file is just a string, so any form of arguments are legal. +Here we’ll do some basic parsing to treat the input as key=value.
+The example usage we are trying to achieve to set the time is:
+time time="March 14 22:10"
+If no time parameter is set, we’ll just leave the time as is and return the current time.
+Let’s look at the code. Read the comments as we’ll explain as we go. Note that this +highly verbose because it’s intended as an educational example. You can write modules +a lot shorter than this:
+#!/usr/bin/python
+
+# import some python modules that we'll use. These are all
+# available in Python's core
+
+import datetime
+import sys
+import json
+import os
+import shlex
+
+# read the argument string from the arguments file
+args_file = sys.argv[1]
+args_data = file(args_file).read()
+
+# for this module, we're going to do key=value style arguments
+# this is up to each module to decide what it wants, but all
+# core modules besides 'command' and 'shell' take key=value
+# so this is highly recommended
+
+arguments = shlex.split(args_data)
+for arg in arguments:
+
+ # ignore any arguments without an equals in it
+ if arg.find("=") != -1:
+
+ (key, value) = arg.split("=")
+
+ # if setting the time, the key 'time'
+ # will contain the value we want to set the time to
+
+ if key == "time":
+
+ # now we'll affect the change. Many modules
+ # will strive to be 'idempotent', meaning they
+ # will only make changes when the desired state
+ # expressed to the module does not match
+ # the current state. Look at 'service'
+ # or 'yum' in the main git tree for an example
+ # of how that might look.
+
+ rc = os.system("date -s \"%s\"" % value)
+
+ # always handle all possible errors
+ #
+ # when returning a failure, include 'failed'
+ # in the return data, and explain the failure
+ # in 'msg'. Both of these conventions are
+ # required however additional keys and values
+ # can be added.
+
+ if rc != 0:
+ print json.dumps({
+ "failed" : True,
+ "msg" : "failed setting the time"
+ })
+ sys.exit(1)
+
+ # when things do not fail, we do not
+ # have any restrictions on what kinds of
+ # data are returned, but it's always a
+ # good idea to include whether or not
+ # a change was made, as that will allow
+ # notifiers to be used in playbooks.
+
+ date = str(datetime.datetime.now())
+ print json.dumps({
+ "time" : date,
+ "changed" : True
+ })
+ sys.exit(0)
+
+# if no parameters are sent, the module may or
+# may not error out, this one will just
+# return the time
+
+date = str(datetime.datetime.now())
+print json.dumps({
+ "time" : date
+})
+
Let’s test that module:
+../ansible/hacking/test-module ./rst/time time=\"March 14 12:23\"
+This should return something like:
+{"changed": true, "time": "2012-03-14 12:23:00.000307"}
+
If writing a module in Python and you have managed nodes running +Python 2.4 or lower, this is generally a good idea, because +json isn’t in the Python standard library until 2.5.:
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
You should also never do this in a module:
+print "some status message"
+
Because the output is supposed to be valid JSON. Except that’s not quite true, +but we’ll get to that later.
+As a reminder from the example code above, here are some basic conventions +and guidelines:
+To make it easier to write modules in bash and in cases where a JSON +module might not be available, it is acceptable for a module to return +key=value output all on one line, like this. The Ansible parser +will know what to do.
+++somekey=1 somevalue=two favcolor=red
If you’re writing a module in Python or Ruby or whatever, though, returning +JSON is probably the simplest way to go.
+If you think your module is generally useful to others, Ansible is preparing +an ‘ansible-contrib’ repo. Stop by the mailing list and we’ll help you to +get your module included. Contrib modules can be implemented in a variety +of languages. Including a README with your module is a good idea so folks +can understand what arguments it takes and so on. We would like to build +up as many of these as possible in as many languages as possible.
+ +High-quality modules with minimal dependencies +can be included in the core, but core modules (just due to the programming +preferences of the developers) will need to be implemented in Python. +Stop by the mailing list to inquire about requirements.
+