wiki:PythonApiExamples
Last modified 6 years ago Last modified on 07/05/11 22:27:01

PythonApiExamples

The Python API allows for manipulation of a set of Func minions in the same way of the command line, with extended power for scripting, attaching calls to one another, and in general, neat shiny things. We hope you like it.

The Modules

Please also see the documentation on the specific modules (ex: VirtModule ). Each module has different types of return data structures, which, while simple, are rather different. This document will explain how to use the API and just one of the basic modules, for the other modules, see the respective module documentation -- or, the Python source to the modules. Either way.

The Client Object

Any program scripting a set of func minions starts off like this:

import func.overlord.client as fc

This just allows us to use the Client object later. Simple enough. The "overlord" is called an overlord because it has control over all the func minions.

The client object is constructed can be constructed in many ways. They all look kind of like this:

client = fc.Client("*.example.org")                          # all systems on example.org
client = fc.Client("*")                                      # all systems
client = fc.Client("*.example.org;*.example.net")            # all systems on example.org or example.net
client = fc.Client("pinky.example.org;brain.example.org")    # two specific systems
client = fc.Client("trogdor.example.org")                    # one specific system

So this isn't really just a "Client" module. It's a "Clients" kind of module. It allows treating all the func minions on the network as one, and addressing them in parallel. There are plenty of reasons you may not want to do this all of the time, but if you want to do this, func makes it easy and encourages it.

An astute developer may note that these look exactly like shell globs. That's because, with the exception of the ";" list syntax, they are. We may do regexes later, but shell globs are simple, and they provide most of the power we need.

Using the client object

Methods are called on the Client object exactly like you might use a XMLRPC proxy (if you know about those things) and feel just like using real (local) objects.

results = fc.Client("*.example.org").yumcmd.install("koan")

The above example would try to install koan on all of the systems on "example.org", using yum. Simple enough.

Return Codes

By default, the client object can be used to address multiple systems, so the returns need to seperate the values from the systems that returned them.

In the above example, results as a data structure might look like this:

results = {
   "foo.example.org"  : 0,
   "bar.example.org"  : 1,
   "baz.example.org"  : 0,
   "zzz.example.org"  : ExceptionObjectInstance
}

What's up with that?

  • foo.example.org and baz.example.org updated fine.
  • bar.example.org returned non-zero, which means something probably fouled up.
  • zzz.example.org had some other more serious error

We would hope in most situations you'd see mostly all 0 returns :)

If you do get any exceptions, they will be XMLRPCFault classes ... so you can see exactly what went wrong. If you just want to print them out, just run "str()" on them and you'll get the exact message. 9 times out of 10 it will be just "Connection failed" which means that either funcd isn't running on the remote box, or that it's unreachable/down/whatever.

You can test whether the client call result is an exception by using func.utils.is_error(). In the example above, only zzz.example.org's return would evaluate to true through the is_error() call. The values of 0 and 1 are up to the client program to determine meaning from.

Data Structures

In the above example, the yum module's "install" method has integer return codes.

In the real world though, a module could return something much more complex. They will always be primative data structures, so think of nested hashes, arrays of arrays, integers, strings, and so on. The only objects that will come back in a return code are exceptions.

A good example of this is the HardwareModule.

An easy way to see what this stuff looks like just to try it... using the Python shell (or better ... iPython) is a great way to do that.

print fc.Client("acme.example.org").hardware.info()

Direct Addressing

It's pretty easy to address all the systems you have at once, but what if you want to address just one system? You really don't need globbing and you don't need a hash to come back with all of your system names in it.

Solution:

print fc.Client("acme.example.org",noglobs=True).yumcmd.install("foopkg")

you'll see that the above returned a simple integer, as opposed to what you would noramlly get:

{
    "acme.example.org"  : 0
}

That may be a bit easier if you know you just want to address one system. Warning though: you can't use a glob for a server name when you are doing it this way.

Just for kicks

Here's an interesting and contrived example -- rebooting all systems that are running httpd. It's contrived, yes, but it's also very simple, thanks to func.

results = fc.Client("*").service.status("httpd")
for (host, returns) in results.iteritems():
   if returns == 0:
       fc.Client(host).reboot.reboot()

See, that's very short.

Hopefully this shows that anything is possible, however contrived.

Better Examples

There are some much more involved examples available, which are also involved in the source checkout in the "Examples" directory.

See ScriptingExamples.