Archive

Posts Tagged ‘Planet Python’

Securing sec-wall services using XPath with namespaces

June 1st, 2011 No comments

One of the things sec-wall, a featured-packed high performance security proxy, provides is the support for securing access to resources using arbitrary XPath expressions. What is currently missing in the documentation though is an explanation of how one should use XML namespaces. The thing can be done and there’s a bug report regarding it which I’m going to fix and close in a day or two but just thought that in the meantime I’d blog about it.

So how would one go about creating a sec-wall config.py file that should let in only clients that use credentials akin to what’s below?

<?xml version="1.0" encoding="utf-8"?>
<a>
    <b>
        <username xmlns="http://example.com/myns1">foo</username>
        <c xmlns="http://example.com/myns2" password="bar" />
    </b>
</a>

The answer is pretty simple – etree.XPath objects accept a namespaces argument which ought to be a mapping between prefixes used in expressions and actual namespaces, so the config file should read like below:

# -*- coding: utf-8 -*-
 
# stdlib
import uuid
 
# lxml
from lxml import etree
 
# Don't share it with anyone.
INSTANCE_SECRET = '7bcb90942d994440af05d02b691ae86d'
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
# ##############################################################################
 
def xpath():
 
    username = 'foo'
    password = 'bar'
 
    xpath1 = "/a/b/myns1:username/text() = '{0}'".format(username)
    xpath2 = "//myns2:c/@password='{0}'".format(password)
 
    ns_dict = {
        'myns1': 'http://example.com/myns1',
        'myns2': 'http://example.com/myns2',
    }
 
    return {
        'xpath': True,
        'xpath-1': etree.XPath(xpath1, namespaces=ns_dict),
        'xpath-2': etree.XPath(xpath2, namespaces=ns_dict),
        'host': 'http://example.com/',
    }
 
urls = [
    ('/xpath', xpath()),
]

Solid, eh? :-)

Share

PCF/MQAI filters with WebSphere MQ and Python

April 16th, 2011 2 comments

I’ve just pushed a new feature to the lp:~pymqi-dev/pymqi/string-filter branch that makes it possible to use WebSphere MQ PCF/MQAI filters with Python. That should be a nice addition if anyone’s thinking of creating cross-platform open-source equivalents of various Windows-only freeware support packs there are already available. Or just for anyone writing slightly more advanced code for administering MQ objects.

So how does it work?

You sure remember how to use PCFExecute, right? Well, the class has just been extended and now accepts a new parameter, a list of filters to use when querying MQ objects. The API has been more or less inspired by how Django’s QuerySets look like which basically means it’s a higher level one then what you’d be normally using with plain MQ verbs. See for yourself, the code below fetches all the queues that have a description starting with a ‘WebSphere MQ Selection*’ pattern and the current depth of 10 and greater:

import pymqi, CMQC, CMQCFC
 
queue_manager = 'QM01'
channel = 'SVRCONN.1'
host = '192.168.1.139'
port = '1434'
conn_info = '%s(%s)' % (host, port)
 
qmgr = pymqi.connect(queue_manager, channel, conn_info)
pcf = pymqi.PCFExecute(qmgr)
 
attrs = {CMQC.MQCA_Q_NAME :'*', CMQC.MQIA_Q_TYPE : CMQC.MQQT_LOCAL}
 
desc_filter = pymqi.Filter(CMQC.MQCA_Q_DESC).like('WebSphere MQ Selection*')
depth_filter = pymqi.Filter(CMQC.MQIA_CURRENT_Q_DEPTH).greater(10)
 
res = pcf.MQCMD_INQUIRE_Q(attrs, [desc_filter, depth_filter])
print(res)

It’s still a feature branch not trunk, and even if it were trunk it still hasn’t been released in PyMQI 1.3, and even then, there still would be PyMQI 1.4, so if you feel like anything should be changed or rather work differently, please speak up! Cheers!

