Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
justquick committed Nov 7, 2009
0 parents commit 72dc8d6
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
@@ -0,0 +1,20 @@
### Simple Python interface for the Varnish management port.

Varnish is a state-of-the-art, high-performance HTTP accelerator.
For more information checkout [http://varnish.projects.linpro.no/](http://varnish.projects.linpro.no/)

Varnish provides a simple telnet management interface for doing things like:

* reloading configurations
* purging URLs from cache
* view statistics
* start and stop the server

This Python API takes full advantage of the available commands and can run
across multiple Varnish instances

Example:

>>> manager = VarnishManager( ('server1:82', 'server2:82') )
>>> print manager.run('stats')
>>> manager.close()
17 changes: 17 additions & 0 deletions bin/varnish_manager
@@ -0,0 +1,17 @@
#!/usr/bin/env python

from varnish import VarnishManager
from pprint import pprint

manager = VarnishManager((raw_input('Varnish Management Address (ip:port): '),))

while 1:
try:
i = raw_input('varnish> ')
except (EOFError, KeyboardInterrupt):
break
if i.startswith('quit') or i.startswith('exit'):
break
pprint(manager.run(*i.split()))

manager.close()
12 changes: 12 additions & 0 deletions setup.py
@@ -0,0 +1,12 @@
from distutils.core import setup

setup(
name='python-varnish',
version='0.1',
description='Simple Python interface for the Varnish management port',
author='Justin Quick',
author_email='justquick@gmail.com',
url='http://github.com/justquick/python-varnish',
scripts=['bin/varnish_manager'],
packages=['varnish'],
)
30 changes: 30 additions & 0 deletions tests.py
@@ -0,0 +1,30 @@
from varnish import VarnishManager
import unittest


class VarnishTests(unittest.TestCase):
def setUp(self):
self.manager = VarnishManager((raw_input('Varnish Management Address (ip:port): '),))

def test_ping(self):
result = self.manager.run('ping')[0][0]
self.assertEqual(len(result), 2)
self.assert_(map(lambda x: isinstance(x, float), (True,True)))

def test_threading(self):
self.manager.run(('purge.url', '^/myrandomurl/$'), threaded=True)
self.assert_(self.manager.run('purge.list')[0][0].endswith('^/myrandomurl/$\n'))

def test_stats(self):
self.assert_(isinstance(self.manager.run('stats')[0][0], dict))

def test_multiple(self):
result = self.manager.run(( ('ping',None),('ping',None) ))
self.assertEqual(result[0][0], result[0][1])

def tearDown(self):
self.manager.close()

if __name__ == '__main__':
unittest.main()

142 changes: 142 additions & 0 deletions varnish/__init__.py
@@ -0,0 +1,142 @@
"""
Simple Python interface for the Varnish management port.
"""
from telnetlib import Telnet
from threading import Thread

class VarnishHandler(Telnet):
def __init__(self, host_port_timeout):
if isinstance(host_port_timeout, basestring):
host_port_timeout = host_port_timeout.split(':')
Telnet.__init__(self, *host_port_timeout)

def quit(self): self.close()

def fetch(self, command):
"""
Run a command on the Varnish backend and return the result
return value is a tuple of ( (status, length), content )
"""
self.write('%s\n' % command)
(status, length), content = map(int, self.read_until('\n').split()), ''
assert status == 200, 'Bad response code: %s %s' % (status, self.read_until('\n').rstrip())
while len(content) < length:
content += self.read_until('\n')
return (status, length), content

# Service control methods
def start(self): return self.fetch('start')
def stop(self): return self.fetch('stop')

# Information methods
def ping(self, timestamp=None):
cmd = 'ping'
if timestamp: cmd += ' %s' % timestamp
return tuple(map(float, self.fetch(cmd)[1].split()[1:]))

def stats(self):
stat = {}
for line in self.fetch('stats')[1].splitlines():
a = line.split()
stat['_'.join(a[1:]).lower()] = int(a[0])
return stat

def help(self, command=None):
cmd = 'help'
if command: cmd += ' %s' % command
return self.fetch(cmd)[1]

# VCL methods
def vcl_load(self, configname, filename):
return self.fetch('vcl.load %s %s' % (configname, filename))

def vcl_inline(self, configname, vclcontent):
return self.fetch('vcl.inline %s %s' % (configname, vclcontent))

def vcl_show(self, configname):
return self.fetch('vcl.show' % configname)

def vcl_use(self, configname):
return self.fetch('vcl.use %s' % configname)

def vcl_discard(self, configname):
return self.fetch('vcl.discard %s' % configname)

def vcl_list(self):
vcls = {}
for line in self.fetch('vcl.list')[1].splitlines():
a = line.split()
vcls[a[2]] = tuple(a[:-1])
return vcls

# Param methods
def param_show(self, param, long=False):
cmd = 'param.show '
if long: cmd += '-l '
return self.fetch(cmd + param)

def param_set(self, param, value):
self.fetch('param.set %s %s' % (param, value))

# Purge methods
def purge_url(self, regex):
return self.fetch('purge.url %s' % regex)[1]

def purge_hash(self, regex):
return self.fetch('purge.hash %s' % regex)[1]

def purge_list(self):
return self.fetch('purge.list')[1]

def purge(self, *args):
for field, operator, arg in args:
self.fetch('purge %s %s %s\n' % (field, operator, arg))[1]

class ThreadedRunner(Thread):
"""
Runs commands on a particular varnish server in a separate thread
"""
def __init__(self, addr, *commands):
self.addr = addr
self.commands = commands
super(ThreadedRunner, self).__init__()

def run(self):
handler = VarnishHandler(self.addr)
for cmd in self.commands:
if isinstance(cmd, tuple) and len(cmd)>1:
getattr(handler, cmd[0].replace('.','_'))(*cmd[1:])
else:
getattr(handler, cmd.replace('.','_'))()
handler.close()

def run(addr, *commands):
"""
Non-threaded batch command runner returning output results
"""
results = []
handler = VarnishHandler(addr)
for cmd in commands:
if isinstance(cmd, tuple) and len(cmd)>1:
results.extend([getattr(handler, c[0].replace('.','_'))(*c[1:]) for c in cmd])
else:
results.append(getattr(handler, cmd.replace('.','_'))())
handler.close()
return results

class VarnishManager(object):
def __init__(self, servers):
assert len(servers), 'No servers found, please declare some'
self.servers = servers

def run(self, *commands, **kwargs):
if kwargs.pop('threaded', False):
[ThreadedRunner(server, *commands).start() for server in self.servers]
else:
return [run(server, *commands) for server in self.servers]

def help(self, command=None): return self.servers[0].help(command)

def close(self):
self.run('close', threaded=True)
self.servers = ()
Binary file added varnish/__init__.pyc
Binary file not shown.

0 comments on commit 72dc8d6

Please sign in to comment.