Last week CloudBees hosted its biggest customer summit ever, discussing real-world implementation of Continuous Delivery and ways to accelerate your software pipeline. It was an exciting and educational experience for customers and EC staff. A few customers approached me with the question, "We're not big on Perl. Are there any plans to develop a Python interface to the Commander server?" This question has come up every year at the customer summit, and the answer has always been the same: "I'm sorry but there are many other features with broader impact that rank higher in priority"... until now! It gives me great joy to introduce the ElectricCommander Python Module 0.1.0! This module (ec.py) contains an ElectricCommander class which is the analog to the ElectricCommander Perl module.
Download and Installation
Download the module and the httplib2 module ec-0.1.1 httplib2-0.7.1. Each module is shipped as a distutils package. To install, unpack the appropriate archive, and issue the following command:
python setup.py install
Usage
To issue a request, call a method on the ElectricCommander object with a dictionary of request parameters. Here's a simple example for retrieving a property:
from ec import ElectricCommander cmdr = ElectricCommander() xml = cmdr.getProperty(dict(propertyName='/server/myprop')) ... process the xml result how you normally would ...
Login is special since it needs to update the active session in the ElectricCommander object. Failed login raises an exception. Note that the request parameters aren't in a dictionary for this api:
try: xml = cmdr.login('user1', 'pass1') except Exception as inst: print(inst)
You can create multiple ElectricCommander objects to connect to multiple servers:
cmdr2 = ElectricCommander('server2', user='user1') ... issue requests on the cmdr2 object ...
Here's how you can issue a findObjects request for projects whose names begin with "D" or "E":
print(cmdr.findObjects(dict( objectType = 'project', filter = dict( operator = 'or', filter = ))))
Here's how you can issue a batch request, in parallel mode:
print(cmdr.httpPost(cmdr.makeEnvelope( cmdr.createRequest('getProperty', dict(propertyName='/server/myprop')) + cmdr.createRequest('getServerStatus'), 'parallel')))
Here's how to set a job property from within a job step:
from os import environ print(cmdr.setProperty(dict( propertyName = '/myJob/prop1', value = '5', jobStepId = environ)))
Prerequisites
Python2 or Python3?
Python doesn't come with an http module that does connection caching, so I wrote ec.py to rely on httplib2 . httplib2 in turn has a bug when run on Python 3.2.2 such that it doesn't issue https requests properly. I tweaked the module to work around the issue and ran into a core Python 3.2.2 issue (which is actually an unresolved issue in OpenSSL). It does issue plain http requests properly, however. httplib2 works fine on Python 2.7.2 for http and https. ec.py does not work on Python 2.5.x, but both work on Python 2.6. Recommendation: Use Python 2.7.x or as close a version to it as possible.
httplib2
As was mentioned earlier, ec.py relies on httplib2 . I've attached httplib-0.7.1 to this blog post.
Behavior Differences from ElectricCommander.pm
There are a few differences between ec.py and ElectricCommander.pm.
ec.py doesn't have a concept of positional arguments for api calls (except login); you must always specify request parameters in a dictionary.
ec.py returns responses as xml strings, while ElectricCommander.pm returns an xpath object. Here are a few reasons for choosing to return a response string:
Python's standard library doesn't include an xpath module/class. The publicly available PyXML package supports xpath, but it is not a pure Python package, so we'd have to build it for every platform that a user wants to use ec.py. Too onerous.
For large responses, you may want to have the option of using a SAX parser to parse the result rather than using a DOM parser (required for xpath queries). DOM parsers can create huge data structures in memory for the DOM representation of xml.
ec.py does no argument validation. You can set any request parameters on any request (including non-existent request names). The module will create the representative request xml, send it to the server, and the server will perform validation and reject invalid requests. This has the side-effect that ec.py is somewhat future-proof -- if new server api's become available in the next version of ElectricCommander, you need not upgrade your ec.py to use them.
Known Limitations
It doesn't automatically tack on context arguments (e.g. jobStepId for property api calls when run in a job-step context).
It does not update the session file. However, it does read it and respects the default session if one exists.
It doesn't have a batch api, but you can use the public methods to issue batch requests yourself (as shown in the example above).
It has no retry behavior for failed requests.
Functions from the Perl API that are more than just wrappers around issuing server requests are not implemented. This includes (but is not limited to) installPlugin, uninstallPlugin, publishArtifactVersion, and retrieveArtifactVersions.
Legal (non)Restrictions
This module is free for use. Modify it however you see fit to better your experience using ElectricCommander. I'd be happy to incorporate updates into my controller. This module is not officially supported by CloudBees. It has undergone no formal testing and you may run into issues that I haven't in my manual testing. Post your questions/comments, and I'll respond when I get a chance.
Future Directions
Since I've just learned Python, most likely some of the module implementation and interface could be improved. I can't promise backward-compatibility, but I will document backward-incompatible changes so it'll be fairly easy for you to update your scripts to work with the new version of ec.py.