Share

Securing sec-wall’s secrets with GnuPG and python-gnupg

April 11th, 2011 No comments

One of the niceties of sec-wall, the security proxy, is that it’s written in Python which means not only that one doesn’t have to learn yet another obscure configuration format summoned from the deepest levels of R’lyeh but also that it’s very easy to extend the config.py file.

For instance, by default all of the secrets need to be given in clear text and that can probably be sub-optimal for some people, so why don’t we customize the config file and have it use GnuPG with symmetric decryption via the python-gnupg wrapper?

Let’s install python-gnupg first (piece of cake):

$ sudo pip install python-gnupg

Let’s prepare the secret.ini file, an INI-formatted configuration file which contains all the secrets:

$ cat secret.ini
[secret]
INSTANCE_SECRET=756817c1e2b8443c9d6f728da9b2b5fd
admin=MySecretPassword
$

The file will be now encrypted using GnuPG (let’s say the password is ’123′) ..

$ gpg --symmetric secret.ini
$

.. the result of which is a new secret.ini.gpg file and secret.ini may now be removed:

$ rm secret.ini
$

OK, we need the accompanying config.py file now, just like the one below:

# -*- coding: utf-8 -*-
 
# stdlib
import cStringIO, getpass, sys, uuid
from ConfigParser import ConfigParser
 
# python-gnupg
import gnupg
 
# Where are we expecting the secrets to be.
gpg_file = open('./secret.ini.gpg', "rb")
 
passphrase = getpass.getpass('Enter passphrase (will not be echoed back): ')
 
gpg = gnupg.GPG()
ini_data = str(gpg.decrypt_file(gpg_file, passphrase=passphrase))
 
# Exit early on an incorrect passphrase.
if not ini_data:
    print("Incorrect passphrase.")
    sys.exit(1)
 
# The passphrase was OK, proceed on to parsing the INI data.
config = ConfigParser()
config.readfp(cStringIO.StringIO(ini_data))
 
# Use the GPG-encrypted data.
INSTANCE_SECRET = config.get('secret', 'INSTANCE_SECRET')
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
# ##############################################################################
 
def admin():
    return {
        'basic-auth': True,
        'basic-auth-username':'user',
        'basic-auth-password':config.get('secret', 'admin'), # Uses GPG now.
        'basic-auth-realm':'Secure area',
        'host': 'http://example.com'
    }
 
def default():
    return {
        'ssl': True,
        'ssl-cert': True,
        'ssl-cert-commonName':INSTANCE_SECRET,
        'host': 'http://' + INSTANCE_SECRET
    }
 
urls = [
    ('/admin/', admin()),
    ('/*', default()),
]

Try starting the proxy now. If everything’s OK, if the passphrase you’ve supplied has been the same as the one used for encrypting the original INI file, the sec-wall instance will start in background as usual:

$ sec-wall --start .
Enter passphrase (will not be echoed back):
$ echo $?
0
$

If on the other hand the password wasn’t ’123′, the proxy won’t start:

$ sec-wall --start .
Enter passphrase (will not be echoed back):
Incorrect passphrase.
$ echo $?
1
$

And that’s it! Couldn’t have been simpler, eh? :-)

Share

Announcing sec-wall 1.0.0, a feature packed high-performance security proxy

April 8th, 2011 No comments

This is mostly a copy of the announcement sent to the python-announce@ list, although I’ve added a couple more usage examples at the end of the post.

sec-wall is a feature packed high-performance security proxy which has many interesting features, including the support for SSL/TLS, WS-Security, HTTP Auth Basic/Digest, extensible authentication schemes based on custom HTTP headers and XPath expressions, powerful URL matching/rewriting and an optional headers enrichment.

sec-wall uses and is built on top of several fantastic Python open source technologies, such as gevent, Spring Python, Pesto, lxml, zdaemon or PyYAML and is meant to be highly customizable and easy to use. Good performance, tests, documentation and building an awesome community are at the very heart of the project.

