comparison orpg/networking/meta_server_lib.py @ 96:65c1604e7949 alpha

Traipse Alpha 'OpenRPG' {090924-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary: 00: Update forwards to the 090909-02 Server code that now works. New default Lobby Map, designed for Traipse. Feel free to change it. Updates to Server GUI: * Admin can Ban from Backend. * Prework to modify Ban List in back end. * Server GUI finds your Lobby Name * New users default as Lurker unless a Role is set New Addition to Chat Die Roll commands. Math Ordering. Ex. [(X+Y)dZ]. Currently does pairs only, no nesting either. Cleaner TraipseSuiteAttention portability and clean up in Main (Beta!)
author sirebral
date Thu, 24 Sep 2009 02:05:08 -0500
parents d1aff41c031b
children 9314d63c0941 dcf4fbe09b70
comparison
equal deleted inserted replaced
95:af6bf998f425 96:65c1604e7949
32 META_DEBUG = 0 32 META_DEBUG = 0
33 33
34 __version__ = "$Id: meta_server_lib.py,v 1.40 2007/04/04 01:18:42 digitalxero Exp $" 34 __version__ = "$Id: meta_server_lib.py,v 1.40 2007/04/04 01:18:42 digitalxero Exp $"
35 35
36 from orpg.orpg_version import PROTOCOL_VERSION 36 from orpg.orpg_version import PROTOCOL_VERSION
37 from orpg.orpg_xml import xml 37 from orpg.orpgCore import component
38 import orpg.dirpath 38 from orpg.tools.validate import validate
39 import orpg.tools.validate 39 from orpg.dirpath import dir_struct
40 import urllib 40 import urllib
41 import orpg.minidom 41 import orpg.minidom
42 from threading import * 42 from threading import *
43 import time 43 import time
44 import sys 44 import sys
67 file.close() 67 file.close()
68 68
69 # Remove any leading or trailing data. This can happen on some satellite connections 69 # Remove any leading or trailing data. This can happen on some satellite connections
70 p = re.compile('(<servers>.*?</servers>)',re.DOTALL|re.IGNORECASE) 70 p = re.compile('(<servers>.*?</servers>)',re.DOTALL|re.IGNORECASE)
71 mo = p.search(data) 71 mo = p.search(data)
72 if mo: 72 if mo: data = mo.group(0)
73 data = mo.group(0)
74 73
75 if META_DEBUG: 74 if META_DEBUG:
76 print 75 print
77 print "Got this string from the Meta at " + path + ":" 76 print "Got this string from the Meta at " + path + ":"
78 print "===============================================" 77 print "==============================================="
79 print data 78 print data
80 print 79 print
81 # build dom 80 # build dom
81 xml = component.get('xml')
82 xml_dom = xml.parseXml(data) 82 xml_dom = xml.parseXml(data)
83 xml_dom = xml_dom._get_documentElement() 83 xml_dom = xml_dom._get_documentElement()
84 return xml_dom 84 return xml_dom
85 85
86 def post_server_data( name, realHostName=None): 86 def post_server_data( name, realHostName=None):
87 # build POST data
88 ## data = urllib.urlencode( {"server_data[name]":name,
89 ## "server_data[version]":PROTOCOL_VERSION,
90 ## "act":"new"} )
91 ##
92 if realHostName: 87 if realHostName:
93 data = urllib.urlencode( {"server_data[name]":name, 88 data = urllib.urlencode( {"server_data[name]":name,
94 "server_data[version]":PROTOCOL_VERSION, 89 "server_data[version]":PROTOCOL_VERSION,
95 "act":"new", 90 "act":"new",
96 "REMOTE_ADDR": realHostName } ) 91 "REMOTE_ADDR": realHostName } )
99 #print "Letting meta server decide the hostname to list..." 94 #print "Letting meta server decide the hostname to list..."
100 data = urllib.urlencode( {"server_data[name]":name, 95 data = urllib.urlencode( {"server_data[name]":name,
101 "server_data[version]":PROTOCOL_VERSION, 96 "server_data[version]":PROTOCOL_VERSION,
102 "act":"new"} ) 97 "act":"new"} )
103 98
104 xml_dom = get_server_dom( data , "http://openrpg.sf.net/openrpg_servers.php") 99 #xml_dom = get_server_dom( data , "http://openrpg.sf.net/openrpg_servers.php")#Sourceforge still?
100 path = component.get('settings').get_setting('MetaServerBaseURL') #getMetaServerBaseURL()
101 xml_dom = get_server_dom(data, path)
105 ret_val = int( xml_dom.getAttribute( "id" ) ) 102 ret_val = int( xml_dom.getAttribute( "id" ) )
106 return ret_val 103 return ret_val
107 104
108 def post_failed_connection(id,meta=None,address=None,port=None): 105 def post_failed_connection(id,meta=None,address=None,port=None):
109 # For now, turning this off. This needs to be re-vamped for 106 # For now, turning this off. This needs to be re-vamped for
110 # handling multiple Metas. 107 # handling multiple Metas.
111 return 0 108 return 0
112 # data = urllib.urlencode({"id":id,"act":"failed"}); 109 #data = urllib.urlencode({"id":id,"act":"failed"});
113 # xml_dom = get_server_dom(data) 110 #xml_dom = get_server_dom(data)
114 # ret_val = int(xml_dom.getAttribute("return")) 111 #ret_val = int(xml_dom.getAttribute("return"))
115 # return ret_val 112 #return ret_val
116 113
117 def remove_server(id): 114 def remove_server(id):
118 data = urllib.urlencode({"id":id,"act":"del"}); 115 data = urllib.urlencode({"id":id,"act":"del"});
119 xml_dom = get_server_dom(data) 116 xml_dom = get_server_dom(data)
120 ret_val = int(xml_dom.getAttribute("return")) 117 ret_val = int(xml_dom.getAttribute("return"))
123 120
124 def byStartAttribute(first,second): 121 def byStartAttribute(first,second):
125 # This function is used to easily sort a list of nodes 122 # This function is used to easily sort a list of nodes
126 # by their start time 123 # by their start time
127 124
128 if first.hasAttribute("start"): 125 if first.hasAttribute("start"): first_start = int(first.getAttribute("start"))
129 first_start = int(first.getAttribute("start")) 126 else: first_start = 0
130 else: 127
131 first_start = 0 128 if second.hasAttribute("start"): second_start = int(second.getAttribute("start"))
132 129 else: second_start = 0
133 if second.hasAttribute("start"):
134 second_start = int(second.getAttribute("start"))
135 else:
136 second_start = 0
137 130
138 # Return the result of the cmp function on the two strings 131 # Return the result of the cmp function on the two strings
139 return cmp(first_start,second_start) 132 return cmp(first_start,second_start)
140 133
141 def byNameAttribute(first,second): 134 def byNameAttribute(first,second):
142 # This function is used to easily sort a list of nodes 135 # This function is used to easily sort a list of nodes
143 # by their name attribute 136 # by their name attribute
144 137
145 # Ensure there is something to sort with for each 138 # Ensure there is something to sort with for each
146 139
147 if first.hasAttribute("name"): 140 if first.hasAttribute("name"): first_name = str(first.getAttribute("name")).lower()
148 first_name = str(first.getAttribute("name")).lower() 141 else: first_name = ""
149 else: 142
150 first_name = "" 143 if second.hasAttribute("name"): second_name = str(second.getAttribute("name")).lower()
151 144 else: second_name = ""
152 if second.hasAttribute("name"):
153 second_name = str(second.getAttribute("name")).lower()
154 else:
155 second_name = ""
156 145
157 # Return the result of the cmp function on the two strings 146 # Return the result of the cmp function on the two strings
158
159 return cmp(first_name,second_name) 147 return cmp(first_name,second_name)
160 148
161 149
162 def get_server_list(versions = None,sort_by="start"): 150 def get_server_list(versions = None,sort_by="start"):
163 data = urllib.urlencode({"version":PROTOCOL_VERSION,"ports":"%"}) 151 data = urllib.urlencode({"version":PROTOCOL_VERSION,"ports":"%"})
169 157
170 return_hash = {} # this will end up with an amalgamated list of servers 158 return_hash = {} # this will end up with an amalgamated list of servers
171 159
172 for meta in all_metas: # check all of the metas 160 for meta in all_metas: # check all of the metas
173 161
174 # get the server's xml from the current meta 162 #get the server's xml from the current meta
175 bad_meta = 0 163 bad_meta = 0
176 #print "Getting server list from " + meta + "..." 164 #print "Getting server list from " + meta + "..."
177 try: 165 try: xml_dom = get_server_dom(data=data,path=meta)
178 xml_dom = get_server_dom(data=data,path=meta)
179 except: 166 except:
180 #print "Trouble getting servers from " + meta + "..." 167 #print "Trouble getting servers from " + meta + "..."
181 bad_meta = 1 168 bad_meta = 1
182 169
183 if bad_meta: 170 if bad_meta:
196 # metas in the list. If a match is found, then use the new values. 183 # metas in the list. If a match is found, then use the new values.
197 for n in node_list: 184 for n in node_list:
198 185
199 # set them from current node 186 # set them from current node
200 187
201 if not n.hasAttribute('name'): 188 if not n.hasAttribute('name'): n.setAttribute('name','NO_NAME_GIVEN')
202 n.setAttribute('name','NO_NAME_GIVEN')
203 name = n.getAttribute('name') 189 name = n.getAttribute('name')
204 if not n.hasAttribute('num_users'): 190 if not n.hasAttribute('num_users'): n.setAttribute('num_users','N/A')
205 n.setAttribute('num_users','N/A')
206 num_users = n.getAttribute('num_users') 191 num_users = n.getAttribute('num_users')
207 if not n.hasAttribute('address'): 192 if not n.hasAttribute('address'): n.setAttribute('address','NO_ADDRESS_GIVEN')
208 n.setAttribute('address','NO_ADDRESS_GIVEN')
209 address = n.getAttribute('address') 193 address = n.getAttribute('address')
210 if not n.hasAttribute('port'): 194 if not n.hasAttribute('port'): n.setAttribute('port','6774')
211 n.setAttribute('port','6774')
212 port = n.getAttribute('port') 195 port = n.getAttribute('port')
213 n.setAttribute('meta',meta) 196 n.setAttribute('meta',meta)
214 end_point = str(address) + ":" + str(port) 197 end_point = str(address) + ":" + str(port)
215 if return_hash.has_key(end_point): 198 if return_hash.has_key(end_point):
216 if META_DEBUG: print "Replacing duplicate server entry at " + end_point 199 if META_DEBUG: print "Replacing duplicate server entry at " + end_point
226 return_list = return_hash.values() 209 return_list = return_hash.values()
227 210
228 # sort them by their name attribute. Uses byNameAttribute() 211 # sort them by their name attribute. Uses byNameAttribute()
229 # defined above as a comparison function 212 # defined above as a comparison function
230 213
231 if sort_by == "start": 214 if sort_by == "start": return_list.sort(byStartAttribute)
232 return_list.sort(byStartAttribute) 215 elif sort_by == "name": return_list.sort(byNameAttribute)
233 elif sort_by == "name":
234 return_list.sort(byNameAttribute)
235 216
236 # Add each node to the DOM 217 # Add each node to the DOM
237 for n in return_list: 218 for n in return_list: return_dom.appendChild(n)
238 return_dom.appendChild(n)
239 return return_dom 219 return return_dom
240 220
241 ## List Format: 221 ## List Format:
242 ## <servers> 222 ## <servers>
243 ## <server address=? id=? name=? failed_count=? > 223 ## <server address=? id=? name=? failed_count=? >
255 if META_DEBUG: print " Authoritive Meta: "+str(authoritative) 235 if META_DEBUG: print " Authoritive Meta: "+str(authoritative)
256 metas = metaservers[0].getElementsByTagName("meta") # get the list of metas 236 metas = metaservers[0].getElementsByTagName("meta") # get the list of metas
257 if META_DEBUG: print " Meta List ("+str(len(metas))+" servers)" 237 if META_DEBUG: print " Meta List ("+str(len(metas))+" servers)"
258 try: 238 try:
259 metacache_lock.acquire() 239 metacache_lock.acquire()
260 ini = open(orpg.dirpath.dir_struct["user"]+"metaservers.cache","w") 240 ini = open(dir_struct["user"]+"metaservers.cache","w")
261 for meta in metas: 241 for meta in metas:
262 if META_DEBUG: print " Writing: "+str(meta.getAttribute('path')) 242 if META_DEBUG: print " Writing: "+str(meta.getAttribute('path'))
263 ini.write(str(meta.getAttribute('path')) + " " + str(meta.getAttribute('versions')) + "\n") 243 ini.write(str(meta.getAttribute('path')) + " " + str(meta.getAttribute('versions')) + "\n")
264 ini.close() 244 ini.close()
265 finally: 245 finally:
271 def getRawMetaList(): 251 def getRawMetaList():
272 try: 252 try:
273 try: 253 try:
274 metacache_lock.acquire() 254 metacache_lock.acquire()
275 # Read in the metas 255 # Read in the metas
276 orpg.tools.validate.Validate().config_file("metaservers.cache","metaservers.cache") 256 validate.config_file("metaservers.cache","metaservers.cache")
277 ini = open(orpg.dirpath.dir_struct["user"]+"metaservers.cache","r") 257 ini = open(dir_struct["user"]+"metaservers.cache","r")
278 metas = ini.readlines() 258 metas = ini.readlines()
279 ini.close() 259 ini.close()
280 return metas 260 return metas
281 finally: 261 finally:
282 metacache_lock.release() 262 metacache_lock.release()
284 if META_DEBUG: traceback.print_exc() 264 if META_DEBUG: traceback.print_exc()
285 print "Meta Server Lib: getRawMetaList(): " + str(e) 265 print "Meta Server Lib: getRawMetaList(): " + str(e)
286 return [] 266 return []
287 267
288 def getMetaServers(versions = None, pick_random=0): 268 def getMetaServers(versions = None, pick_random=0):
289 # get meta server URLs as a list 269 """
290 270 get meta server URLs as a list
291 # versions is a list of acceptable version numbers. 271
292 # A False truth value will use getMetaServerBaseURL() 272 versions is a list of acceptable version numbers.
293 273 A False truth value will use getMetaServerBaseURL()
294 # set a default if we have weird reading problems 274
295 # default_url = "http://www.openrpg.com/openrpg_servers.php" 275 set a default if we have weird reading problems
276 default_url = "http://www.openrpg.com/openrpg_servers.php"
277 """
296 278
297 meta_names = [] 279 meta_names = []
298 280
299 if(versions): # If versions are supplied, then look in metaservers.conf 281 if(versions): # If versions are supplied, then look in metaservers.conf
300 try: 282 try:
301 # read in the metas from file 283 """
302 # format of file is one meta entry per line 284 read in the metas from file
303 # each entry will be the meta url, followed by one or more version numbers that it 285 format of file is one meta entry per line
304 # handle. Generally, this will be either a 1 for the original Meta format, or 286 each entry will be the meta url, followed by one or more version numbers that it
305 # 2 for the new one. 287 handle. Generally, this will be either a 1 for the original Meta format, or
288 2 for the new one.
289 """
306 290
307 # Read in the metas 291 # Read in the metas
308 metas = getRawMetaList() 292 metas = getRawMetaList()
309 #print str(metas) 293 #print str(metas)
310 294
313 for meta in metas: 297 for meta in metas:
314 298
315 # split the line on whitespace 299 # split the line on whitespace
316 # obviously, your meta servers urls shouldn't contain whitespace. duh. 300 # obviously, your meta servers urls shouldn't contain whitespace. duh.
317 words = meta.split() 301 words = meta.split()
318
319 success = 0 # init success flag for version check 302 success = 0 # init success flag for version check
320
321 for version in versions: # run through each allowed version from caller 303 for version in versions: # run through each allowed version from caller
322 if version in words[1:]: # if the allowed version token was found 304 if version in words[1:]: # if the allowed version token was found
323 success += 1 # then increment the success indicator 305 success += 1 # then increment the success indicator
324
325 if success: # if the meta entry is acceptable to the caller 306 if success: # if the meta entry is acceptable to the caller
326 meta_names.append(words[0]) # add the entry 307 meta_names.append(words[0]) # add the entry
327 if META_DEBUG: print "adding metaserver " + meta 308 if META_DEBUG: print "adding metaserver " + meta
328 309
329 # at this point, we should have at least one name from the cache. If not ... 310 # at this point, we should have at least one name from the cache. If not ...
330 if not meta_names: 311 if not meta_names:
331 default_meta = getMetaServerBaseURL() # grab the meta from ini.xml 312 default_meta = getMetaServerBaseURL() # grab the meta from ini.xml
332 meta_names.append(default_meta) # add it to the return list 313 meta_names.append(default_meta) # add it to the return list
333 # print "Warning!!\nNo valid metaservers cached." 314 # print "Warning!!\nNo valid metaservers cached."
334 # print "Using meta from MetaServerBaseURL: " + default_meta + "\n" 315 # print "Using meta from MetaServerBaseURL: " + default_meta + "\n"
335 # if we have more than one and want a random one 316 # if we have more than one and want a random one
336 elif pick_random: 317 elif pick_random:
337 if META_DEBUG: print "choosing random meta from: " + str(meta_names) 318 if META_DEBUG: print "choosing random meta from: " + str(meta_names)
338 i = int(random.uniform(0,len(meta_names))) 319 i = int(random.uniform(0,len(meta_names)))
339 #meta = meta_names[i] 320 #meta = meta_names[i]
355 336
356 def getMetaServerBaseURL(): 337 def getMetaServerBaseURL():
357 # get meta server URL 338 # get meta server URL
358 url = "http://www.openrpg.com/openrpg_servers.php" 339 url = "http://www.openrpg.com/openrpg_servers.php"
359 try: 340 try:
360 orpg.tools.validate.Validate().config_file("settings.xml","default_settings.xml") 341 component.get('validate').config_file("settings.xml","default_settings.xml")
361 ini = open(orpg.dirpath.dir_struct["user"]+"settings.xml","r") 342 ini = open(dir_struct["user"]+"settings.xml","r")
362 txt = ini.read() 343 txt = ini.read()
344 xml = component.get('xml')
363 tree = xml.parseXml(txt)._get_documentElement() 345 tree = xml.parseXml(txt)._get_documentElement()
364 ini.close() 346 ini.close()
365 node_list = tree.getElementsByTagName("MetaServerBaseURL") 347 node_list = tree.getElementsByTagName("MetaServerBaseURL")
366 if node_list: 348 if node_list:
367 url = node_list[0].getAttribute("value") 349 url = node_list[0].getAttribute("value")
368 350
369 # allow tree to be collected 351 # allow tree to be collected
370 try: 352 try: tree.unlink()
371 tree.unlink() 353 except: pass
372 except:
373 pass
374 354
375 except Exception,e: 355 except Exception,e:
376 print e 356 print e
377 # print "using meta server URI: " + url 357 #print "using meta server URI: " + url
378 return url 358 return url
379 359
380 ####################################################################################### 360 """
381 # Beginning of Class registerThread 361 Beginning of Class registerThread
382 # 362
383 # A Class to Manage Registration with the Meta2 363 A Class to Manage Registration with the Meta2
384 # Create an instance and call it's start() method 364 Create an instance and call it's start() method
385 # if you want to be (and stay) registered. This class 365 if you want to be (and stay) registered. This class
386 # will take care of registering and re-registering as 366 will take care of registering and re-registering as
387 # often as necessary to stay in the Meta list. 367 often as necessary to stay in the Meta list.
388 # 368
389 # You may call register() yourself if you wish to change your 369 You may call register() yourself if you wish to change your
390 # server's name. It will immediately update the Meta. There 370 server's name. It will immediately update the Meta. There
391 # is no need to unregister first. 371 is no need to unregister first.
392 # 372
393 # Call unregister() when you no longer want to be registered. 373 Call unregister() when you no longer want to be registered.
394 # This will result in the registerThread dying after 374 This will result in the registerThread dying after
395 # attempting to immediately remove itself from the Meta. 375 attempting to immediately remove itself from the Meta.
396 # 376
397 # If you need to become registered again after that, you 377 If you need to become registered again after that, you
398 # must create a new instance of class registerThread. Don't 378 must create a new instance of class registerThread. Don't
399 # just try to call register() on the old, dead thread class. 379 just try to call register() on the old, dead thread class.
400 380 """
401 381
402 class registerThread(Thread): 382 class registerThread(Thread):
403 # Originally, I wrote this as a sub-class of wxThread, but 383 """
404 # A) I couldn't get it to import right 384 Originally, I wrote this as a sub-class of wxThread, but
405 # B) I realized that I want this to be used in a server, 385 A) I couldn't get it to import right
406 # which I don't want needing wxWindows to run! 386 B) I realized that I want this to be used in a server,
407 # 387 which I don't want needing wxWindows to run!
408 # Because of this fact, there are some methods from wxThread 388
409 # that I implemented to minimize changes to the code I had 389 Because of this fact, there are some methods from wxThread
410 # just written, i.e. TestDeleteStatus() and Delete() 390 that I implemented to minimize changes to the code I had
391 just written, i.e. TestDeleteStatus() and Delete()
392 """
411 393
412 def __init__(self,name=None,realHostName=None,num_users = "Hmmm",MetaPath=None,port=6774,register_callback=None): 394 def __init__(self,name=None,realHostName=None,num_users = "Hmmm",MetaPath=None,port=6774,register_callback=None):
413 395
414 Thread.__init__(self,name="registerThread") 396 Thread.__init__(self,name="registerThread")
415 self.rlock = RLock() # Re-entrant lock used to make this class thread safe 397 self.rlock = RLock() # Re-entrant lock used to make this class thread safe
427 # indicates a new registration. 409 # indicates a new registration.
428 self.interval = 0 # interval returned from Meta. Is how often to 410 self.interval = 0 # interval returned from Meta. Is how often to
429 # re-register, in minutes. 411 # re-register, in minutes.
430 self.destroy = 0 # Used to flag that this thread should die 412 self.destroy = 0 # Used to flag that this thread should die
431 self.port = str(port) 413 self.port = str(port)
432 self.register_callback = register_callback # set a method to call to report result of register 414 self.register_callback = register_callback # set a method to call to report result of register
433 # This thread will communicate with one and only one 415 """
434 # Meta. If the Meta in ini.xml is changed after 416 This thread will communicate with one and only one
435 # instantiation, then this instance must be 417 Meta. If the Meta in ini.xml is changed after
436 # unregistered and a new instance instantiated. 418 instantiation, then this instance must be
437 # 419 unregistered and a new instance instantiated.
438 # Also, if MetaPath is specified, then use that. Makes 420
439 # it easier to have multiple registerThreads going to keep the server registered 421 Also, if MetaPath is specified, then use that. Makes
440 # on multiple (compatible) Metas. 422 it easier to have multiple registerThreads going to keep the server registered
423 on multiple (compatible) Metas.
424 """
441 425
442 if MetaPath == None: 426 if MetaPath == None:
443 self.path = getMetaServerBaseURL() # Do this if no Meta specified 427 self.path = getMetaServerBaseURL() # Do this if no Meta specified
444 else: 428 else:
445 self.path = MetaPath 429 self.path = MetaPath
460 self.die_event.set() 444 self.die_event.set()
461 finally: 445 finally:
462 self.rlock.release() 446 self.rlock.release()
463 447
464 def run(self): 448 def run(self):
465 # This method gets called by Thread implementation 449 """
466 # when self.start() is called to begin the thread's 450 This method gets called by Thread implementation
467 # execution 451 when self.start() is called to begin the thread's
468 # 452 execution
469 # We will basically enter a loop that continually 453
470 # re-registers this server and sleeps Interval 454 We will basically enter a loop that continually
471 # minutes until the thread is ordered to die in place 455 re-registers this server and sleeps Interval
456 minutes until the thread is ordered to die in place
457 """
472 while(not self.TestDeleteStatus()): # Loop while until told to die 458 while(not self.TestDeleteStatus()): # Loop while until told to die
473 # Otherwise, call thread safe register(). 459 # Otherwise, call thread safe register().
474 self.register(self.name, self.realHostName, self.num_users) 460 self.register(self.name, self.realHostName, self.num_users)
475 if META_DEBUG: print "Sent Registration Data" 461 if META_DEBUG: print "Sent Registration Data"
476
477 # register() will end up setting the state variables 462 # register() will end up setting the state variables
478 # for us, including self.interval. 463 # for us, including self.interval.
479 try: 464 try:
480 self.rlock.acquire() # Serialize access to this state information 465 self.rlock.acquire() # Serialize access to this state information
481 466
494 # If we get past the while loop, it's because we've been asked to die, 479 # If we get past the while loop, it's because we've been asked to die,
495 # so just let run() end. Once this occurs, the thread is dead and 480 # so just let run() end. Once this occurs, the thread is dead and
496 # calls to Thread.isAlive() return False. 481 # calls to Thread.isAlive() return False.
497 482
498 def unregister(self): 483 def unregister(self):
499 # This method can (I hope) be called from both within the thread 484 """
500 # and from other threads. It will attempt to unregister this 485 This method can (I hope) be called from both within the thread
501 # server from the Meta database 486 and from other threads. It will attempt to unregister this
502 # When this is either accomplished or has been tried hard enough 487 server from the Meta database
503 # (after which it just makes sense to let the Meta remove the 488 When this is either accomplished or has been tried hard enough
504 # entry itself when we don't re-register using this id), 489 (after which it just makes sense to let the Meta remove the
505 # this method will either cause the thread to immediately die 490 entry itself when we don't re-register using this id),
506 # (if called from this thread's context) or set the Destroy flag 491 this method will either cause the thread to immediately die
507 # (if called from the main thread), a positive test for which will cause 492 (if called from this thread's context) or set the Destroy flag
508 # the code in Entry() to exit() when the thread wakes up and 493 (if called from the main thread), a positive test for which will cause
509 # checks TestDeleteStatus(). 494 the code in Entry() to exit() when the thread wakes up and
510 # lock the critical section. The unlock will 495 checks TestDeleteStatus().
511 # automatically occur at the end of the function in the finally clause 496 lock the critical section. The unlock will
497 automatically occur at the end of the function in the finally clause
498 """
512 try: 499 try:
513 self.rlock.acquire() 500 self.rlock.acquire()
514 if not self.isAlive(): # check to see if this thread is dead 501 if not self.isAlive(): # check to see if this thread is dead
515 return 1 # If so, return an error result 502 return 1 # If so, return an error result
516 # Do the actual unregistering here 503 # Do the actual unregistering here
529 # No special handling is required. If the de-registration worked we're done. If 516 # No special handling is required. If the de-registration worked we're done. If
530 # not, then it's because we've already been removed or have a bad cookie. Either 517 # not, then it's because we've already been removed or have a bad cookie. Either
531 # way, we can't do anything else, so die. 518 # way, we can't do anything else, so die.
532 self.Delete() # This will cause the registerThread to die in register() 519 self.Delete() # This will cause the registerThread to die in register()
533 # prep xml_dom for garbage collection 520 # prep xml_dom for garbage collection
534 try: 521 try: xml_dom.unlink()
535 xml_dom.unlink() 522 except: pass
536 except:
537 pass
538 return 0 523 return 0
539 finally: 524 finally: self.rlock.release()
540 self.rlock.release()
541 525
542 def register(self, name=None, realHostName=None, num_users=None): 526 def register(self, name=None, realHostName=None, num_users=None):
543 # Designed to handle the registration, both new and 527 """
544 # repeated. 528 Designed to handle the registration, both new and
545 # 529 repeated.
546 # It is intended to be called once every interval 530
547 # (or interval - delta) minutes. 531 It is intended to be called once every interval
548 532 (or interval - delta) minutes.
549 # lock the critical section. The unlock will 533
550 # automatically occur at the end of the function in the finally clause 534 lock the critical section. The unlock will
535 automatically occur at the end of the function in the finally clause
536 """
551 try: 537 try:
552 self.rlock.acquire() 538 self.rlock.acquire()
553 if not self.isAlive(): # check to see if this thread is dead 539 if not self.isAlive(): # check to see if this thread is dead
554 return 1 # If so, return an error result 540 return 1 # If so, return an error result
555 541
556 # Set the server's attibutes, if specified. 542 # Set the server's attibutes, if specified.
557 if name: 543 if name: self.name = name
558 self.name = name 544 if num_users != None: self.num_users = num_users
559 if num_users != None: 545 if realHostName: self.realHostName = realHostName
560 self.num_users = num_users
561 if realHostName:
562 self.realHostName = realHostName
563 # build POST data 546 # build POST data
564 if self.realHostName: 547 if self.realHostName:
565 data = urllib.urlencode( {"server_data[id]":self.id, 548 data = urllib.urlencode( {"server_data[id]":self.id,
566 "server_data[cookie]":self.cookie, 549 "server_data[cookie]":self.cookie,
567 "server_data[name]":self.name, 550 "server_data[name]":self.name,
584 except: 567 except:
585 if META_DEBUG: print "Problem talking to server. Setting interval for retry ..." 568 if META_DEBUG: print "Problem talking to server. Setting interval for retry ..."
586 if META_DEBUG: print data 569 if META_DEBUG: print data
587 if META_DEBUG: print 570 if META_DEBUG: print
588 self.interval = 0 571 self.interval = 0
589 # If we are in the registerThread thread, then setting interval to 0 572 """
590 # will end up causing a retry in about 6 seconds (see self.run()) 573 If we are in the registerThread thread, then setting interval to 0
591 # If we are in the main thread, then setting interval to 0 will do one 574 will end up causing a retry in about 6 seconds (see self.run())
592 # of two things: 575 If we are in the main thread, then setting interval to 0 will do one
593 # 1) Do the same as if we were in the registerThread 576 of two things:
594 # 2) Cause the next, normally scheduled register() call to use the values 577 1) Do the same as if we were in the registerThread
595 # provided in this call. 578 2) Cause the next, normally scheduled register() call to use the values
596 # 579 provided in this call.
597 # Which case occurs depends on where the registerThread thread is when 580
598 # the main thread calls register(). 581 Which case occurs depends on where the registerThread thread is when
582 the main thread calls register().
583 """
599 return 0 # indicates that it was okay to call, not that no errors occurred 584 return 0 # indicates that it was okay to call, not that no errors occurred
585
600 # If there is a DOM returned .... 586 # If there is a DOM returned ....
601 if xml_dom: 587 if xml_dom:
602 # If there's an error, echo it to the console 588 # If there's an error, echo it to the console
603 if xml_dom.hasAttribute("errmsg"): 589 if xml_dom.hasAttribute("errmsg"):
604 print "Error durring registration: " + xml_dom.getAttribute("errmsg") 590 print "Error durring registration: " + xml_dom.getAttribute("errmsg")
605 if META_DEBUG: print data 591 if META_DEBUG: print data
606 if META_DEBUG: print 592 if META_DEBUG: print
607 # No special handling is required. If the registration worked, id, cookie, and interval 593 """
608 # can be stored and used for the next time. 594 No special handling is required. If the registration worked, id, cookie, and interval
609 # If an error occurred, then the Meta will delete us and we need to re-register as 595 can be stored and used for the next time.
610 # a new server. The way to indicate this is with a "0" id and "0" cookie sent to 596 If an error occurred, then the Meta will delete us and we need to re-register as
611 # the server during the next registration. Since that's what the server returns to 597 a new server. The way to indicate this is with a "0" id and "0" cookie sent to
612 # us on an error anyway, we just store them and the next registration will 598 the server during the next registration. Since that's what the server returns to
613 # automatically be set up as a new one. 599 us on an error anyway, we just store them and the next registration will
614 # 600 automatically be set up as a new one.
615 # Unless the server calls register() itself in the meantime. Of course, that's okay 601
616 # too, because a success on THAT register() call will set up the next one to use 602 Unless the server calls register() itself in the meantime. Of course, that's okay
617 # the issued id and cookie. 603 too, because a success on THAT register() call will set up the next one to use
618 # 604 the issued id and cookie.
619 # The interval is stored unconditionally for similar reasons. If there's an error, 605
620 # the interval will be less than 1, and the main thread's while loop will reset it 606 The interval is stored unconditionally for similar reasons. If there's an error,
621 # to 6 seconds for the next retry. 607 the interval will be less than 1, and the main thread's while loop will reset it
622 # Is it wrong to have a method where there's more comments than code? :) 608 to 6 seconds for the next retry.
609 Is it wrong to have a method where there's more comments than code? :)
610 """
623 try: 611 try:
624 self.interval = int(xml_dom.getAttribute("interval")) 612 self.interval = int(xml_dom.getAttribute("interval"))
625 self.id = xml_dom.getAttribute("id") 613 self.id = xml_dom.getAttribute("id")
626 self.cookie = xml_dom.getAttribute("cookie") 614 self.cookie = xml_dom.getAttribute("cookie")
627 if not xml_dom.hasAttribute("errmsg"): 615 if not xml_dom.hasAttribute("errmsg"):