Setting the Daemon as True

51 views
Skip to first unread message

James Simpson

unread,
Sep 26, 2011, 7:37:17 AM9/26/11
to multi-mechanize
Hello,

We are currently trying to use multi-mechanize to monitor our system
during our release procedure. Our goal is to have the multi-mechanize
running as we run the release and check that non of our transactions
are going missing as we start and stop services on multiple boxes. For
this I have written our own script that imports the multi-mechanize.py
and calls its main method. I realise this is not the usual use for
multi-mechanize but it does seem to match most of our requirements.
However we have found the following issues:

1) The multi-mechanize file cannot be imported, seemingly you cannot
import a file with a "-" in its name. Is there any reason that it was
named this way as surely multi-mechanize should be importable? (we
have manually changed the filename and imported the code fine)

After this we wanted the ability to stop the process once we have
finished releasing by using the ctrl-c kill command. In doing this we
were simply killing the initial process and not the subprocesses the
multi-mechanize kicks off. We found that monkey patching the UserGroup
class to override the daemon to True meant that we could kill of all
the processes using the ctrl-c. However we realise this might not be
future compatible and so could there be a new option introduced to
allow us to set the deamon to true if we wish? E.g. --deamon=true
See monkey patch below:
<pre>
class NewUserGroup(multimechanize.UserGroup):
def __init__(self,*args, **kw):
super(NewUserGroup,self).__init__(*args,**kw)
self.daemon=True
multimechanize.UserGroup = NewUserGroup
</pre>

The next issue we came across is the location of the results. We
wanted to house this code within our main code base but that would
mean that the results would remain within our code base. We would
prefer to store the results in a temp directory but the only way to
choose this is by hacking the actual multi-mechanize.py changing the
output_dir to /tmp/ instead of projects/. Could this too be an option
to override the output directory?
E.g. --options_dir=/tmp/multimechanize/<timestamp>/

Thirdly as described above we want to run this for an unspecified time
until we hit ctrl-c. Currently the config only allows the tests to be
run for X amount of time, could there be an option in the config of
e.g. run_time: -1 that would allow the multi-mechanize run
indefinitely?
Currently we are setting this to a ridiculously high time to
synthesize this but we may want to run this continuously in the
background over a weekend if a release to our Staging/testing system
for some reason takes an extended period of time so this solution is
messy.
We have also noticed that if we then do kill the process (once our
release is finished) we are not getting the full results of the tests
that ran successfully. Could there not be a try catch around the tests
themselves to then produce the results based on the tests that were
run?

If I were to submit a patch(s) for any or all of these issues would
they be accepted? If so which ones would be accepted and where should
I submit the patch to?

Thanks,

James Simpson

James Simpson

unread,
Sep 26, 2011, 11:47:33 AM9/26/11
to multi-mechanize
Hello,

I have worked on a patch for the things I outlined above, I cant see
anywhere to attach a patch file so I will copy it in below this
message but if you need the actual patch file then I'll be happy to
email this to you. The following commands will patch the file:

cd <directory containing multi-mechanize.py>
patch -p0 -d <directory containing multi-mechanize.py> < /tmp/
patchfile.diff
mv multi-mechanize.py multimechanize.py

I hope you find my changes acceptable.

Thanks,
James Simpson

patchfile.diff:

STARTFILE


diff -ur ./lib/results.py ./lib/results.py
--- ./lib/results.py 2011-06-07 15:33:02.000000000 +0000
+++ ./lib/results.py 2011-09-26 15:28:37.011161876 +0000
@@ -287,6 +287,9 @@
custom_timers[timer] = val

r = ResponseStats(request_num, elapsed_time, epoch_secs,
user_group_name, trans_time, error, custom_timers)
+
+ if self.run_time<0: ## If runtime is negative we will run
until interrupted
+ elapsed_time= self.run_time-1