Here are  examples showing how little is needed to secure a backend server with HTTP Basic Auth, SSL/TLS client certificates and WS-Security.

# -*- coding: utf-8 -*-
 
# stdlib
import uuid
 
# Don't share it with anyone.
INSTANCE_SECRET = '5bf4e78c256746eda2ce3e0e73f256d0'
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
def default():
    return {
        'basic-auth':True,
        'basic-auth-username':'MyUser',
        'basic-auth-password':'MySecret',
        'basic-auth-realm':'Secure area',
        'host': 'http://example.com'
    }
 
urls = [
    ('/*', default()),
]

# -*- coding: utf-8 -*-
 
# stdlib
import os.path as path, uuid
 
# Don't share it with anyone.
INSTANCE_SECRET = '5bf4e78c256746eda2ce3e0e73f256d0'
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
# Useful constants
cur_dir = path.dirname(__file__)
 
# Crypto
keyfile = path.join(cur_dir, './crypto/server-priv.pem')
certfile = path.join(cur_dir, './crypto/server-cert.pem')
ca_certs = path.join(cur_dir, './crypto/ca-cert.pem')
 
server_type = 'https'
 
def default():
    return {
        'ssl': True,
        'ssl-cert': True,
        'ssl-cert-commonName': 'My Client',
        'ssl-cert-organizationName': 'My Company',
        'host': 'http://example.com'
    }
 
urls = [
    ('/*', default()),
]

# -*- coding: utf-8 -*-
 
# stdlib
import uuid
 
# Don't share it with anyone.
INSTANCE_SECRET = '5bf4e78c256746eda2ce3e0e73f256d0'
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
def default():
    return {
        'wsse-pwd': True,
        'wsse-pwd-username': 'MyUser',
        'wsse-pwd-password': 'MySecret', # Needs to be given in clear text
        'wsse-pwd-reject-empty-nonce-creation': True,
        'wsse-pwd-reject-stale-tokens': True,
        'wsse-pwd-reject-expiry-limit': 120,
        'wsse-pwd-nonce-freshness-time': 120,
        'host': 'http://example.com'
    }
 
urls = [
    ('/*', default()),
]

Links:

Project’s homepage: http://sec-wall.gefira.pl/
Getting started: http://sec-wall.gefira.pl/documentation/getting-started/index.html
Usage examples: http://sec-wall.gefira.pl/documentation/usage-examples/index.html
Twitter: https://twitter.com/fourthrealm
Blog: http://www.gefira.pl/blog
IRC: #sec-wall channel on Freenode network

Cheers!

Share

PyMQI 1.2 has been released!

March 15th, 2011 3 comments

Woo hoo! PyMQI 1.2 – Python interface to WebSphere MQ – has been released and here’s an excerpt on new features taken from the release announcement:

  • Added support for MQ 7.0-style publish/subscribe
  • Added support for creating and parsing of MQRFH2 headers
  • Added new constants in the CMQZC.py module
  • Added new structures – MQSRO, MQSD, MQTM, MQTMC2
  • PyMQI now supports byte strings
  • New MQ verb – MQSUB
  • Simplified establishing connections to queue managers
  • Added means for checking whether a client application is connected to a queue manager

Special thanks to Hannes Wagener for his excellent contributions!

Links

Project’s homepage: http://packages.python.org/pymqi/
Download URL: https://launchpad.net/pymqi/+download
Usage examples: http://packages.python.org/pymqi/examples.html
Twitter: https://twitter.com/fourthrealm
Blog: http://www.gefira.pl/blog
LinkedIn: http://www.linkedin.com/groups?gid=3726448
IRC: #pymqi channel on Freenode network

Share

WebSphere MQ Administration with Python. Part I – introducing PCFExecute

March 10th, 2011 4 comments

