#!/bin/env python ''' $Id: zouch.py,v 1.3 2000/06/12 04:23:01 zen Exp $ Zouch - the Zope torture tester ''' import whrandom import sha import threading import ftplib import httplib from string import split,join,replace from time import time,strftime,localtime,sleep from StringIO import StringIO from Queue import Queue from threading import Thread,RLock from urllib import urlencode from urlparse import urlparse from base64 import encodestring retries = 10 retrysleep = 1 def debug(msg): print 'D: %s - %s' % (threading.currentThread().getName(),msg) # Fatal exceptions will not be caught class FatalException(Exception): pass class UnsupportedProtocol(FatalException): pass class FolderLock: def __init__(self): self.locks = {} self.sync = RLock() def lock(self,dirs): self._lock(self._mypath(dirs)) self._lock(self._parentpath(dirs)) def unlock(self,dirs): self._unlock(self._parentpath(dirs)) self._unlock(self._mypath(dirs)) def _parentpath(self,dirs): if len(dirs) == 1: return 'root' else: return join(dirs[:-1],'/') def _mypath(self,dirs): return join(dirs,'/') def _lock(self,d): locks = self.locks sync = self.sync while 1: try: sync.acquire() acq = 1 if locks.has_key(d): l = locks[d] sync.release() acq = 0 l.acquire() l.release() else: l = RLock() l.acquire() locks[d] = l break finally: if acq: sync.release() def _unlock(self,d): locks = self.locks sync = self.sync sync.acquire() try: l = locks[d] del locks[d] l.release() finally: sync.release() folderlock = FolderLock() class HTTPMaker: 'Baseclass for HTTP Maker classes' def __init__(self,queue,url,username,password): purl = urlparse(url) host,port = split(purl[1],':',1) path = purl[2] if port: port = int(port) else: port = 80 if path[-1] == '/': self.path = path else: self.path = path + '/' self.queue = queue self.ops = 0 if username is None: self.auth = None else: if password is None: password = '' self.auth = 'Basic %s' % \ replace(encodestring('%s:%s' % (username,password)),'\012','') #self.http = httplib.HTTP(host,port) self.ops = 0 self.host = host self.port = port def doget(self,getcmd): http = None for i in range(0,retries): try: http = httplib.HTTP(self.host,self.port) break except: sleep(retrysleep) if http is None: debug('Failed %s' % getcmd) return None auth = self.auth http.putrequest('GET', getcmd) http.putheader('Accept','text/html') http.putheader('Accept','text/plain') if auth is not None: http.putheader('Authorization',auth) http.endheaders() errcode,errmsg,headers = http.getreply() if errcode not in (200,302): debug('%s ret %d %s' % (getcmd,errcode,errmsg)) return None f = http.getfile() # Suck in the data since we are torturing junk = f.readlines() f.close() return junk class HTTPFolderMaker(HTTPMaker): def run(self): queue = self.queue path = self.path dirs = queue.get() while dirs is not None: id = dirs[-1] parms = urlencode({'id': id,'title': 'Zouch folder ' + id}) folderlock.lock(dirs) try: p = path+join(dirs[:-1],'/')+'/manage_addFolder?' + parms if self.doget(p) is None: return finally: folderlock.unlock(dirs) self.ops = self.ops + 1 dirs = queue.get() class HTTPFileMaker(HTTPMaker): def run(self): queue = self.queue path = self.path l = queue.get() while l is not None: try: filepath,data = l except: debug('WTF?: %s' % l) raise d = join(filepath[:-1],'/') id = filepath[-1] parms = urlencode({ 'id': id, 'title': 'Zouch generated document ' + id, 'file': data}) folderlock.lock(filepath) try: p = path + d + '/manage_addDTMLDocument?' + parms if self.doget(p) is None: return finally: folderlock.unlock(filepath) self.ops = self.ops + 1 l = queue.get() class HTTPFileReader(HTTPMaker): def run(self): queue = self.queue path = self.path filepath = queue.get() while filepath is not None: p = path + join(filepath,'/') if self.doget(p) is None: return self.ops = self.ops + 1 filepath = queue.get() class FTPMaker: 'Baseclass for FTP Maker classes' def __init__(self,queue,url,username,password): purl = urlparse(url) host,port = split(purl[1],':',1) path = purl[2] if port: port = int(port) else: port = 21 ftp = ftplib.FTP() #ftp.set_debuglevel(1) #ftp.set_pasv(1) if password is None: password = '' if len(path) > 0 and path[-1] <> '/': path = path + '/' # Don't use os.path.join - this is FTP self.host = host self.port = port self.username = username self.password = password self.path = path self.ftp = ftp self.queue = queue self.ops = 0 def __del__(self): try: self.ftp.quit() self.ftp.close() except: pass def run(self): try: ftp = self.ftp ftp.connect(self.host,self.port) if self.username: ftp.login(self.username,self.password) else: ftp.login() try: self.feed() finally: ftp.quit() except ftplib.all_errors: self.ops = -1 # Never got a chance to connect class FTPFolderMaker(FTPMaker): def feed(self): queue = self.queue path = self.path ftp = self.ftp dirs = queue.get() try: while dirs is not None: try: folderlock.lock(dirs) ftp.mkd(path + join(dirs,'/')) finally: folderlock.unlock(dirs) self.ops = self.ops + 1 dirs = queue.get() except ftplib.error_perm: debug('Failed to create directory %s%s' % (path,join(dirs,'/'))) class FTPFileMaker(FTPMaker): def feed(self): queue = self.queue path = self.path ftp = self.ftp l = queue.get() try: while l is not None: filepath,data = l data = StringIO(data) folderlock.lock(filepath) try: ftp.storlines('STOR %s%s' % (path,join(filepath,'/')),data) self.ops = self.ops + 1 finally: try:data.close() except: pass folderlock.unlock(filepath) l = queue.get() except ftplib.error_perm: debug('Failed to upload file %s%s' % (path,join(filepath,'/'))) class Zouch: 'Feeds commands to the clients' def __init__(self,url,username=None,password=None): ''' Create a feeder to feed our consumers. ''' # Setup and seed our random number generator - seed to known # value to sequence repeats identically each run self._rng = whrandom.whrandom() self._rng.seed(1,5,2) self.rootfolder = strftime('%Y%m%d_%H%M%S',localtime(time())) if url[-1] <> '/': url = url + '/' self.url = url self.numthreads = 4 self.folders = 5 self.depth = 3 self.files = 20 self.username = username self.password = password def run(self): self.purl = urlparse(self.url) protocol = self.purl[0] #if protocol == 'ftp': # FolderMaker = FTPFolderMaker # FileMaker = FTPFileMaker if protocol == 'http': FolderMaker = HTTPFolderMaker FileMaker = HTTPFileMaker FileReader = HTTPFileReader else: raise UnsupportedProtocol(protocol) print "Base URL is %s%s" % (self.url,self.rootfolder) self.timeit('Generating folders',FolderMaker,self.makefolders) self.timeit('Generating files',FileMaker,self.makefiles) self.timeit('Reading files',FileReader,self.readfiles) #self.timeit('Trashing files',FolderMaker,self.trashfiles) #self.timeit('Trashing folders',FolderMaker,self.trashfolders) def timeit(self,txt,MakerClass,fillmethod): numthreads = self.numthreads queue = Queue(self.queuesize) self.queue = queue url = self.url username = self.username password = self.password print txt, threads = [] makers = [] starttime = time() for i in range(0,numthreads): m = MakerClass(queue,url,username,password) makers.append(m) t = Thread(target=m.run,name='Zouch maker %d' % i) t.setDaemon(1) threads.append(t) for i in range(0,numthreads): threads[i].start() t = Thread(target=fillmethod,name='Zouch filler') t.setDaemon(1) t.start() for t in threads: if t.isAlive(): t.join() endtime = time() print ' %f seconds' % (endtime - starttime) threads = None print 'Thread[Operations] -', tot = 0 for i in range(0,len(makers)): print ' %d[%d]' % (i,makers[i].ops), tot = tot + makers[i].ops print print 'Total operations = %d' % tot def makefolders(self): depth = self.depth queue = self.queue i = -1 while 1: p = self.folderpath(i) if len(p) > depth+1: # depth+1 for top folder break queue.put(self.folderpath(i)) i = i + 1 self.numfolders = i for i in range(0,self.numthreads): queue.put(None) # Make sure our makers know to terminate def makefiles(self): queue = self.queue # standard_html_header filepath = self.folderpath(-1) filepath.append('standard_html_header') data = ''' &dtml-title_or_id; ''' queue.put((filepath,data)) # standard_html_footer filepath = self.folderpath(-1) filepath.append('standard_html_footer') data = '' queue.put((filepath,data)) # standard_error_message filepath = self.folderpath(-1) filepath.append('standard_error_message') data = ''' &dtml-error_message;

