flagg <ianand0...@gmail.com> wrote: > On Feb 3, 7:32?am, Nick Craig-Wood <n...@craig-wood.com> wrote: > > flagg <ianand0...@gmail.com> wrote: > > > ?This xmlrpc server is designed to parse dns zone files and then > > > ?perform various actions on said files. \ > > > ?It uses dnspython, and xmlrpclib > > > ? I'd like to know what some of the more experienced python users > > > ?think. Where I could improve code, make it more efficient, whatever. > > > ?All suggestions are welcome. ?This is my first functional python > > > ?program, ?and I have tons of questions to go along with whatever > > > ?suggestions. ? How could I do sanity checking; right now nothing > > > ?checks the input to these functions, error-handling in this program is > > > ?almost non-existant how do you integrate decent error handling into a > > > ?piece of software......ill shut up now. > > > > I made a few comments, some about error handling! ?See below > > > > > > > > > ?import dns.zone > > > ?from time import localtime, strftime, time > > > ?import os, sys > > > ?from dns.rdataclass import * > > > ?from dns.rdatatype import * > > > ?from string import Template > > > ?import xmlrpclib > > > ?from SimpleXMLRPCServer import SimpleXMLRPCServer > > > ?from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler > > > > > ?zoneDir = "/dns" > > > > > ?def openZoneFile(zoneFile): > > > ? ? ?""" > > > ? ? ?Opens a zone file. ?Then reads in zone file to dnspython zone > > > ?object. > > > ? ? ?""" > > > ? ? ?global zone > > > ? ? ?global host > > > ? ? ?global domain > > > ? ? ?domain = ".".join(zoneFile.split('.')[1:]) > > > ? ? ?host = ".".join(zoneFile.split('.')[:1]) > > > > > ? ? ?try: > > > ? ? ? ? ? ? ?zone = dns.zone.from_file(zoneDir + domain + '.zone', > > > ?domain) > > > ? ? ?except dns.exception, e: > > > ? ? ? ? ? ? ?print e.__class__, e > > > > Don't use global variables! > > > > This function should probably return 3 things > > > > ? ? return zone, host, domain > > > > And whenever you use it, say > > > > ? ? zone, host, domain = openZoneFile(xxx) > > > > It should probably be a method of DNSFunctions. ?I'd avoid setting > > self.zone etc as that is making a different sort of global data which > > I'd avoid too. > > > > Also if the dns.zone.from_file didn't work (raises dns.exception) you > > set the host and domain but the zone is left at its previous value > > which probably isn't what you wanted. ?I'd let the error propagate > > which I think will do something sensible for the xmlrpc. > > > > > > > > > ?class DNSFunctions: > > > > > ? ? ? # Not exposed to xml-rpc calls, used internally only. > > > ? ? ?def _writeToZoneFile(self, record): > > > ? ? ? ? ?""" > > > ? ? ? ? ?Checks if zone file exists, if it does it write values to zone > > > ?file > > > ? ? ? ? ?""" > > > > > ? ? ? ? ?for (name, ttl, rdata) in zone.iterate_rdatas(SOA): > > > ? ? ? ? ? ? ?new_serial = int(strftime('%Y%m%d00', localtime(time()))) > > > ? ? ? ? ? ? ?if new_serial <= rdata.serial: > > > ? ? ? ? ? ? ? ? ?new_serial = rdata.serial + 1 > > > ? ? ? ? ? ? ?rdata.serial = new_serial > > > > > ? ? ? ? ?if os.path.exists(zoneDir + str(zone.origin) + "zone"): > > > ? ? ? ? ? ? ?f = open(zoneDir + str(zone.origin) + "zone", "w") > > > ? ? ? ? ? ? ?zone.to_file(f) > > > ? ? ? ? ? ? ?f.close() > > > ? ? ? ? ?else: > > > ? ? ? ? ? ? ?print "Zone: " + zone.origin + " does not exist, please > > > ?add first" > > > > This should probably be raising an exception to be caught or > > propagated back to the client via xmlrpc. > > > > > > > > > > > > > ? ? ?def showRecord(self, record): > > > ? ? ? ? ?""" > > > ? ? ? ? ?Shows a record for a given zone. Prints out > > > ? ? ? ? ?TTL, IN, Record Type, IP > > > ? ? ? ? ?""" > > > > > ? ? ? ? ?openZoneFile(record) > > > > > ? ? ? ? ?try: > > > ? ? ? ? ? ? ?rdataset = zone.get_node(host) > > > ? ? ? ? ? ? ?for rdata in rdataset: > > > ? ? ? ? ? ? ? ? ?return str(rdata) > > > ? ? ? ? ?except: > > > ? ? ? ? ? ? ?raise Exception("Record not found") > > > > > ? ? ?def changeRecord(self, record, type, target): > > > ? ? ? ? ?""" > > > ? ? ? ? ?Changes a dns entry. > > > ? ? ? ? ?...@param record: which record to chance > > > ? ? ? ? ?...@param type: ?what type of record, A, MX, NS, CNAME > > > ? ? ? ? ?...@target: if CNAME target points to HOSTNAME, if A record > > > ?points to IP > > > ? ? ? ? ?""" > > > ? ? ? ? ?openZoneFile(record) > > > ? ? ? ? ?try: > > > ? ? ? ? ? ? ?rdataset = zone.find_rdataset(host, rdtype=type) > > > ? ? ? ? ?except: > > > ? ? ? ? ? ? ?raise Exception("You must enter a valid record and type. > > > ?See --usage for examples") > > > > Don't catch all exceptions - find out which exceptions are thrown. > > Consider just letting it propagate - hopefully the find_rdataset error > > is descriptive enough. > > > > Thanks for taking the time to reply I appreciate it.
No problems. > Are global variables "bad"? Or just inefficient? They make code a lot more difficult to follow because you have to wonder where the variable has been set. If you create multiple instances of DNSFunctions (more than likely in a multi-user server) you'll find that global variables will cause your program to go wrong quite badly! In general only ever use global variables in quick and dirty scripts. For static configuration which you might think globals are good for you can easily do that in a module namespace or in a Config object which you only have one of. > Also when you say: > > "Also if the dns.zone.from_file didn't work (raises dns.exception) you > set the host and domain but the zone is left at its previous value > which probably isn't what you wanted. I'd let the error propagate > which I think will do something sensible for the xmlrpc." > > Could you elaborate on that. Im not following what you mean by > "propagate" I mean don't catch the exception. If you raise an exception while in an RPC it will get propagated back to the client which is exactly what you want. Eg, run this in one window import SimpleXMLRPCServer class SimpleTest: def divide(self, x, y): """Does an integer divide""" print "About to divide %r by %r" % (x, y) return x // y server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 8000)) server.register_instance(SimpleTest()) server.serve_forever() And this in another import xmlrpclib server = xmlrpclib.Server('http://localhost:8000') print server.divide(6, 3) print server.divide(6, 2) print server.divide(6, 0) The client will print 2 3 Traceback (most recent call last): File "xmlrpc_client.py", line 6, in <module> print server.divide(6, 0) File "/usr/lib/python2.5/xmlrpclib.py", line 1147, in __call__ return self.__send(self.__name, args) File "/usr/lib/python2.5/xmlrpclib.py", line 1437, in __request verbose=self.__verbose File "/usr/lib/python2.5/xmlrpclib.py", line 1201, in request return self._parse_response(h.getfile(), sock) File "/usr/lib/python2.5/xmlrpclib.py", line 1340, in _parse_response return u.close() File "/usr/lib/python2.5/xmlrpclib.py", line 787, in close raise Fault(**self._stack[0]) xmlrpclib.Fault: <Fault 1: "<type 'exceptions.ZeroDivisionError'>:integer division or modulo by zero"> Whereas the server will keep running with About to divide 6 by 3 localhost - - [04/Feb/2009 09:01:56] "POST /RPC2 HTTP/1.0" 200 - About to divide 6 by 2 localhost - - [04/Feb/2009 09:01:56] "POST /RPC2 HTTP/1.0" 200 - About to divide 6 by 0 localhost - - [04/Feb/2009 09:01:56] "POST /RPC2 HTTP/1.0" 200 - So in general if the error is caused by the client, send it back to the client by not catching it in the server. The server will keep running and the client will know it has done something wrong. -- Nick Craig-Wood <n...@craig-wood.com> -- http://www.craig-wood.com/nick -- http://mail.python.org/mailman/listinfo/python-list