Hi and welcome to the first installment in the WebSphere MQ Administration with Python series. The goal is to introduce readers to the topic of performing admin tasks with Python and encourage those who still waver over whether to migrate all those Visual Basic or Java tools to actually start using Python, a modern, fast and cross-platform programming language.

The Python interface to MQ is called PyMQI and among many goodies it provides there’s also a very useful pymqi.PCFExecute class. PCF is, as you surely remember, a short for Programmable Command Formats, which basically means it’s an API for writing programs that are meant to automate tasks MQ admins usually do.

How does it work in Python? Well, you just need to open the appropriate MQ manual and pick any PCF command, say an MQCMD_PING_Q_MGR. The name you’ve chosen is always available as an attribute of the PCFExecute class instances, like below:

import pymqi
 
queue_manager = "QM01"
channel = "SVRCONN.1"
host = "192.168.1.139"
port = "1434"
conn_info = "%s(%s)" % (host, port)
 
qmgr = pymqi.QueueManager(None)
qmgr.connectTCPClient(queue_manager, pymqi.cd(), channel, conn_info)
pcf = pymqi.PCFExecute(qmgr)
 
# Woohoo, we've just executed our first PCF command!
pcf.MQCMD_PING_Q_MGR()
 
qmgr.disconnect()

So you just need to connect to a queue manager, create an instance of the pymqi.PCFExecute class and then you can directly invoke anything that’s listed in MQ manuals.

OK, that was easy as pinging a queue manager doesn’t require any parameters nor does it return anything. How about something less trivial, like creating a new queue with MQCMD_CREATE_Q?

import CMQC
import pymqi
 
queue_manager = "QM01"
channel = "SVRCONN.1"
host = "192.168.1.139"
port = "1434"
conn_info = "%s(%s)" % (host, port)
 
qmgr = pymqi.QueueManager(None)
qmgr.connectTCPClient(queue_manager, pymqi.cd(), channel, conn_info)
pcf = pymqi.PCFExecute(qmgr)
 
queue_name = "PYMQI.1"
queue_type = CMQC.MQQT_LOCAL
 
args = {CMQC.MQCA_Q_NAME: queue_name, CMQC.MQIA_Q_TYPE: queue_type}
 
pcf.MQCMD_CREATE_Q(args)
 
qmgr.disconnect()

MQCMD_CREATE_Q obviously needs some arguments and these are passed in wrapped in a dictionary whose keys are the same as the manual says there should be. For instance, it says that MQCA_Q_NAME and MQIA_Q_TYPE are required identifiers so I knew I could use the PyMQI’s CMQC module for looking them up. Likewise, the manual lists all the supported queue types and they also can be found in CMQC and that’s how I knew I could use CMQC.MQQT_LOCAL for the queue type. So to recap, the arguments – required and optional – are explained in the MQ documentation and you need to use PyMQI constants to form a dictionary and pass it in to the PCF command of your choice. What other modules are there in addition to CMQC? Well, there’s also CMQXC and CMQCFC and PyMQI 1.2 will introduce CMQZC – they all may be used for constructing the arguments to PCF calls.

So here it is, an introduction to PCFExecute. I plan on writing more about manipulating MQ objects, queues, channels, queue managers, aliases and so on and if you’d like to have anything covered you just need to drop me an e-mail. Thanks!

Share

Message browsing with WebSphere MQ and PyMQI

February 21st, 2011 No comments

I’ve noticed quite a few people hitting this blog and looking for keywords along the lines of “pymqi read a message and leave on queue” so I thought it should be explained. The thing you’re looking for is called message browsing and this post will show it in action.

Before I get into details though, I’d like to ask you for contacting me if there’s anything I can do regarding the documentation and usage examples. Really, it’s open-source so both the code and docs should shine but I’ll never know what to improve next if you don’t tell me it first :-) Checking the referrers is of course important but it won’t help much if you shun sending me your suggestions. Thanks!