if elapsed_time < self.run_time: # drop all times that
appear after the last request was sent (incomplete interval)
resp_stats_list.append(r)
diff -ur ./multi-mechanize.py ./multi-mechanize.py
--- ./multi-mechanize.py 2011-06-07 15:33:02.000000000 +0000
+++ ./multi-mechanize.py 2011-09-26 15:28:37.011161876 +0000
@@ -28,6 +28,8 @@
parser = optparse.OptionParser(usage=usage)
parser.add_option('-p', '--port', dest='port', type='int', help='rpc
listener port')
parser.add_option('-r', '--results', dest='results_dir',
help='results directory to reprocess')
+parser.add_option('-o', '--outputdirectory', dest='output_dir',
type='string', help='output directory for results')
+parser.add_option('-d', '--daemon', dest='daemon_process',
help='output directory for results', default=False,
action="store_true")
cmd_opts, args = parser.parse_args()

try:
@@ -68,94 +70,102 @@

run_time, rampup, results_ts_interval, console_logging,
progress_bar, results_database, post_run_script, xml_report,
user_group_configs = configure(project_name)

- run_localtime = time.localtime()
- output_dir = time.strftime('projects/' + project_name + '/results/
results_%Y.%m.%d_%H.%M.%S/', run_localtime)
-
- # this queue is shared between all processes/threads
- queue = multiprocessing.Queue()
- rw = ResultsWriter(queue, output_dir, console_logging)
- rw.daemon = True
- rw.start()
-
- user_groups = []
- for i, ug_config in enumerate(user_group_configs):
- ug = UserGroup(queue, i, ug_config.name,
ug_config.num_threads, ug_config.script_file, run_time, rampup)
- user_groups.append(ug)
- for user_group in user_groups:
- user_group.start()
-
- start_time = time.time()
+ run_localtime = time.localtime()

- if console_logging:
- for user_group in user_groups:
- user_group.join()
+ if cmd_opts.output_dir:
+ base_output_dir=cmd_opts.output_dir+"/"
else:
- print '\n user_groups: %i' % len(user_groups)
- print ' threads: %i\n' % (ug_config.num_threads *
len(user_groups))
+ base_output_dir='projects/'
+ output_dir = time.strftime(base_output_dir + project_name + '/
results/results_%Y.%m.%d_%H.%M.%S/', run_localtime)
+ try:
+ # this queue is shared between all processes/threads
+ queue = multiprocessing.Queue()
+ rw = ResultsWriter(queue, output_dir, console_logging)
+ rw.daemon = True
+ rw.start()
+
+ user_groups = []
+ for i, ug_config in enumerate(user_group_configs):
+ ug = UserGroup(queue, i, ug_config.name,
ug_config.num_threads, ug_config.script_file, run_time, rampup)
+ user_groups.append(ug)
+ for user_group in user_groups:
+ user_group.start()
+
+ start_time = time.time()
+
+ if console_logging:
+ for user_group in user_groups:
+ user_group.join()
+ else:
+ print '\n user_groups: %i' % len(user_groups)
+ print ' threads: %i\n' % (ug_config.num_threads *
len(user_groups))

- if progress_bar:
- p = progressbar.ProgressBar(run_time)
- elapsed = 0
- while elapsed < (run_time + 1):
- p.update_time(elapsed)
- if sys.platform.startswith('win'):
- print '%s transactions: %i timers: %i errors:
%i\r' % (p, rw.trans_count, rw.timer_count, rw.error_count),
- else:
- print '%s transactions: %i timers: %i errors:
%i' % (p, rw.trans_count, rw.timer_count, rw.error_count)
- sys.stdout.write(chr(27) + '[A' )
- time.sleep(1)
- elapsed = time.time() - start_time
+ if progress_bar:
+ p = progressbar.ProgressBar(run_time)
+ elapsed = 0
+ while elapsed < (run_time + 1):
+ p.update_time(elapsed)
+ if sys.platform.startswith('win'):
+ print '%s transactions: %i timers: %i
errors: %i\r' % (p, rw.trans_count, rw.timer_count, rw.error_count),
+ else:
+ print '%s transactions: %i timers: %i
errors: %i' % (p, rw.trans_count, rw.timer_count, rw.error_count)
+ sys.stdout.write(chr(27) + '[A' )
+ time.sleep(1)
+ elapsed = time.time() - start_time

- print p
+ print p

- while [user_group for user_group in user_groups if
user_group.is_alive()] != []:
- if progress_bar:
- if sys.platform.startswith('win'):
- print 'waiting for all requests to finish...\r',
- else:
- print 'waiting for all requests to finish...\r'
- sys.stdout.write(chr(27) + '[A' )
- time.sleep(.5)
+ while [user_group for user_group in user_groups if
user_group.is_alive()] != []:
+ if progress_bar:
+ if sys.platform.startswith('win'):
+ print 'waiting for all requests to finish...
\r',
+ else:
+ print 'waiting for all requests to finish...
\r'
+ sys.stdout.write(chr(27) + '[A' )
+ time.sleep(.5)

- if not sys.platform.startswith('win'):
- print
-
- # all agents are done running at this point
- time.sleep(.2) # make sure the writer queue is flushed
- print '\n\nanalyzing results...\n'
- results.output_results(output_dir, 'results.csv', run_time,
rampup, results_ts_interval, user_group_configs, xml_report)
- print 'created: %sresults.html\n' % output_dir
- if xml_report:
- print 'created: %sresults.jtl' % output_dir
- print 'created: last_results.jtl\n'
-
- # copy config file to results directory
- project_config = os.sep.join(['projects', project_name,
'config.cfg'])
- saved_config = os.sep.join([output_dir, 'config.cfg'])
- shutil.copy(project_config, saved_config)
-
- if results_database is not None:
- print 'loading results into database: %s\n' %
results_database
- import lib.resultsloader
- lib.resultsloader.load_results_database(project_name,
run_localtime, output_dir, results_database,
- run_time, rampup, results_ts_interval,
user_group_configs)
-
- if post_run_script is not None:
- print 'running post_run_script: %s\n' % post_run_script
- subprocess.call(post_run_script)
-
- print 'done.\n'
-
- if remote_starter is not None:
- remote_starter.test_running = False
- remote_starter.output_dir = output_dir
-
- return
+ if not sys.platform.startswith('win'):
+ print
+ finally:
+ # all agents are done running at this point
+ time.sleep(.2) # make sure the writer queue is flushed
+ print '\n\nanalyzing results...\n'
+ results.output_results(output_dir, 'results.csv', run_time,
rampup, results_ts_interval, user_group_configs, xml_report)
+ print 'created: %sresults.html\n' % output_dir
+ if xml_report:
+ print 'created: %sresults.jtl' % output_dir
+ print 'created: last_results.jtl\n'
+
+ # copy config file to results directory
+ project_config = os.sep.join(['projects', project_name,
'config.cfg'])
+ saved_config = os.sep.join([output_dir, 'config.cfg'])
+ shutil.copy(project_config, saved_config)
+
+ if results_database is not None:
+ print 'loading results into database: %s\n' %
results_database
+ import lib.resultsloader
+ lib.resultsloader.load_results_database(project_name,
run_localtime, output_dir, results_database,
+ run_time, rampup,
results_ts_interval, user_group_configs)
+
+ if post_run_script is not None:
+ print 'running post_run_script: %s\n' % post_run_script
+ subprocess.call(post_run_script)
+
+ print 'done.\n'
+
+ if remote_starter is not None:
+ remote_starter.test_running = False
+ remote_starter.output_dir = output_dir
+ return



def rerun_results(results_dir):
- output_dir = 'projects/%s/results/%s/' % (project_name,
results_dir)
+ if cmd_opts.output_dir:
+ base_output_dir=cmd_opts.output_dir+"/"
+ else:
+ base_output_dir='projects/'
+ output_dir = '%s/%s/results/%s/' % (base_output_dir,project_name,
results_dir)
saved_config = '%s/config.cfg' % output_dir
run_time, rampup, results_ts_interval, console_logging,
progress_bar, results_database, post_run_script, xml_report,
user_group_configs = configure(project_name, config_file=saved_config)
print '\n\nanalyzing results...\n'
@@ -226,6 +236,8 @@
self.process_num = process_num
self.user_group_name = user_group_name
self.num_threads = num_threads
+ if cmd_opts.daemon_process:
+ self.daemon = True
self.script_file = script_file
self.run_time = run_time
self.rampup = rampup
@@ -286,6 +298,9 @@
# scripts have access to these vars, which can be useful for
loading unique data
trans.thread_num = self.thread_num
trans.process_num = self.process_num
+
+ if self.run_time<0: ## If runtime is negative we will run
until interrupted
+ elapsed= self.run_time-1

while elapsed < self.run_time:
error = ''
@@ -305,7 +320,9 @@

fields = (elapsed, epoch, self.user_group_name,
scriptrun_time, error, trans.custom_timers)
self.queue.put(fields)
-
+
+ if self.run_time<0: ## If runtime is negative we will run
until interrupted
+ elapsed= self.run_time-1


class ResultsWriter(threading.Thread):



ENDFILE

corey goldberg

unread,
Sep 26, 2011, 3:03:59 PM9/26/11
to multi-mechanize
Hi James,
thanks for posting.
> If I were to submit a patch(s) for any or all of these issues would> they be accepted? If so which ones would be accepted and where should> I submit the patch to?
I'd like to start using the tracker on GoogleCode for issues and
patches:http://code.google.com/p/multi-mechanize/issues/list
could you submit these there?(please try to break things down into
individual issues with associated patches)
also, make sure your updates are based off the latest svn trunk, not
the released version.thanks much!
-Corey

Corey Goldberg

unread,
Sep 26, 2011, 3:06:52 PM9/26/11
to multi-mechanize
Hi James,

thanks for posting.
 > If I were to submit a patch(s) for any or all of these issues would
> they be accepted? If so which ones would be accepted and where should
> I submit the patch to?

I'd like to start using the tracker on GoogleCode for issues and
patches:http://code.google.com/p/multi-mechanize/issues/list

 could you submit these there? (please try to break things down into

corey goldberg

unread,
Sep 26, 2011, 3:08:58 PM9/26/11
to multi-mechanize
[apologies if this posted multiple times]
Reply all
Reply to author
Forward
0 new messages