Search code examples
pythonsslcassandraeventlet

Does anybody have ssl working with the Python Cassandra driver and eventlet?


I am stuck getting ssl to work with the Python Cassandra driver and eventlet. We are using Python 3.4, eventlet 18.3 and Cassandra driver 3.0.0. Eventlet without ssl and ssl without eventlet both work.

Has anybody got the combination of ssl, cassandra and eventlet to work with Python? If so, what versions?

A code example may be too much to ask for, but would be very helpful.

Followup Feb 18 2016: Sorry for being so terse. Here is some code, all with Python3:

First, the simplest possible Cassandra client. No eventlet, no ssl. It works:

from cassandra.cluster import Cluster
cluster = Cluster(contact_points=['<ip>'],
                  connection_class=None)
session = cluster.connect('<keyspace>')
print("OK, session:", session)

Next, eventlet. No threads, so eventlet is pointless here. But it works:

from cassandra.cluster import Cluster
from cassandra.io.eventletreactor import EventletConnection
cluster = Cluster(contact_points=['<ip>'],
                  connection_class=EventletConnection)
session = cluster.connect('<keyspace>')
print("OK, session:", session)

Next, ssl, no eventlet. This also works:

import ssl
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(contact_points=['<ip>'],
                  connection_class=None,
                  ssl_options=dict(ca_certs='<certfile>',
                                   cert_reqs=ssl.CERT_REQUIRED,
                                   ssl_version=ssl.PROTOCOL_TLSv1),
                  auth_provider=PlainTextAuthProvider(username='<user>',
                                                      password='<pass>'))
session = cluster.connect('<keyspace>')
print("OK, session:", session)

Finally, ssl and eventlet. This fails:

import ssl
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
from cassandra.io.eventletreactor import EventletConnection
cluster = Cluster(contact_points=['<ip>'],
                  connection_class=EventletConnection,
                  ssl_options=dict(ca_certs='<certfile>',
                                   cert_reqs=ssl.CERT_REQUIRED,
                                   ssl_version=ssl.PROTOCOL_TLSv1),
                  auth_provider=PlainTextAuthProvider(username='<user>',
                                                      password='<pass>'))
session = cluster.connect('<keyspace>')
print("OK, session:", session)

The backtrace shows that we are using eventlet.green.ssl:

Traceback (most recent call last):
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/hubs/poll.py", line 115, in wait
    listener.cb(fileno)
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/green/select.py", line 55, in on_read
    current.switch(([original], [], []))
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/greenthread.py", line 214, in main
    result = function(*args, **kwargs)
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/cassandra/io/eventletreactor.py", line 98, in <lambda>
    self._read_watcher = eventlet.spawn(lambda: self.handle_read())
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/cassandra/io/eventletreactor.py", line 153, in handle_read
    buf = self._socket.recv(self.in_buffer_size)
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/green/ssl.py", line 198, in recv
    read = self.read(buflen)
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/green/ssl.py", line 138, in read
    super(GreenSSLSocket, self).read, *args, **kwargs)
  File "/home/jk/eventlet/venv3/lib/python3.5/site-packages/eventlet/green/ssl.py", line 112, in _call_trampolining
    return func(*a, **kw)
  File "/home/jk/python-org/dst/python-3.5.1/lib/python3.5/ssl.py", line 786, in read
    return self._sslobj.read(len, buffer)
TypeError: must be read-write bytes-like object, not None
Removing descriptor: 5
Traceback (most recent call last):
  File "so4.py", line 13, in <module>
    session = cluster.connect('<keyspace>')
  File "cassandra/cluster.py", line 824, in cassandra.cluster.Cluster.connect (cassandra/cluster.c:11354)
  File "cassandra/cluster.py", line 850, in cassandra.cluster.Cluster.connect (cassandra/cluster.c:11176)
  File "cassandra/cluster.py", line 844, in cassandra.cluster.Cluster.connect (cassandra/cluster.c:11056)
  File "cassandra/cluster.py", line 2041, in cassandra.cluster.ControlConnection.connect (cassandra/cluster.c:36224)
  File "cassandra/cluster.py", line 2076, in cassandra.cluster.ControlConnection._reconnect_internal (cassandra/cluster.c:37080)
cassandra.cluster.NoHostAvailable: ('Unable to connect to any servers', {'<ip>': OperationTimedOut('errors=Timed out creating connection (5 seconds), last_host=None',)})

Again, no actual threads. But our real system does use them.

I didn't see any difference if I included eventlet.monkey_patch() in the setup. The real system does.


Solution

  • This seems to be caused by the cassandra-drivers eventlet code using select.select() on a ssl socket. This causes problems as described here: select and ssl in python

    This issue is fixed in https://github.com/datastax/python-driver/pull/485