So, the code is below.  The trick is composed of two parts that sort of resemble using cursors in SQL databases:

  • The queue needs to be open for browsing using the CMQC.MQOO_BROWSE option,
  • When calling queue.get, you need to pass it a proper CMQC.MQGMO_BROWSE_* option, in our simple case, there’s only one message on the queue and it just so happens that right after opening the queue for browsing, the logical cursor used for browsing is placed before the first message so we can use CMQC.MQGMO_BROWSE_NEXT and it will browse the next message which happens to be the first and the only one on the queue.

The code has been written in a form of a test case in the hope that it will make it clear that msg1 and msg2 are really equal to self.expected_msg, which is a UUID4 so we know it’s a different message on each test invocation.

I think that would be it as far as explaining the concept goes and again, don’t hesitate to contact me if you’d like me to expand on it or maybe cover something entirely else.

# stdlib
import unittest, uuid
 
# PyMQI
import CMQC, pymqi
 
queue_manager = "QM01"
channel = "SVRCONN.1"
host = "192.168.1.139"
port = "1434"
queue_name = "TEST.1"
conn_info = "%s(%s)" % (host, port)
 
class BrowsingTestCase(unittest.TestCase):
    def setUp(self):
        self.expected_msg = uuid.uuid4().hex
        self.qmgr = pymqi.QueueManager(None)
        self.qmgr.connectTCPClient(queue_manager, pymqi.cd(), channel, conn_info)
 
        self.put_queue = pymqi.Queue(self.qmgr, queue_name)
        self.put_queue.put(self.expected_msg)
        self.put_queue.close()
 
        browse_options = CMQC.MQOO_BROWSE
        self.browse_queue = pymqi.Queue(self.qmgr, queue_name,  browse_options)
        self.get_queue = pymqi.Queue(self.qmgr, queue_name)
 
    def tearDown(self):
        # Clear the queue and close all objects.
        try:
            while True:
                self.get_queue.get()
        except pymqi.MQMIError, e:
            if e.comp != CMQC.MQCC_FAILED and e.reason != CMQC.MQRC_NO_MSG_AVAILABLE:
                raise
 
        self.browse_queue.close()
        self.get_queue.close()
        self.qmgr.disconnect()
 
    def test_message_browsing(self):
 
        md = pymqi.md()
        gmo = pymqi.gmo()
        gmo.Options = CMQC.MQGMO_BROWSE_NEXT
 
        msg1 = self.browse_queue.get(None, md, gmo)
        self.assertEquals(msg1, self.expected_msg)
 
        msg2 = self.get_queue.get()
        self.assertEquals(msg2, self.expected_msg)
Share

Spring Python 1.2 & SSL/TLS XML-RPC (a friendly reminder)

February 20th, 2011 Comments off

Spring Python 1.2 has been released some time ago and one of its newest features is the support for wrapping XML-RPC with SSL/TLS, including both clients and servers. I’ve covered it already in a tutorial some time ago but I don’t think Planet Python was picking up the blog’s posts at that time, besides, Spring Python was still a release candidate when I was writing it so things could’ve changed (but they didn’t :-) ). In any case, now that 1.2 is a stable release, I thought I’d just mention it in case you were looking for means to secure your XML-RPC traffic. Here’s the reference material and the tutorial is here.

Share

A quick intro to WebSphere MQ & Python programming

January 31st, 2011 Comments off

This post will show you the basic concepts pertaining to programming asynchronous applications with WebSphere MQ (‘MQ’ from now on) and Python. MQ, in case you didn’t know it,  is a proprietary piece of software by IBM which lets you easily design and create distributed applications utilising the popular messaging patterns – queues and topics. If you’ve ever heard of RabbitMQ, ZeroMQ or similar projects then you’ll find yourself comfortable with MQ right away. Only MQ is much older and proprietary though there’s a 90-day trial version you can download from IBM’s site. I don’t know about you but I stick to the opinion that one should usually know main opensource as well as closed applications in their domain so if you’re into asynchronous programming in general, you’ll most likely find MQ an interesting subject to learn. Python isn’t (yet?) an officially IBM-blessed language for MQ programming but PyMQI, the Python bindings, has been around for about a decade and it’s extremely well field-tested, so that’s what we’ll be using.