Zope Error

Zope has encountered an error while publishing this resource.

Error Type:
Error Value:

For more detailed information about the error, please refer to the HTML source for this page.

''' queue.put((filepath,data)) i = 0 for filenum in range(0,self.files): for foldnum in range(0,self.numfolders): filepath = self.folderpath(foldnum) filepath.append('f%03d' % filenum) queue.put((filepath,self.genfile())) i = i + 1 self.numfiles = i for i in range(0,self.numthreads): queue.put(None) # Make sure our makers know to terminate def readfiles(self): files = self.files folders = self.folders rng = self._rng queue = self.queue for i in range(0,self.numfiles * self.reads): foldnum = rng.randint(0,folders-1) filenum = rng.randint(0,files-1) queue.put(self.filepath(foldnum,filenum)) for i in range(0,self.numthreads): queue.put(None) # Make sure our makers know to terminate def folderpath(self,num): ''' Generate the full path to folder number 'num'. Returns a list of path components. Root folder is num=-1. First folder is num=0. ''' folders = self.folders l = [self.rootfolder,] num = num + 1 while num > 0: n = (num-1) % folders l.append('d%03d' % n) num = (num - n) / folders return l def filepath(self,foldnum,filenum): filepath = self.folderpath(foldnum) filepath.append('f%03d' % filenum) return filepath def genfile(self): return '''

This is the Document.

''' if __name__ == '__main__': print __doc__ url = 'http://your.site.here:8080/dumparea' uname = 'zouch' passwd = 'zouch' f = Zouch(url,uname,passwd) f.folders = 3 f.depth = 5 f.files = 100 f.reads = 5 # This is a multiplier - readops = totalfiles * reads f.numthreads = 10 f.queuesize = 100 f.retries = 10 f.retrysleep = 1 f.run()