tag:blogger.com,1999:blog-71194942401141035562024-03-18T20:51:07.815-07:00thoughts by claygMostly just documentation of technical experiments and experience that I felt worthy of archiving on the interwebz.clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.comBlogger65125tag:blogger.com,1999:blog-7119494240114103556.post-44387539040770716922020-01-15T20:36:00.000-08:002020-01-16T13:27:02.350-08:00Ansible Download or Fetch remote logs from nodesI really like the Ansible connection management and inventory system (I use a dynamic inventory generated from my Terraform state files). Playbook organization gives me fits sometimes, I tend to cut things up into small plays with just a few very closely related tasks and then roll those up into larger playbooks using imports so they cover an entire systems. Then I'll have a good collection of fairly complex but decoupled playbooks that are mostly just imports of smaller plays. I'll then import the fewer larger playsbooks into site.yaml so it reads like an overview.<br />
<br />
I've tried roles, and tags - but they never seem to be satisfying. IIRC, when I looked at the timeline the import statement was added to ansible *after* roles were introduced - so I assume that means my requirements or conceptualizations that lead me to want to develop, organize and compose things this way is not unprecedented.<br />
<br />
I wish I was better at using modules adhoc from the command line. I'm pretty good at grabbing:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">ansible nodes -m shell -a "some bash"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
... but I've also written more than a few plays that I only use once and throw away. For example something like fetch_logs.yaml<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">- hosts: nodes</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> become: true</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> tasks:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> - name: fetch logs</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> fetch:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> src=/var/log/swift/all.log</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> dest=.scratch/logs/</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> validate_checksum=false</span><br />
<div>
<br /></div>
<div>
<span style="font-family: inherit;">And while it's entirely reasonable to have a single task play checked into version control in case you need it again later - I'm also finding it getting a little easier to start to convert these things to adhoc commands.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">To use ansible to download remote logs from remote nodes using the fetch module adhoc on the commnad line, try something like this:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">ansible nodes -b -m fetch -a "src=/var/log/swift/all.log dest=.scratch/logs/ validate_checksum=false"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">In this command "nodes" is just an ansible group, to specify hosts, it could just as well be "all". The "-b" option is for "--become", "-m" specifics the module and the space separated list of arguments is given with "-a". Hopefully I'll find this next time I forget.</span></div>
clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-67286335137087302592017-09-21T08:51:00.001-07:002017-09-21T18:36:05.707-07:00Today is a good day.Walking home from my run this morning, the sun rising above the hills to my back, casting off the cool mist at the break of day in the Sunset district of San Francisco, I began to climb, tired, up a hill behind an older Asian man, maybe two dozen years my senior, moving slowly and smoking a sweet smelling joint. As I overtook him he turned to me, startled, and said <i>not quite</i> too loudly "Good day!" Assuming he would know, I felt compelled to echo his declaration and assured him, yes indeed it was, a "Good day." <br />
<br />
As I walked home I thought I 'ought let y'all know; I have it on reputable authority...<br />
<br />
Today is a good day.clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-89259946944143037802017-06-15T11:01:00.002-07:002017-06-15T11:01:57.330-07:00My mental model for software<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmyvLMaPF0KbEpuRrp_R_5lA2_du7oRIGwlKc11vjykKIcNUSV4hJxLr3PrDF-sZQaJxQMSev3fQBQKjX473ze5ORtPk8YeCHOoDobvklnrIMrGKouskDu5_u7RiI1gjGiK8EeE64QBviN/s1600/Screen+Shot+2017-06-15+at+11.00.44+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1076" data-original-width="1600" height="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmyvLMaPF0KbEpuRrp_R_5lA2_du7oRIGwlKc11vjykKIcNUSV4hJxLr3PrDF-sZQaJxQMSev3fQBQKjX473ze5ORtPk8YeCHOoDobvklnrIMrGKouskDu5_u7RiI1gjGiK8EeE64QBviN/s400/Screen+Shot+2017-06-15+at+11.00.44+AM.png" width="400" /></a></div>
<br />clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com1tag:blogger.com,1999:blog-7119494240114103556.post-15873018618637170032016-09-09T02:33:00.000-07:002016-09-09T02:33:47.783-07:00OpenStack for OperatorsA mission is a strongly felt aim, ambition, or calling.<br />
<br />
The OpenStack mission:<br />
<br />
to produce the ubiquitous Open Source Cloud Computing platform that<br />
enables building interoperable public and private clouds regardless of<br />
size, by being simple to implement and massively scalable while serving<br />
the cloud users’ needs<br />
<br />
This is what I most strongly identify with OpenStack.<br />
<br />
A value is one's judgment of what is important in life.<br />
<br />
OpenStack states some values; which I think are also good:<br />
<br />
Open Source - License; because IANAL<br />
Open Design - Summits are fun; if you have a spec<br />
Open Development - Patches welcome<br />
Open Community - Let's talk; we're nice!<br />
<br />
... the four opens are pretty great - but it's not what I feel called to<br />
do - it's not what I'm about. I just think that in order to build out<br />
that ubiquitous platform; where the open source cloud is found<br />
<b>everywhere</b> ... no one company can do that? So we better have a good<br />
strategy for a community if we're gunna do this thing.<br />
<br />
And that's what I come to when I think about "what is OpenStack" - I<br />
don't think about the software that OpenStack produces. To me OpenStack<br />
is a phenomenon - because I literally do not understand the cause or<br />
explanation for why this thing took off like it did?<br />
<br />
Maybe I think there's just a lot of hackers out there that all feel like<br />
I do - that building this thing is important (to society?) - and that<br />
they can make a meaningful contribution.<br />
<br />
OpenStack is a system to organize the people committed to the mission.<br />
The result is OpenStack software - not all of which is good - some of it<br />
might not even be useful - but if it was done with a passion for the<br />
mission and according to the values - we should be happy to call it<br />
OpenStack.<br />
<br />
Or maybe it's the money.<br />
<br />
Maybe it's the system to organize the profit-generating-organisms that<br />
feed the hackers committed to the mission, the astroturf, the latch your<br />
wagon^Wdriver-plugin onto it while it's hot, the cushy arm-chair<br />
architect pontificate on the grand unified theory of exception logging,<br />
the ego of the -2, the paycheck, the passport to hang with friends<br />
around a whiteboard.<br />
<br />
Meanwhile, in the real world, the market is happily shipping AWS so much<br />
money Google and Microsoft can't mount a reasonable defense - Rackspace<br />
is going to tap out and HP already gave up.<br />
<br />
If we're still committed to the mission - we have got to focus on our<br />
deployments.<br />
<br />
Nothing tells you more about what's important than working with real<br />
deployments. The people operating OpenStack software are the best<br />
resource we have to cut through the junk and deal with what matters.<br />
<br />
If you're not at least <i>partially</i> engaged with the delivery, deployment<br />
and operation of OpenStack software I strongly recommend you have a talk<br />
with your manager - or seek a different employer.<br />
<br />
Listen to the operators. Ignore everything else. Ignore internal<br />
organizational boundaries. Ignore your direct manager. Ignore the TC.<br />
Ignore me. Listen to the operators.<br />
<div>
<br /></div>
If they continue to deploy OpenStack's software - then OpenStack matters<br />
- and we <i>have</i> to keep after the mission - if they do <b>not</b> continue to<br />
deploy OpenStack software - then none of this matters.<br />
<br />clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com2tag:blogger.com,1999:blog-7119494240114103556.post-36678317827047842902016-06-22T22:39:00.001-07:002016-06-22T22:39:24.190-07:00#NoBillNoBreak #StopTheStuntSee the problem is, the frequency of high profile mass shootings is too high - it's making people that would prefer not to carry weapons feel uncomfortable. That and all the murder.<br />
<br />
There is in no clear answer. Smart legislation <a href="http://www.nytimes.com/interactive/2015/10/03/us/how-mass-shooters-got-their-guns.html?_r=0">may help in some cases</a>. No Fly No Buy <a href="https://www.aclu.org/blog/speak-freely/until-no-fly-list-fixed-it-shouldnt-be-used-restrict-peoples-freedoms">would not be perfect</a>. I think we should vote on it!?<br />
<br />
Why is there always so much name calling on social media? Wag of the finger to both sides! Just stop doing that!? Phew, solved that problem.<br />
<br />
But, ugh, the <a href="https://en.wikipedia.org/wiki/Fear,_uncertainty_and_doubt">FUD</a>.<br />
<br />
<ul>
<li>No one thinks smart regulations for firearms will solve murder.</li>
<li>I don't think a radical redefinition of the 2nd amendment is possible in my lifetime - <a href="http://feelthebern.org/bernie-sanders-on-gun-policy/">Bernie didn't either</a>.</li>
<li>Any broad support to legislate confiscation of legally obtained firearms would meet riots on a national level well before congress had a chance to shoot it down - I don't really see how people can honestly claim they think the government is going to "take their guns"?</li>
</ul>
And yet, every time guns enter the national stage - more Americans arm themselves?<br />
<br />
We need to stop talking <i>past</i> each other. Is there really no common ground? No compromise? <br />
<br />
Congress, do your job. Get in there and debate it out - I know it's hard - people say stupid shit - seemingly refuse to listen to common sense. But you're a professional. You can show us how it's done! Negotiate. Find the best solution that you can get some agreement on and pass it! Demonstrate progress! Don't think about what sound byte will play best in the next news cycle - think about what is best for your constituents - not what is most likely to get you re-elected. Then come November, show us someone who is good at their <b>job</b>.clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-28686180719753944992015-12-23T12:45:00.001-08:002015-12-23T12:45:09.318-08:00Openstack SDK attach cloud networkI don't think the current <a href="https://github.com/openstack/python-openstacksdk">OpenStack SDK</a> supports the nova api extension for virtual interfaces.<br />
<br />
I'm <a href="http://stackalytics.com/?user_id=clay-gerrard&release=all">not a nova guy</a>.<br />
<br />
I don't understand the difference between the <a href="http://developer.openstack.org/api-ref-compute-v2-ext.html#os-interface">OpenStack compute v2 os-interfaces API</a> - and the <a href="https://developer.rackspace.com/docs/cloud-servers/v2/developer-guide/#retrieve-list-of-virtual-interfaces">Openstack compute os-virtual-interfacesv2 extension API</a>.<br />
<br />
But rackspace seems to have had to <a href="https://github.com/rackerlabs/os_virtual_interfacesv2_ext">write an extension for the novaclient CLI</a> to make it able to attach a new virtual interfaces for a cloud network to a running instance.<br />
<br />
I was thinking I might have to write a new <a href="https://github.com/openstack/python-openstacksdk/blob/2574e9585a55f2554ac10299f18be9fd9c0e852f/openstack/resource.py#L207">resource</a> to support the api extension (probably just crib from the existing <a href="https://github.com/openstack/python-openstacksdk/blob/2574e9585a55f2554ac10299f18be9fd9c0e852f/openstack/compute/v2/server_interface.py#L17">ServerInterface</a>) - but it turned out there's already <a href="http://developer.openstack.org/sdks/python/openstacksdk/users/guides/compute.html">support in OpenStackSDK to boot instances with extra networks</a>.<br />
<br />
Here's what I'm using:<br />
<pre class="brush:py">#!/usr/bin/env python
import os
import requests
import sys
# default instance and network attributes
IMAGE = '09de0a66-3156-48b4-90a5-1cf25a905207'
FLAVOR = 'general1-4'
REGION = 'DFW'
NETWORK = 'my-test'
CIDR = '192.168.5.0/24'
auth_url = os.environ.get(
'OS_AUTHURL', 'https://identity.api.rackspacecloud.com/v2.0/')
# username and *password* must be set in the envrion
username = os.environ['OS_USERNAME']
password = os.environ['OS_PASSWORD']
# something about the rackspace cert makes requests mad
requests.packages.urllib3.disable_warnings()
from openstack import connection
from openstack.compute.v2._proxy import _server
# the openstacksdk doesn't support admin_pass
# https://bugs.launchpad.net/python-openstacksdk/+bug/1528949
class PatchedServerResource(_server.Server):
def create_by_id(self, *args, **kwargs):
resp = super(PatchedServerResource, self).create_by_id(*args, **kwargs)
self._attrs['adminPass'] = resp.get('adminPass')
return resp
_server.Server = PatchedServerResource
conn = connection.Connection(auth_url=auth_url,
username=username,
password=password)
conn.profile.set_region(conn.profile.ALL, REGION)
def _get_or_create_network(name):
# this doesn't seem to work
# network = conn.network.find_network(name)
# https://feedback.rackspace.com/forums/298158-network/suggestions/11188446-500-error-response-in-cloud-network-api
# so we just search by hand
for n in conn.network.networks():
if n.name == name:
return n
network = conn.network.create_network(name=name)
conn.network.create_subnet(
network_id=network.id, ip_version='4', cidr=CIDR)
return network
def main():
try:
server_name = sys.argv[1]
except IndexError:
return 'USAGE: %s <server-name>' % sys.argv[0]
image = conn.compute.find_image(IMAGE)
flavor = conn.compute.find_flavor(FLAVOR)
network = _get_or_create_network(NETWORK)
network_ids = (
network.id,
# must explicitly include Public & Service networks
'11111111-1111-1111-1111-111111111111',
'00000000-0000-0000-0000-000000000000',
)
server = conn.compute.create_server(
name=server_name, image=image, flavor=flavor,
networks=[{'uuid': id_} for id_ in network_ids]
)
print 'pass', server['adminPass']
server = conn.compute.wait_for_server(server)
print 'ip', server.access_ipv4
if __name__ == "__main__":
sys.exit(main())
</pre>
<br />
So... that's not terrible. You can see there's a few hacks in there - some defaults are globals for simplicity instead of bothering with argparse - credentials from environ for the same reason - I made the conn instance a global because it's easier to get ahold of in a repl just by importing - there's that <a href="https://github.com/shazow/urllib3/issues/497">weird cert error</a> - some ugly monkey patching <a href="https://bugs.launchpad.net/python-openstacksdk/+bug/1528949">to get at admin_pass</a> - another function for finding a network by name to work around <a href="https://feedback.rackspace.com/forums/298158-network/suggestions/11188446-500-error-response-in-cloud-network-api">something I hit</a> in the Rackspace neutron API.<br />
<br />
Having to add the awkward default static uuid's for the Public and Service networks sorta threw me on my first go 'round.<br />
<br />
But all in all this works for creating new servers with a private cloud network on the Rackspace cloud. Hooray!clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com3tag:blogger.com,1999:blog-7119494240114103556.post-28435851455152868162015-12-22T11:17:00.000-08:002015-12-22T11:32:30.931-08:00Openstack SDK with RackspaceI used to use rackspace-novaclient, but that <a href="https://github.com/rackerlabs/rackspace-novaclient/issues/14">fell apart on me</a>.<br />
<br />
If you search for the <a href="https://developer.rackspace.com/sdks/python/">official python SDK for Rackspace Cloud</a> - you're likely to find <a href="https://github.com/rackspace/pyrax">pyrax</a>.<span id="goog_125627606"></span><span id="goog_125627607"></span><a href="https://www.blogger.com/"></a><br />
<br />
But I guess by <a href="https://github.com/rackspace/pyrax/commit/1139fa04b12e2c2643a3767cb3225a2e0f70bd6d">summer '15</a> they were full on the Openstack-SDK <a href="https://wiki.openstack.org/wiki/SDK-Development/PythonOpenStackSDK">ride</a>!<br />
<br />
<a href="https://github.com/openstack/python-openstacksdk">Openstack-SDK</a> seems like a decent attempt to organize things. <a href="http://developer.openstack.org/sdks/python/openstacksdk/index.html">Docs</a> are decent. But my first attempt to do something 401'd<br />
<br />
<pre>In [68]: conn.authorize()
---------------------------------------------------------------------------
HttpException Traceback (most recent call last)
<ipython-input-68-922206a50f8d> in <module>()
----> 1 conn.authorize()
/private/tmp/test-os-sdk/lib/python2.7/site-packages/openstack/connection.pyc in authorize(self)
264 raise exceptions.HttpException("Unknown exception",
265 six.text_type(ex),
--> 266 status_code=500)
267
268 return headers.get('X-Auth-Token') if headers else None
HttpException: HttpException: Unknown exception, Unauthorized (HTTP 401)
</module></ipython-input-68-922206a50f8d></pre>
<br />
Turns out, the <span style="font-family: Courier New, Courier, monospace;"><b>openstack.connection.Connection</b></span> class doesn't work quite like novaclient, a few params are different. Instead of <i>using your API Key</i> - you need to <i>use your password</i>:<br />
<br />
<pre class="brush:py">from openstack import connection
conn = connection.Connection(
auth_url='https://identity.api.rackspacecloud.com/v2.0/',
username='username',
password='password') # <- NOT API KEY!!!
</pre>
<br />
After that the only tricky part was setting the region:<br />
<br />
<pre class="brush:py">conn.profile.set_region(conn.profile.ALL, 'IAD')
</pre>
<br />clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-10503393757053385812015-12-16T09:15:00.001-08:002017-09-21T18:36:51.739-07:00Eulogy for Thomas Clay Gerrard<div class="p1">
Thank you for joining us today to celebrate and honor the memory of my Father.</div>
<div class="p2">
<br /></div>
<div class="p1">
Thomas Clay Gerrard</div>
<div class="p1">
Uncle Tom</div>
<div class="p1">
Dad</div>
<div class="p1">
or more recently Paw Paw</div>
<div class="p1">
although a lot of us probably called him mostly, just “Yes, Sir”</div>
<div class="p2">
<br /></div>
<div class="p1">
I’d imagine many of you can share an experience where my Dad held a fatherly like role in your life - protective, supportive. But in case any of you ever tried to imagine what it might have been like to have Tom as your actual father - well, I’d like to set the record straight.</div>
<div class="p2">
<br /></div>
<div class="p1">
It was amazing.</div>
<div class="p2">
<br /></div>
<div class="p1">
Yeah, better than you could imagine.</div>
<div class="p2">
<br /></div>
<div class="p1">
I was <b><i>so</i></b> blessed to grow up in home centered and supported by a good man of strong character, a countryman, a good husband and a great father. He was a great example, and he is my ideal role model.</div>
<div class="p2">
<br /></div>
<div class="p1">
Dad was a man of integrity, honesty and authenticity. He never acted for the benefit of others perception - his actions were guided by his own principles - <i>he</i> would tell you he “could not care less what those people thought” - but he was not disrespectful of others. He spoke with sincerity and frankness - without pretense - although he might temper an obvious truth with wit and sarcasm. “And how did that work out for you?” </div>
<div class="p2">
<br /></div>
<div class="p1">
He taught me how to show grace and strength in the face of adversity - how to respectfully disagree…</div>
<div class="p2">
<br /></div>
<div class="p1">
But I also learned, that if you have passion about something (and Dad had passion for everything he did — “If it’s worth doing; it’s worth doing right”) … I learned that if you have passion for something - it’s ok to show that - it’s ok to get mad when someone messes up - people will respect you more if they know where you stand. Everyone knew that Dad had high expectations, for himself and for those with him. You can either suck it up and do your best - or you can get out of the way - there’s no place to half-ass it with Tom. But he’d never hold a grudge. He was willing to get mad, but he was far quicker to forgive, and if you <i>told</i> him <i>up front</i> that you had made a mistake, he respected that a great deal, and there was barely another word about it. He was very fair.</div>
<blockquote class="tr_bq">
We’ve all been gushing this week about what an amazing man my father was, and on occasion someone would try to help find some perspective by remembering “Yeah but he was strict too” - and that’s true. I have often described my father as setting very high expectations. He absolutely expected that the rules were obeyed. But he was always was up front about the ground rules, and there were always well defined consequences.<br />
So honestly, even as a younger man, I didn’t recall him as strict. As I recall, as a boy, he had taught me discipline. And so after that, when I was learning to be a young man and he told me “you <i>KNOW</i> better” - he was right.</blockquote>
<div class="p1">
My father vigorously supported and loved his country. His grit and natural talent served our country well - throughout two terms during the Vietnam War with a Naval Construction Battalion - the Fighting SeaBees. He remained, throughout his life willing and ready to defend this country. He carried his sense of duty, loyalty and self sacrifice forward onto his family. I believe he felt personally responsible to ensure our freedom and safety. And I always knew we were in good strong hands.</div>
<div class="p2">
<br /></div>
<div class="p1">
My father was wise. He had the experience, he had the knowledge, and he had <i>good</i> judgment - which made him an exceptional teacher.</div>
<blockquote class="tr_bq">
I recall a passing moment, as a grown man, at my Aunt Linda’s house. Dad had heard Aunt Linda mention that she and Fellow had collected the remains of a number of wooden decks - and that they wanted to assemble them together into a deck of sorts around the back of their house. Dad immediately set to organizing the affair. We had a lot of help. Some folks I didn’t even know. There was a young man, he must have been someone’s boyfriend or a neighbor, Dad had set him to running his screw gun. A few screws in Dad stopped him - and I overheard “Hey. Slow down. Anyone can screw some boards down - it’s not a race. You need to line up each screw, space them evenly, and run them down straight and even. That’s craftsmanship. That’s what it means. When someone steps onto this deck - they’ll know that the person who built it - did it with care - and that they did a good job” It felt like an out of body experience, watching that young boy receive that wisdom that my father had given me years ago - and he nodded and he understood. He did a good job.</blockquote>
<div class="p1">
My dad taught me so much. I still had so much to learn. I know that in time the shock I’m feeling now will give way to grief, and in time after that I’ll learn to carry that sorrow with dignity. But I don’t think I’ll ever be able to deny the big hole in my life, where I knew I could always turn to him for answers, and support.</div>
<div class="p2">
<br /></div>
<div class="p1">
So uncle Bill, next time when I’m asking you something about my water heater or whatever it is - and I just flat out loose it - you cut me some slack a’ight.</div>
<div class="p2">
<br /></div>
<br />
<div class="p1">
Dad had an uncanny gift for service. Service, i’ve read, is one of the “love languages” - and that is how he was best able to show us just how MUCH he loved us. And it suited him - “actions speak louder than words”. As you remember how much my father has done for you - try not to get caught feeling a great debt for his service - he was <b><i>HAPPY</i></b> to do it. It was his gift. He loves you very much.</div>
clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-40284055856335838252013-09-02T13:39:00.003-07:002013-09-02T13:39:51.835-07:00Twistd upload FTP server<h2>
How to fix "Failed to retrieve directory listing"</h2>
<br />
I was trying to get a quick ftp server up and running, and it seems liked `twistd` is getting to be installed just about everywhere these days so it seemed simple enough:<br />
<br />
<blockquote class="tr_bq">
twistd -n ftp -r .</blockquote>
<br />
Got a twistd up and serving ftp out of the current working directly for anonymous download.<br />
<br />
But I actually wanted the server to let me upload (side note: people hate windows because it's terrible, what a wasteland, it's a terrible chore to get even the most basic things done without tools like scp?).<br />
<br />
So I started with the simple <a href="http://twistedmatrix.com/documents/13.0.0/core/examples/ftpserver.py">ftpserver.py example</a>.<br />
<br />
But nothing is ever "simple" in Twisted. It turns out the <a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.protocols.ftp.FTPRealm.html">FTPRealm</a> blah blah blah - no body cares.<br />
<br />
I was getting an error doing the first directory listing when I connected to the server as an authenticated user, anonymous listing worked fine. Here was the not helpful at all traceback:<br />
<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] DTPFactory.setTimeout set to 10 seconds</blockquote>
<blockquote class="tr_bq">
2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] DTPFactory starting on 62816</blockquote>
<blockquote class="tr_bq">
2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] Starting factory <twisted .protocols.ftp.dtpfactory="" 0x105471f80="" at="" instance=""></twisted></blockquote>
<blockquote class="tr_bq">
2013-09-02 13:03:54-0700 [twisted.protocols.ftp.DTPFactory] DTPFactory.buildProtocol</blockquote>
<blockquote class="tr_bq">
2013-09-02 13:03:54-0700 [twisted.protocols.ftp.DTPFactory] cancelling DTP timeout</blockquote>
<div>
</div>
</blockquote>
<br />
Oh of course! The DTPFactory/ProtcolWrapper blah blah - no body cares!<br />
<br />
Silly thing was trying to read `/home/<logged-in-username>` - which while probably useful in on a Linux machine wasn't so helpful on my MacBook.</logged-in-username><br />
<br />
Here's what I ended up with:<br />
<br />
<h2>
"simple" FTP server on a Mac</h2>
<pre class="brush:python">#! /usr/bin/env python
import os
from twisted.internet import reactor
from twisted.protocols.ftp import FTPFactory
from twisted.protocols.ftp import FTPRealm
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse as \
InMemoryDB
PASSWORD = ''
users = {
os.environ['USER']: PASSWORD
}
p = Portal(FTPRealm('./', userHome='/Users'),
( AllowAnonymousAccess(),
InMemoryDB(**users),)
)
f = FTPFactory(p)
reactor.listenTCP(2121, f)
reactor.run()
</pre>
<div>
<br /></div>
^ login with your username and blank password; it's not secure, duh.<br />
<br />clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com2tag:blogger.com,1999:blog-7119494240114103556.post-63459337512704569152013-03-13T14:10:00.000-07:002013-03-13T14:10:04.934-07:00Short commit SHA in Jenkins jobs' build nameI always forget this, and spend way too much time looking it up. This is the part I'm looking for:<br />
<blockquote class="tr_bq">
${GIT_REVISION,length=8}</blockquote>
Hi future me.<br />
<br />
If you're not me, you probably already have the <a href="https://wiki.jenkins-ci.org/display/JENKINS/GitHub+Plugin">GitHub Plugin</a> installed, which pulls in the base <a href="https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin">Git Plugin</a>. And if you started reading on the wiki page for the git plugin you may have noticed a reference to a environment variable available as GIT_COMMIT. After some bit of searching you bump into the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Build+Name+Setter+Plugin">Build Name Setter Plugin</a>, and try adding some stuff in the "Build Name" field on the job configuration maybe like:<br />
<blockquote class="tr_bq">
${GIT_COMMIT}-${BUILD_NUMBER}</blockquote>
Which doesn't work, so you go back to more important stuff.<br />
<br />
Or maybe you get lucky and realize the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Token+Macro+Plugin">Token Macro Plugin</a> is involved and stumble across the example there. Which is hidden in-between some Java you can barely look at, is the small blurb at the end of a paragraph which mentions "${GIT_REVISION,length=8}".<br />
<br />
What was interesting there for me was that the build name setter relies completely on the token macro plugin for the string expansion, and pulling strings out of the ENV is not it's primary purpose. There's a special syntax even:<br />
<blockquote class="tr_bq">
${ENV,var="GIT_COMMIT"}</blockquote>
Which if you discovered, probably frustrated you, since can't seem to use the ENV vars you export during the build script, or even do basic bash variable manipulation like ${GIT_COMMIT:0:8}.<br />
<br />
At this point you're realizing the fact that the token macro expansion only looks like bash variable expansion to confuse you.<br />
<br />
Jenkins plugins can and do internally in their java guts define and export sometimes parameterized "tokens" which are exported for the specific purpose of being available to the token macro plugin which the build name setter users.<br />
<br />
I honestly had no idea how <i>anyone</i> was supposed to know how this stuff worked together short of trolling through the git plugin's source like I did and reading <a href="https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/hudson/plugins/git/GitRevisionTokenMacro.java">GitRevisionTokenMacro</a>. But when I looked at the <a href="https://github.com/jenkinsci/git-plugin/commit/9e5847264b4d6b814d017f76e2e93458b644b0f7">commit</a>, my eyes were opened!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://wiki.jenkins-ci.org/download/attachments/54723366/image9a.png?version=3&modificationDate=1335886541000" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://wiki.jenkins-ci.org/download/attachments/54723366/image9a.png?version=3&modificationDate=1335886541000" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Jenkin's plugin system allows authors to write these html snippets which will be inlined into the job configuration page!? This is why I can never seem to find "the docs" for a plugin - they're just added in all over the place whenever you install them - wherever it seems relevant to the author who wrote them!<br />
<br />
So my new plan is to "always click the ? first" - why wasn't that my old plan?clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com3tag:blogger.com,1999:blog-7119494240114103556.post-21705621546459751012012-08-17T11:48:00.003-07:002012-08-17T11:48:39.974-07:00Default Devstack Cirros Image "root" passwordIf you forget to send in an ssh key for injection when you boot the devstack default "cirros" image you can still log-in with password auth.<br />
<br />
username: cirros<br />
password: cubswin:)<br />
<br />
that's "cubswin" followed by a "colon" and "close-paren" (smily face)<br />
<br />
e.g.<br />
<br />
<pre>clayg@devstack:~$ ssh cirros@10.0.0.2
Warning: Permanently added '10.0.0.2' (RSA) to the list of known hosts.
cirros@10.0.0.2's password: cubswin:)
</pre>
<br />
Just as an aside, the dropbear ssh server on the cirros image allows root login by default, but the root user's password on the image is basically disabled:<br />
<br />
<pre> root:!$1$LJwQnqlv$DK6oKqcTq9Rf2ClC.kMa3/:10933:0:99999:7:::
cirros:$1$LJwQnqlv$DK6oKqcTq9Rf2ClC.kMa3/:10933:0:99999:7:::
</pre>
<br />
... that leading "!" in the encrypted password field for "root" in /etc/shadow makes all the difference. If you remove it, you can see it's the same hash as the cirros user and the "cubswin:)" password works for root too.<br />
<br />
If you're just having trouble getting into ssh/port 22, a bit of security groups should square you away:<br />
<br />
<pre>nova secgroup-add-rule default tcp 22 22 0.0.0.0/0
</pre>
clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com2tag:blogger.com,1999:blog-7119494240114103556.post-76636367574757263662010-08-02T08:20:00.000-07:002010-08-02T08:20:14.932-07:00Commits to RediscoSo this is kinda cool, my first pull request on github was accepted:<br />
<a href="http://github.com/iamteem/redisco/commit/83f8f92b7120691a8bbbaa433a1907b7ad991d77">test</a>, <a href="http://github.com/iamteem/redisco/commit/27d64e001e0972691012f0c7977a2d73eb16136a">fix</a><br />
<br />
<a href="http://github.com/iamteem/redisco">Redisco</a>, is a neat project:<br />
<br />
<br />
<blockquote>Redisco allows you to store objects in <a class="reference external" href="http://code.google.com/p/redis/">Redis</a>. It is inspired by the Ruby library <a class="reference external" href="http://github.com/soveran/ohm/">Ohm</a> and its design and code are loosely based on Ohm and the Django ORM. It is built on top of <a class="reference external" href="http://github.com/andymccurdy/redis-py/">redis-py</a>. It includes container classes that allow easier access to Redis sets, lists, and sorted sets.</blockquote>I'm excited about using redisco in a project I'm working on for work that has a django/jQuery front end and <a href="http://ask.github.com/celery/index.html">celery</a>/<a href="http://code.google.com/p/redis/">redis-server</a> back end. Redisco itself is a pretty new project (initial commit <a href="http://github.com/iamteem/redisco/commit/7941733e4854acf4472d8804db1cbdf91a01ded7">May 13, 2010</a> ) but I think it serves and important niche, allowing a lower barrier of entry to projects that want to move away from a RDMS data-store and get their first taste of <a href="http://en.wikipedia.org/wiki/NoSQL">NoSQL</a>. Plus, the author was quick to help me fix problems that were effecting ME.<br />
<br />
In fact, you can check it out right now - my bug fixes are already available on PyPI:<br />
<pre>$ pip install redisco
Downloading/unpacking redisco
Downloading redisco-0.1.1.tar.gz
Running setup.py egg_info for package redisco
Installing collected packages: redisco
Running setup.py install for redisco
Successfully installed redisco </pre>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-45243526864707504192010-07-01T11:04:00.000-07:002010-07-02T09:20:27.467-07:00Py3K - PEP 355So, we still don't get object oriented path manipulation in Py3K<br />
<br />
<pre class="brush:py">$ python3
Python 3.1.1+ (r311:74480, Nov 2 2009, 14:49:22)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named path
>>>
</pre><br />
Is there still <a href="http://mail.python.org/pipermail/python-dev/2006-September/069087.html">hope</a>?<br />
<blockquote><pre>OK. Pronouncement: PEP 355 is dead. The authors (or the PEP editor)
can update the PEP.
I'm looking forward to a new PEP.
--Guido</pre></blockquote><a href="http://www.python.org/dev/peps/pep-0355/">PEP 355</a> died out years ago, but the <a href="http://pypi.python.org/pypi/path.py/">reference implementation</a> still exists, and IMHO is still garbage. Will there ever be another attempt at a stdlib <a href="http://wiki.python.org/moin/PathClass">PathClass</a>?clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-33537935803435574392010-05-24T11:34:00.000-07:002010-05-24T11:34:31.715-07:00Happy Daddy!<div style="margin: 0px auto 10px; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX6D8gg3HDgB5IiBHvMvskcAfouq9cNz0Ruo3X2lKCK7QWkjebBiWUIer4oKJw8J-NHXbIc_8wsKR2fUthpqRXhhPkQe3mHGVNq1H5SukJrAuZDny2827P6aNb-7epYIJhfMDVcekvi_y6/s1600/IMG_0867.JPG"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX6D8gg3HDgB5IiBHvMvskcAfouq9cNz0Ruo3X2lKCK7QWkjebBiWUIer4oKJw8J-NHXbIc_8wsKR2fUthpqRXhhPkQe3mHGVNq1H5SukJrAuZDny2827P6aNb-7epYIJhfMDVcekvi_y6/s400/IMG_0867.JPG" /></a> </div><div style="text-align: center;"><a href="http://en.wikipedia.org/wiki/Twin#Monozygotic_twins">Monozygotic</a> twin boys!</div>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com1tag:blogger.com,1999:blog-7119494240114103556.post-10282619638832172732010-03-01T19:43:00.000-08:002010-03-01T19:43:06.643-08:00How vain am I?This vain:<br />
<a href="http://code.djangoproject.com/ticket/5786">http://code.djangoproject.com/ticket/5786</a><br />
<h3 id="comment:36">changed by jacob </h3><h3 id="comment:36"><a class="anchor" href="http://code.djangoproject.com/ticket/5786#comment:36" title="Permalink to comment:36"> </a></h3><strong>resolution</strong> set to <em>fixed</em><br />
<br />
Thanks to alextreme, lbruno, and clayg.<br />
<em> </em><br />
<em><br />
</em><br />
Yeah, that's MY bug fix in your django 1.2 (currently in beta, <a href="http://www.djangoproject.com/download/">get your official copy</a> March 9th!)<br />
<br />
The really sad part is, it's not even really MY code change, not REALLY. I just re-compiled some other guys patch as a top level diff and updated the tests... but I'm still TOTALLY jazzed about being the last submitter before the final commit. Wait, is that vain? Or just totally lame?clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com2tag:blogger.com,1999:blog-7119494240114103556.post-40198433344451171292009-10-06T20:21:00.000-07:002009-10-06T20:21:57.030-07:00Python over-riding options with ConfigParser and OptionParserIt looks like it may be woth looking into <a href="http://code.google.com/p/argparse/">argparse</a> (non-standard library)<br />
<br />
"""<br />
The basic idea is that config information can be pulled out of <br />
<br />
* Global config file<br />
* User local config file<br />
* environment variables<br />
* command line<br />
<br />
Higher priority comes later in the list. <br />
"""<br />
<br />
But here's an implementation of cascading config file/command line options with ConfigParser and OptionParser - without subclassing.<br />
<br />
Basically you just...<br />
<br />
<ul><li>Set global defaults in at the top of the module in the constants section</li>
<li>Use those to set defaults in the config object</li>
<li>Read in the config file possibly over-riding defaults</li>
<li>Read out the values in the config object to a dictionary</li>
<li>Use THOSE values to set the command line defaults</li>
</ul><br />
Then you can get any option you need out of the command line options argument<br />
<br />
It's not very flexible, but it uses standard libraries and should be fairly obvious.<br />
<br />
<br />
<pre class="brush:python">#! /usr/bin/env python
__doc__ = """
example of cascading options with ConfigParser and OptionParser
"""
# imports
import sys
from ConfigParser import ConfigParser
from optparse import OptionParser
# constants
default_option = 'default_value'
default_toggle = True
# classes
# internal functions
def main():
# create a config parser
# config parser objects store all options as strings
config = ConfigParser({'option':str(default_option),
'toggle':str(default_toggle),
})
config.read('example.cfg')
# create command line option parser
parser = OptionParser("%prog [options]" + __doc__.rstrip())
# configure command line options
parser.add_option("-o", "--option", action="store", dest="option", help="set option")
parser.add_option("-t", "--toggleOff", action="store_false", dest="toggle", help="set toggle off")
parser.add_option("-T", "--toggleOn", action="store_true", dest="toggle", help="set toggle on")
# read config objects defaults section into a dictionary
config_options = config.defaults()
# config_options is dictionary of strings, over-ride toggle to bool
config_options['toggle'] = config.getboolean('DEFAULT', 'toggle')
# feed dictionary of defaults into parser object
parser.set_defaults(**config_options)
# parse command line options
(options, args) = parser.parse_args()
print "option: %s" % options.option
if options.toggle:
print "toggle is ON"
else:
print "toggle is OFF"
return 0;
if __name__ == '__main__':
status = main()
sys.exit(status)
</pre>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com1tag:blogger.com,1999:blog-7119494240114103556.post-35187219523159327282009-09-28T20:11:00.000-07:002009-09-28T20:11:39.482-07:00vuze torrents get more peersWhen I only had one or two torrents going at a time they seemed to download slower than my connection should have been able to support. I was looking for ways to maximize my torrent download speed when I found the setting for "maximum number of connections" under options in <a href="http://www.vuze.com/">Vuze</a>.<br />
<br />
Please note all the details here have only been tested to work to improve download speeds of freely distributable copyleft materials such as <a href="http://www.ubuntu.com/getubuntu/downloadmirrors#bt">Ubuntu</a> full DVD images.<br />
<br />
If you're like me, you've already covered the basics (I had "<a href="http://wiki.vuze.com/index.php/Torrent_health">green smiles</a>" and "<a href="http://wiki.vuze.com/index.php/NAT">open nat</a>") but if not, you may want to <a href="http://forum.vuze.com/thread.jspa?threadID=78768">start with that</a>.<br />
<br />
<br />
My problem was that I was only connecting to 20-30 peers out of thounds of seeders and my down speed for a single torrent capped out around 150-170 kB/s. I discovered that the max number of connections was set to 40, I don't remember changing it - hitting reset seemed to indicate that by default Vuze limits the max number of connections to 50:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"> <a href="http://farm3.static.flickr.com/2589/3964179501_98ec754a6a_o.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="http://farm3.static.flickr.com/2589/3964179501_98ec754a6a_o.gif" width="420" /></a><br />
</div><br />
<div class="separator" style="clear: both; text-align: left;">This number maps directly to the number of peers in your swarm. And mine was set much lower than the recommend "<a href="http://www.azureuswiki.com/index.php/Good_settings">Good Settings</a>" - I went ahead and just disabled it for now:<br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="http://farm3.static.flickr.com/2617/3964179437_c954eff621.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="http://farm3.static.flickr.com/2617/3964179437_c954eff621.jpg" width="420" /></a><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><div class="separator" style="clear: both; text-align: left;"><br />
</div><br />
And I was pretty happy with the results:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://farm3.static.flickr.com/2575/3964985172_8da731ec3b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://farm3.static.flickr.com/2575/3964985172_8da731ec3b.jpg" /></a><br />
</div>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-51248950378186695852009-09-18T19:16:00.000-07:002009-09-18T20:23:08.187-07:00post code snippets in google bloggerHowdy folks, here's some code:<br />
<br />
<pre class="brush: python">def myfunc(*args, **kwargs):
for arg in args:
print arg
for k,v in kwargs.items():
print k,v </pre><br />
First thing you need to do is make sure you're useing the Google Blogger's "Updated Editor".<br />
<ol><li>Log into Blogger, and from the Dashboard goto Settings.</li>
<li>On the Settings tab, down towards the bottom under "Global Settings"</li>
<li>"Select post editor" - "Updated editor"</li>
</ol><br />
The updated editor actually solved a lot of the "problems" I was having with pre formatted text getting messed up when I went back and forth between compose and edit html.<br />
<br />
But to post some really good looking source code you have to use <a href="http://code.google.com/p/syntaxhighlighter/">SyntaxHighlighter</a>.<br />
<br />
SyntaxHighlighter is written in JavaScript, and there some css styling as well. To use SyntaxHighlighter on your google blogger page, you have to edit the template.<br />
<br />
Under the Layout tab, go to "Edit HTML"<br />
In the "Edit Template" box, scroll down toward the bottom.<br />
You want to add the .js/.css references just before the last closing head tag (</head>):<br />
<br />
<pre class="brush: html"><link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='Stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='Stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js ' type='text/javascript'/>
</pre><div class="separator" style="clear: both; text-align: center;"><br />
</div><div class="separator" style="clear: both; font-family: Times,"Times New Roman",serif; text-align: left;"><i>SyntaxHighlighter makes it easy to copy the above snippet - just hover over the code, and click the "copy-to-clipboard" icon: <a href="http://alexgorbatchev.com/pub/sh/current/styles/page_white_copy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://alexgorbatchev.com/pub/sh/current/styles/page_white_copy.png" /></a></i><br />
</div><br />
<br />
Then at the very bottom, you also want to set some global parameters just before the last closing body tag (</body>):<br />
<br />
<pre class="brush: html"><script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>
</pre><br />
<br />
You can then make highlighted code by wrapping your code snippet's in a <br />
<pre> tag with a specially formatted "class" attribute, like this:<br />
<br />
<pre class="brush: html"><pre class="brush:js">
alert("Hello world");
</pre>
</pre><br />
Result:<br />
<br />
<pre class="brush:js">alert("Hello world");
</pre><br />
You can add brushes for different languages by adding more JavaScript files to your Template. The author hosts the most recent version here:<br />
<a href="http://alexgorbatchev.com/pub/sh/current/">http://alexgorbatchev.com/pub/sh/current/</a><br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><b>N.B. Just because you put html inside a <pre> tag doesn't magically make html parsers know you're try to post literal HTML code - you still have to escape valid HTML using &lt; and the like or it won't be rendered correctly. Try something like <a href="http://www.string-functions.com/htmlencode.aspx">this</a>.</b><br />
</div><br />
I cobbled together the following steps from these sources: <br />
<a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter Homepage</a> <br />
<a href="http://developertips.blogspot.com/2007/08/syntaxhighlighter-on-blogger.html">Using SyntaxHighlighter on BLOGGER</a> <br />
<a href="http://urenjoy.blogspot.com/2008/10/publish-source-code-in-blogger.html">Publish Source code in Blogger</a> <br />
<a href="http://alexgorbatchev.com/forums/comments.php?DiscussionID=82&page=1#Item_0">Syntax Highlighter NOT working in my googleblog</a>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com1tag:blogger.com,1999:blog-7119494240114103556.post-22722443522838424362009-09-16T20:06:00.000-07:002009-09-16T20:14:35.580-07:00Newegg Shipping Halo: ODST earlyI pre-ordered from Newegg, and it arrived Monday Sept 14th.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/wikipedia/en/archive/9/9a/20090611232449%21Halo_3_ODST_Box_Art.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 454px; height: 640px;" src="http://upload.wikimedia.org/wikipedia/en/archive/9/9a/20090611232449%21Halo_3_ODST_Box_Art.png" alt="" border="0" /></a>Play'd it through the first night on Heroic (6 hours).<br />It was fun.<br />No dual wielding.<br />No battle rifle.<br />You move surprisingly fast with a turret.<br />No match-making for firefight (yet?).<br /><br />Halo: Mythic is a separate disk, with:<br /><ul><li>Matchmaking</li><li>Custom Games</li><li>Theater</li><li>Forge</li></ul>...and ALL the multilayer maps.<br /><br />Heretic is Midship.<br />Cathedral is like Epitaph, but VERY symmetrical.<br />Longshore seems pretty cool.<br /><br />You play them off Mythic, you don't download them to your HD. Your Halo3 saved content is available from with-in Mythic automatically.clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-8871332950896748592009-08-14T20:07:00.000-07:002009-08-14T22:47:18.055-07:00TypeError: dict objects are unhashableMy mom told me to update my blog. Hi mom.<br /><br />I've been wanting to write this one for awhile anyway.<br /><br />In retrospect it was rather naive - but I wonder who hasn't at one time tried to create a set of dictionaries:<br /><span style="white-space: pre; font-weight: bold;font-family:courier new;" ><br />|</span><span style="white-space: pre; font-weight: bold;font-family:courier new;" >>></span><span style="white-space: pre; font-weight: bold;font-family:courier new;" >> parts = [<br />|... {'id':1, 'desc':'widget', 'detail':'rear widget'},<br />|... {'id':1, 'desc':'widget', 'detail':'front widget'},<br />|... {'id':2, 'desc':'gear', 'size':4},<br />|... {'id':3, 'desc':'cog', 'type':'green'},<br />|... ]<br />|>>> myset = set(parts)<br />|Traceback (most recent call last):<br />| File "<stdin>", line 1, in ?<br />|TypeError: dict objects are unhashable<br /></stdin></span><br /><br /><stdin>What did I expect it to do? I think the first time I tried this it seemed more reasonable. I think my list of dictionaries actually contained exactly the same keys, with some exact duplicates and I needed to <a href="http://www.urbandictionary.com/define.php?term=uniquify">uniquify</a> the list. I acctually ended up doing something like the example at the bottom...<br /><br />Dict objects are not hashable, read about <a href="http://en.wikipedia.org/wiki/Hash_table">hash tables</a> and <a href="http://en.wikipedia.org/wiki/Set_data_structure">sets</a> if it isn't obvious why it's important that objects in a set support a __hash__ method.<br /><br />A hash function is really simple idea:<br />two <span style="font-style: italic;">equal </span>objects <span style="font-weight: bold;">MUST </span>return the same hash<br />two <span style="font-style: italic;">un-equal </span>objects should <span style="font-weight: bold;">RARELY </span>return the same key<br /><br />But there's no really obvious reasonable way to implement hash on a dictionary of arbitrary keys and values.<br /><br />Here's a couple dumb ideas for adding a hash function to a dict:<br /><br /><span style="white-space: pre; font-weight: bold;font-family:courier new;" ><br />|def __hash__(self):<br />| key = 0<br /><br />|def __hash__(self):<br />| id = 0<br />| for pair in sorted(self.items()):<br />| id += hash(pair)<br />| return hash(id)</span><br /><br />And they "work" - at least in the sense that they remove the error:<br /><span style="white-space: pre; font-weight: bold;font-family:courier new;" ><br />|</span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >>></span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >> class Part(dict):<br />|... def __hash__(self):<br />|... return self['id']<br />|...<br />|>>> myset = set([Part(x) for x in parts])<br />|>>> for part in myset:<br />|... print part<br />|...<br />|{'desc': 'widget', 'detail': 'rear widget', 'id': 1}<br />|{'desc': 'gear', 'id': 2, 'size': 4}<br />|{'type': 'green', 'id': 3, 'desc': 'cog'}<br />|{'desc': 'widget', 'detail': 'front widget', 'id': 1}</span><br /><br />And that's great, if my parts list contained EXACTLY equal dictionaries they would be removed - I could turn it back into a list an continue on with everything uniquified! It might be worth nothing that the two parts with 'id' = 1 were not considered equal just because they returned the same hash. When there is a <a href="http://en.wikipedia.org/wiki/Hash_collision">hash collision</a>, the inhereited __eq__() method recognized that 'rear widget' != 'left widget' and that the two parts were distinct.<br /><br />But what's really interesting what I've done by <a href="http://pyref.infogami.com/__hash__">making a mutable object hashable</a>...<br /><br />It can be used as a dictionary key with surprisingly bad results:<br /><span style="white-space: pre; font-weight: bold;font-family:courier new;" ><br />|>>> part.__class__ # part is an instance of my Part class<br />|&ltclass '__main__.Part'><br />|>>> part # a mutable object with a hash function<br />|{'desc': 'widget', 'detail': 'front widget', 'id': 1}<br />|>>> mydict = {} # mydict is a plain dictionary<br />|>>> mydict[part] = 1 # i can use part as a key!<br />|>>> part['id'] = 2 # I then modify the key<br />|>>> mydict[part] # there is no value assigned to this new "modified" key<br />|Traceback (most recent call last):<br />|File "<stdin>", line 1, in ?<br />| KeyError: {'desc': 'widget', 'detail': 'front widget', 'id': 2}<br />|>>> mydict # or is there?<br />|{{'desc': 'widget', 'detail': 'front widget', 'id': 2}: 1}<br /></stdin></span></stdin><br />Here's a fairly reasonable attempt at making a custom class that is <span style="font-weight: bold;">mostly</span> mutable dictionary, but has a safe and reasonable hash function. I'll also over-ridden __eq__ to ignore minor differences in the 'detail' between objects.<br /><br /><span style="white-space: pre; font-weight: bold;font-family:courier new;" ><br /></span><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >>>> class Part(dict):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... def __init__(self, part_dict):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... if 'id' not in part_dict:<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... raise TypeError("Parts must have an id")<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... dict.__init__(self, part_dict)<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... def __setitem__(self, key, value):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... if key == 'id':<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... raise ValueError("Part id's can't change - create a new part")<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... return dict.__setitem__(self, key, value)<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... def __hash__(self):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... return self['id']<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... def __eq__(self, other):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... a = self.copy()<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... del a['detail']<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... b = other.copy()<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... del b['detail']<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... return a == b<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >...<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >>>> for part in set([Part(x) for x in parts]):<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >... print part<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >...<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >{'desc': 'widget', 'detail': 'rear widget', 'id': 1}<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >{'desc': 'gear', 'id': 2, 'size': 4}<br /></span></stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >|</span><stdin><span style="white-space: pre; font-weight: bold;font-family:courier new;" >{'type': 'green', 'id': 3, 'desc': 'cog'}<br /></span><br />So if parts is a list of build materials, and you wanted to know how many distinct parts it takes to build this thing... the above Part class might be the right track. Aside from the ambiguity of added by over-riding "==" like that... dose anyone see any other problems with this?<br /></stdin>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com3tag:blogger.com,1999:blog-7119494240114103556.post-44010133379159252272009-02-05T18:03:00.000-08:002009-02-09T11:28:55.234-08:00pymssql and sqlalchemyAt the time of this writing the latest version of <a href="http://www.sqlalchemy.org/">sqlalchemy</a> (0.5.2) does not support the recent re-write of <a href="http://pymssql.sourceforge.net/">pymssql</a> (1.0.0), which was released last week.<br /><br /><p><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 188px; height: 52px;" src="http://www.sqlalchemy.org/_img/sqla-logo6.gif" alt="" border="0" /></p><br />attempting to <a href="http://www.sqlalchemy.org/docs/05/dbengine.html#create-engine-url-arguments">create a sqlalchemy engine object</a> will result in an exception:<p><span style="font-family:courier new;"> File "/lib/python2.5/site-packages/SQLAlchemy-0.5.2-py2.5.egg/sqlalchemy/databases/mssql.py", line 1294, in create_connect_args<br />self.dbapi._mssql.set_query_timeout(self.query_timeout)<br />AttributeError: 'module' object has no attribute 'set_query_timeout'<br />>>></span><br /></p><p>According to the pymssql <a href="http://pymssql.sourceforge.net/index.html">news</a> page, the low level module in this major version release is not backwards compatible:</p><ul><li>BEWARE however, if you were using the lower level <b>_mssql module</b>, it <b>changed in incompatible way</b>. You will need to change your scripts, or continue to use pymssql 0.8.0. This is why major version number was incremented.</li></ul>As a 'work-around' you can always install an older stable version of pymssql (0.8.0)<p><span style="font-family:courier new;">$easy_install pymssql==0.8.0</span><br /></p>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com2tag:blogger.com,1999:blog-7119494240114103556.post-50636020750712625022009-01-06T23:00:00.000-08:002009-01-06T23:13:08.586-08:00Quake Live Beta Invites Crash TestI finally got my QuakeLive Closed Beta Account - and you may have too!<br /><br />The QuakeLive Beta is warming up!<br /><blockquote><span style="font-style: italic;">on Monday we're going to be sending out our largest number of new beta invites ever - hopefully more than doubling our current active player base.</span></blockquote>- QuakeLive News<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.quakelive.com/"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 101px;" src="http://farm4.static.flickr.com/3421/3176444654_f24aa44786_o.jpg" alt="" border="0" /></a><br />id is planning a big "crash test" on Wednesday afternoon - Jan 7th 2009. Go check your email, and be ready for a three part sign-up and activation rig-o-mo-rag...<br /><br />id has asked us to "BRING THE HEAT"clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com1tag:blogger.com,1999:blog-7119494240114103556.post-65015962896319137022008-12-29T20:47:00.000-08:002008-12-30T04:09:48.058-08:00Set PYTHONPATHTo set the enviornment variable <a href="http://docs.python.org/using/cmdline.html#envvar-PYTHONPATH">PYTHONPATH</a> in bash:<br />export PYTHONPATH=/path/to/modules<br /><br />just setting PYTHONPATH=/path/to/modules won't work - you have to use export. If you want a variable passed on to a child processes it has to show up when you type 'env'<br /><br />But since you used export - next time you start python, '/path/to/modules' will automatically be appended to the front of your sys.path<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm4.static.flickr.com/3104/3149920792_eebfa3c85d_o.jpg"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 500px;" src="http://farm4.static.flickr.com/3104/3149920792_eebfa3c85d_o.jpg" alt="" border="0" /></a><br />Obviously '/path/to/modules' should be the full path to whereever you're keeping your modules - something like /home/clayg/lib. (relative path's will work, but not ~/ , probably best to avoid both) You can separate multiple directories with a colon:<br /><br />export PYTHONPATH=/path/to/modules:/path/to/other/modules<br /><br />Setting python path is handy if, for example, you are using <a href="http://peak.telecommunity.com/DevCenter/setuptools">setuptools</a> or <a href="http://docs.python.org/library/distutils.html">distutils</a> to install python modules on a system which you do not have root privileges.<br /><br />Just download the source dist and find the directory with setup.py, then run:<br />python setup.py build<br /><br />Then re-locate the folder with the __init__.py (usually ./build/lib/packagename) to your /path/to/modules folder.<br /><br />if you want your PYTHONPATH to stick around - you should add it to <a href="http://thoughtsbyclayg.blogspot.com/2008/01/how-does-bash-work.html">.bashrc</a><br /><br />import packagename should be good to go!clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0tag:blogger.com,1999:blog-7119494240114103556.post-49279267664174504252008-12-04T03:20:00.000-08:002009-04-23T07:03:51.138-07:00get dates from excel with python xlrda1_as_datetime = datetime.datetime(*xlrd.xldate_as_tuple(a1, 0))<br /><br />UPDATE: Please read the discussion of the second argument to xldate_as_tuple - "datemode" in the comments section of this post before using this example. It is LIKELY that hard-coding the "datemode" option will not meet your long term needs.<br /><br />I had to piece this line together from two other articles, sorry don't remember which.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm4.static.flickr.com/3207/3149135417_68b0d4aeb9_o.jpg"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 216px; height: 144px;" src="http://farm4.static.flickr.com/3207/3149135417_68b0d4aeb9_o.jpg" alt="" border="0" /></a><br />Full Example:<br /><br /><span style="font-family:courier new;">>>> import datetime, xlrd<br />>>> book = xlrd.open_workbook("myfile.xls")<br />>>> sh = book.sheet_by_index(0)<br />>>> a1 = sh.cell_value(rowx=0, colx=0)<br />>>> print "Cell A1 is ", a1<br />Cell A1 is 39811.0<br />>>> a1_as_datetime = datetime.datetime(*xlrd.xldate_as_tuple(a1, book.datemode))<br />>>> print 'datetime: %s' % a1_as_datetime<br />datetime: 2008-12-29 00:00:00<br />>>></span><br /><br />This might make more sense if you're familiar with <a href="http://www.lexicon.net/sjmachin/xlrd.html">xlrd</a> -<b> A Python module for extracting data from MS Excel ™ spreadsheet files.</b><br /><br />If you <span style="font-style: italic;">are </span>familiar with xlrd, then the only part really worth discussing is <a href="http://www.lexicon.net/sjmachin/xlrd.html#xlrd.xldate_as_tuple-function">xldate_as_tuple</a>, which will convert the float that excel is using to store the date as something more useful, like a tuple:<br />(2008, 12, 29, 0, 0, 0)<br /><br />Note that the first argument to the xldate_as_tuple function is the variable I defined as a1. xldate_as_tuple will not accept a cell reference 'a1' or some such thing - you have to give it the float!<br /><br />The <a href="http://www.python.org/doc/2.5.2/lib/datetime-datetime.html">datetime</a> module has a constructor for dates that requires at minimum three positional arguments:<br /><table cellpadding="0" cellspacing="0"><tbody><tr valign="baseline"><td><nobr><b><tt id="l2h-634" class="class">datetime</tt></b>(</nobr></td> <td><var>year, month, day</var><big>[</big><var>, hour</var><big>[</big><var>, minute</var><big>[</big><var>, second</var><big>[</big><var>, microsecond</var><big>[</big><var>, tzinfo</var><big>]</big><var></var><big>]</big><var></var><big>]</big><var></var><big>]</big><var></var><big>]</big><var></var>)</td></tr></tbody></table><br /><span style="font-family:courier new;">This would also work:</span><br /><span style="font-family:courier new;">a1_as_date = datetime.date(*xlrd.xldate_as_tuple(a1, 0)[:3])<br /><br /></span>You can pass the items of the tuple as positional arguments by prefacing the tuple with an <a href="http://en.wikipedia.org/wiki/Asterisk">asterisk</a> (wtg python!)clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com10tag:blogger.com,1999:blog-7119494240114103556.post-71800662546507992752008-10-17T01:36:00.001-07:002008-10-18T00:03:45.102-07:00Simple example of Threads in PythonThe first time it was immediately obvious to me that there would be a significant gain from 'threading' a program I had written - was in the context of screen scraping. I had a handful of HTTP GET requests from almost 20 pages that were being processed one... after... the... other. I realized of course that if I would just start the next request before waiting on the last one to finish - the entire process would be over much more quickly.<br /><br />In this example the screen scraping 'worker' function is replaced with a simpler 'random wait' function:<br /><pre><br />#! /usr/bin/env python<br /><br />import sys<br />import threading<br />import time<br />import random<br /><br /># The worker function does the processing<br />def worker(arg):<br /> arg = random.randint(2,10)<br /> time.sleep(arg)<br /> return arg<br /><br /># The myThreadObj wraps the worker function in a thread<br />class myThreadObj(threading.Thread):<br /> def __init__(self, arg):<br /> threading.Thread.__init__(self)<br /> self.arg = arg<br /> self.value = 0<br /> def run(self):<br /> self.value = worker(self.arg)<br /> print 'Thread %d Ended.' % self.arg<br /><br /># my array of arguments to be processed by the worker function<br />myArgs = range(5)<br /><br /># create a myThreadObj to process each argument<br />myThreadList = []<br />for i in myArgs:<br /> myThreadList.append(myThreadObj(i))<br /> # and start it immediately<br /> myThreadList[i].start()<br /><br /># wait for all threads to finish<br />for each in myThreadList:<br /> each.join()<br /><br />print 'All threads have completed.'<br /><br />for i in myArgs:<br /> print "myThreadList[%d] = %d" % (i, myThreadList[i].value)<br /></pre><br />The myThreadObj wrapper should accept whatever arguments you normally pass to the worker, and when the worker is completed - it will store the returned value in 'self.value'<br /><br />The .join() function blocks until the .isAlive() method would return false. I process each thread iteratively to verify that all have completed. It doesn't matter if .join() blocks for 8 seconds while it's waiting on the first thread, or if it gets to a thread that's already been completed for 6 seconds cause an earlier .join was waiting on a previous thread that took longer. The point is that, by the time all of the .join() statements complete - ALL THREADS HAVE FINISHED.<br /><br />Once the threads are done we expect myThreadObj.value to contain the return value of the worker function.<br /><br />If your 'worker' function is something like an API call, or database query - anything with some built in lag from a system that's designed to serve multiple simultaneous requests - as long as you can queue them up - threading will provide a significant improvement.<br /><br />e.g.<br /><pre><br />clayg@m-net:~$ cat nonthread.py<br />#! /usr/bin/env python<br /><br />import sys<br />import threading<br />import time<br />import random<br /><br /># The worker function does the processing<br />def worker(arg):<br />arg = random.randint(2,10)<br />time.sleep(arg)<br />return arg<br /><br />myArgs = range(5)<br /><br />for i in myArgs:<br />print "myThreadList[%d] = %d" % (i, worker(i))<br />clayg@m-net:~$ time ./nonthread.py ; echo ; time ./simplethread.py<br />myThreadList[0] = 5<br />myThreadList[1] = 5<br />myThreadList[2] = 10<br />myThreadList[3] = 2<br />myThreadList[4] = 9<br /><br />real 0m31.073s<br />user 0m0.045s<br />sys 0m0.024s<br /><br />Thread 4 Ended.<br />Thread 0 Ended.<br />Thread 1 Ended.<br />Thread 2 Ended.<br />Thread 3 Ended.<br />All threads have completed.<br />myThreadList[0] = 5<br />myThreadList[1] = 10<br />myThreadList[2] = 10<br />myThreadList[3] = 10<br />myThreadList[4] = 2<br /><br />real 0m10.078s<br />user 0m0.045s<br />sys 0m0.030s<br /></pre>clayghttp://www.blogger.com/profile/17201646652441277087noreply@blogger.com0