Let’s say we have a CRM and a pair of surrounding applications, an IVR system and a self-service portal. When a customer account’s data is being changed we need to notify the other systems and we’re to use MQ for the task. Note that in reality I’d likely use a middleware construct – such as an ESB – in the middle of it all to avoid the tight coupling between the apps but the post’s about MQ so let’s turn a blind eye to it for now. There are apps and queues, each client application – one to be notified – has its dedicated queue on which it listens for messages from the CRM.

The figure below depicts the communication paths; note that even though we can say that MQ notifies the IVR and a portal about the changes, it’s actually the case that both apps need to initiate a connection to MQ and listen for incoming messages hence the arrows point towards MQ, not the other way around.

Example MQ communication. CRM sends messages to IVR.1 and PORTAL.1 queues off which the messages are being read by the IVR and a portal, respectively.

Each particular arrow represents a connection through a channel. A channel is sort of a link and a gate through which messages are being sent. There’s one channel for each application but a single channel may be reused for sending messages to more than one queue as it’s the case with CRM which puts messages onto both client applications’ queues. It’s even more than that, a single channel may be used by more that one application and that’s what the code below does – not that it’s particularly safe or sound to do it but it’s there in case that’s how you’d like things be set up – and how channels can be secured with or without Python around is a topic for one of the next posts.

For the sake of the post let’s say our environment is comprised of the following MQ objects:

  • an MQ queue manager (that is, an MQ server) named QM01, running on host 192.168.1.139, listening for connections on port 1434
  • a channel for the CRM, called CRM.SVRCONN.1,
  • a channel for the IVR, called IVR.SVRCONN.1,
  • a channel for the portal, called PORTAL.SVRCONN.1,
  • a queue for the IVR, called IVR.1,
  • a queue for the portal, called PORTAL.1

The first thing we’ll do is to connect the CRM to the queue manager over its dedicated channel and put some messages onto queues:

# stdlib
import time
 
# PyMQI
import pymqi
 
queue_manager = 'QM01'
channel = 'SVRCONN.1'
host = '192.168.1.139'
port = '1434'
message = 'New data!'
queue_names = ['CRM.1', 'IVR.1']
conn_info = '%s(%s)' % (host, port)
 
qmgr = pymqi.QueueManager(None)
qmgr.connectTCPClient(queue_manager, pymqi.cd(), channel, conn_info)
 
queues = [pymqi.Queue(qmgr, name) for name in queue_names]
 
try:
    while True:
        for queue in queues:
            queue.put(message)
            time.sleep(1)
finally:
    for queue in queues:
        queue.close()
    qmgr.disconnect()

What we’ve just achieved is obtaining a connection to MQ through a TCP channel and adding a loop which makes sure messages are trickling into each of the client queues. Note the finally block which closes any objects used in the example. Always clean up after yourself :-)

OK, let’s add the receiving part now. That’s the same code for both IVR and a portal so the name of a queue to get messages off is read from the command line.

# stdlib
import sys, optparse # (I know there's argparse but I'm stuck with 2.6)
 
# PyMQI
import pymqi, CMQC
 
queue_manager = 'QM01'
channel = 'SVRCONN.1'
host = '192.168.1.139'
port = '1434'
conn_info = '%s(%s)' % (host, port)
 
# Read the queue name from the command line
parser = optparse.OptionParser()
parser.add_option("-q", dest="queue", help="Queue to get the messages off")
(options, args) = parser.parse_args()
 
if not options.queue:
    print("Forgot to specify the queue?")
    parser.print_help()
    sys.exit(1)
 
qmgr = pymqi.QueueManager(None)
qmgr.connectTCPClient(queue_manager, pymqi.cd(), channel, conn_info)
 
queue = pymqi.Queue(qmgr, options.queue)
 
# Message Descriptor
md = pymqi.md()
 
# Get Message Options
gmo = pymqi.gmo()
gmo.Options = CMQC.MQGMO_WAIT | CMQC.MQGMO_FAIL_IF_QUIESCING
gmo.WaitInterval = 5000 # 5 seconds
 
try:
    while True:
        try:
            # Wait up to to gmo.WaitInterval for a new message.
            message = queue.get(None, md, gmo)
 
            print(message)
 
            # Reset the MsgId, CorrelId &amp; GroupId so that we can reuse
            # the same 'md' object again.
            md.MsgId = CMQC.MQMI_NONE
            md.CorrelId = CMQC.MQCI_NONE
            md.GroupId = CMQC.MQGI_NONE
 
        except pymqi.MQMIError, e:
            if e.comp == CMQC.MQCC_FAILED and e.reason == CMQC.MQRC_NO_MSG_AVAILABLE:
                # No messages, that's OK, we can ignore it.
                pass
            else:
                # Some other error condition.
                raise
finally:
    queue.close()
    qmgr.disconnect()

I now feel the urge to mention that the client code above could’ve been written in a shorter – and  much a sloppier – way but I instead opted for showing you all the correct MQ programming patterns that make the code run with a speed close to that of a C MQ program with a minimal amount of RAM needed. That’s why the code may seem a little bit verbose but then again, even though I’m about as lazy as most Python programmers are, I don’t really have any problems with writing 3 lines of code instead of a single one if only the API doesn’t feel too contrived and baroque.

So what’s new in the client code anyway?

A Message Descriptor (MD) and Get Message Options (GMO) are one of the structures used by PyMQI to pass the relevant options to MQ layers below. That’s how you do programming with PyMQI  – and with MQ generally speaking – there are few concepts and verbs, like a queue, putting a message onto queue or getting it from it, but there are literally hundreds of customization options it all can be wrapped with. This particular GMO says that we don’t want to bail out immediately if there aren’t any messages on the queue (MQGMO_WAIT) and that we’re good MQ citizens and when the queue manager’s shutting down we will be disconnecting from it instead of leaving dangling half-open connections that prevent a queue manager from shutting down – and believe me that MQ admins will start loving you for using that option (MQGMO_FAIL_IF_QUIESCING). Then there’s gmo.WaitInterval which says how many milliseconds we intend to wait for a message before giving up and raising an exception as an indication that there has been none (for the curious one, PyMQI’s releasing GIL while waiting for messages or indeed, when calling any MQ API function).

Note there’s an inner try/except block now. What it does is catching any MQ exceptions and checking whether the exception raised isn’t trying to tell us that there have been no messages on a queue – that’s how it’s signalled to layers above, by raising an exception. The exception object, ‘e’, has two interesting properties – one is a completion code ‘.comp’, the latter one’s a reason code ‘.reason’. Basically, the completion code says if there was an error, a warning condition or if everything went fine. If there indeed was an error or a warning situation, the reason code is set to some meaningful value, such as MQRC_NO_MSG_AVAILABLE above which makes it obvious for us that well, no messages were found on a queue during that interval, and if so, we may safely ignore the exception and let it try getting the message again. In this example any other error condition is being re-raised though so that we know something went wrong. All the constants – and their meaning – can be looked up in IBM’s documentation, PyMQI is simply passing it on from MQ to our code.

As for resetting the IDs (MsgId, CorrelId and GroupId), the thing is that most options are passed to PyMQI and MQ by reference. In this context it means that some of the MD object’s attributes will be overwritten by MQ and it just so happens that

  • on a successful call to a .get, MQ sets .MsgId, .CorrelId and .GroupId values to those found in the message read,
  • we’re using the same MD object over and over in the loop,
  • if a call to a .get specifies an MD whose .MsgId, .CorrelId and .GroupId attributes are set to anything but their equivalent of None (MQMI_NONE, MQCI_NONE and MQGI_NONE respectively), MQ will be trying to get a message from a queue but only if that message’s attributes have values matching those set in the MD.

See where it takes us to? Hadn’t we been resetting the IDs, the first successful call to .get would’ve set the MD’s IDs for good and any subsequent call would’ve been trying to get new messages by those IDs, which – unless you’re trying hard, but what for – is guaranteed to never succeed, IDs are unique by default.

So that would be it as far as an introduction goes :-) I’m not even saying I haven’t covered everything about PyMQI because I’ve rather only mentioned a tiny little bit of what’s available to Python programmers. And there’s more, there’s Jython, Spring, Spring Python, JMS programming, MQ administration – PCF and MQAI, publish/subscribe and even more. Be sure to leave a comment or drop me an e-mail if there’s anything in particular you’d like to see covered!

Share

HOWTO: Installing Spring Python and its dependencies

December 17th, 2010 Comments off

Depending on your exact needs and the work environment, there are several ways to install Spring Python with its dependencies and this post will go through what’s available.

First thing is, install pip, this is the command line tool to use for installing Python software.

Here are the installation options:

  1. Telling pip to fetch it from PyPI and then install it, type:
    • pip install springpython, this will download the latest version – currently it’s 1.2.0RC1 – from the remote repository and install it
  2. Downloading the source installer from the Spring Source site manually – the link always points to the latest release of Spring Python though you can also pick from any previous releases. At the time of this writing, the latest release is springpython-1.2.0.RC1.tar.gz, place it in any directory and type pip install springpython-1.2.0.RC1.tar.gz to install it.
  3. Using the latest git version (read only) – create a directory, cd to it and type:
    • git clone git://git.springsource.org/spring-python/spring-python.git, this will create a local clone of the latest version of the source code
    • cd spring-python
    • python build.py –package, note the name of a tar.gz package that will be created, in my case it was springpython-1.3.0.BUILD-20101216115817
    • pip install target/artifacts/springpython-1.3.0.BUILD-20101216115817.tar.gz to install the newly built package
  4. Using the latest git version (read/write, for core developers) – same as the previous point except for the first command, its should be git clone git@git.springsource.org:spring-python/spring-python.git, note that the command is very similar to the previous one and for me personally it used to be a source of confusion, but that I guess is a git thing.
  5. Using the DEB installer created by Sven Wilhelm and Miguel Landaeta
    • Add deb http://ppa.wiredobjects.eu/springpython/ppa/debian experimental main to /etc/apt/sources.list
    • Issue sudo apt-get update && apt-get sudo install python-springpython to refresh the sources list and install Spring Python
    • Note there’s ‘debian’ in the name but I’ve had no problems with using the DEB on Ubuntu 10.04

A word on the project’s dependencies. The commands above will install Spring Python but it still doesn’t mean you’ll be able to make use of each and every piece of functionality. The installation methods mentioned above haven’t been (yet) broken into smaller ones, each of which could express its own concrete dependencies and on the other hand, adding a huge list of mandatory dependencies would clearly be an overkill, after all, not every project needs to access XML, Yaml, Oracle, Postgres, JMS WebSphere MQ, Pyro, Hessian, CherryPy and other external resources at the same time :-) So, once you install Spring Python, you still need to check the documentation for a list of things to install before you can start using a given Spring Python’s feature. Each chapter has a section devoted to listing all the dependencies or stating that a stock Python distribution will do, see for instance Security, AOP, IoC, JMS and Remoting.

I hope it sheds some light on the matter, in case you need any help, be sure to drop in the forum, mailing list or the IRC channel (#springpython on Freenode network). See you!

Share