88import urllib2
99import xmlrpclib
1010
11+ import logging as log
12+ log .basicConfig (format = '[%(levelname)s] %(message)s' , level = log .DEBUG )
13+
1114# matches all IPv4 addresses, including invalid ones. we look for
1215# multiple-provider agreement before returning an IP.
1316IP_ADDRESS_REGEX = re .compile ('\d{1,3}(?:\.\d{1,3}){3}' )
@@ -59,7 +62,7 @@ def __call__(self, *args):
5962def get_external_ip (attempts = 100 , threshold = 3 ):
6063 '''Return our current external IP address, or None if there was an error.'''
6164
62- # load the tuple of IP address providers
65+ # load the list of IP address providers
6366 providers = load_providers ()
6467
6568 # we want several different providers to agree on the address, otherwise we
@@ -97,6 +100,7 @@ def get_external_ip(attempts=100, threshold=3):
97100 # the chances that several sites will return the same false-positive
98101 # number?
99102 if len (addys ) > 0 :
103+ log .info ('Got external address from provider: %s' , provider )
100104 ip = random .choice (addys )
101105 ip_counts .update ({ ip : 1 })
102106
@@ -108,20 +112,22 @@ def get_external_ip(attempts=100, threshold=3):
108112 return ip
109113
110114 except Exception , e :
111- print 'Error getting external IP address from %s:' % provider , e
115+ log . warning ( 'Error getting external IP address from %s: %s' , provider , e )
112116
113117 # sleep a bit after errors, in case it's a general network error. if it
114118 # is, hopefully this will give some time for the network to come back up.
115119 time .sleep (0.1 + random .random () * 2 )
116120
121+ log .warning ('Failed to get an external IP address after %d attempts!' , attempts )
122+
117123 # return None if no agreement could be reached
118124 return None
119125
120126def load_providers ():
121- '''Load the providers file as a de-duplicated and normalized tuple of URLs.'''
127+ '''Load the providers file as a de-duplicated and normalized list of URLs.'''
122128 with open ('providers.json' ) as f :
123129 providers = json .load (f )['providers' ]
124- return tuple (set ([p .strip () for p in providers ]))
130+ return list (set ([p .strip () for p in providers ]))
125131
126132def load_config ():
127133 '''Load the config file from disk.'''
@@ -141,26 +147,26 @@ def main():
141147 import sys
142148
143149 # load the config file so we can get our variables
144- print 'Loading config file...'
150+ log . info ( 'Loading config file...' )
145151 config = load_config ()
146- print 'Config file loaded.'
152+ log . info ( 'Config file loaded.' )
147153
148154 # create a connection to the Gandi production API
149155 gandi = GandiServerProxy (config ['api_key' ])
150156
151157 # get the current zone id for the configured domain
152- print "Getting domain info for domain '%s'..." % config ['domain' ]
158+ log . info ( "Getting domain info for domain '%s'..." , config ['domain' ])
153159 domain_info = gandi .domain .info (config ['domain' ])
154160 zone_id = domain_info ['zone_id' ]
155- print 'Got domain info.'
161+ log . info ( 'Got domain info.' )
156162
157163 # get the list of records for the domain's current zone
158- print 'Getting zone records for live zone version...'
164+ log . info ( 'Getting zone records for live zone version...' )
159165 zone_records = gandi .domain .zone .record .list (zone_id , 0 )
160- print 'Got zone records.'
166+ log . info ( 'Got zone records.' )
161167
162168 # find the configured record, or None if there's not a valid one
163- print "Searching for dynamic record '%s'..." % config ['name' ]
169+ log . info ( "Searching for dynamic record '%s'..." , config ['name' ])
164170 dynamic_record = None
165171 for record in zone_records :
166172 if is_valid_dynamic_record (config ['name' ], record ):
@@ -169,44 +175,44 @@ def main():
169175
170176 # fail if we found no valid record to update
171177 if dynamic_record is None :
172- print 'No record found - there must be an A record with a matching name.'
178+ log . fatal ( 'No record found - there must be an A record with a matching name.' )
173179 sys .exit (1 )
174180
175- print 'Dynamic record found.'
181+ log . info ( 'Dynamic record found.' )
176182
177183 # see if the record's IP differs from ours
178- print 'Getting external IP...'
184+ log . info ( 'Getting external IP...' )
179185 external_ip = get_external_ip ()
180186
181187 # make sure we actually got the external IP
182188 if external_ip is None :
183- print 'Could not get external IP.'
189+ log . fatal ( 'Could not get external IP.' )
184190 sys .exit (2 )
185191
186- print 'External IP is:' , external_ip
192+ log . info ( 'External IP is: %s ' , external_ip )
187193
188194 # extract the current live IP
189195 record_ip = dynamic_record ['value' ].strip ()
190- print 'Current dynamic record IP is:' , record_ip
196+ log . info ( 'Current dynamic record IP is: %s ' , record_ip )
191197
192198 # compare the IPs, and exit if they match
193199 if external_ip == record_ip :
194- print 'External IP matches current dynamic record IP, no update necessary.'
200+ log . info ( 'External IP matches current dynamic record IP, no update necessary.' )
195201 sys .exit (0 )
196202
197- print 'External IP differs from current dynamic record IP!'
203+ log . info ( 'External IP differs from current dynamic record IP!' )
198204
199205 # clone the active zone version so we can modify it
200- print 'Cloning current zone version...'
206+ log . info ( 'Cloning current zone version...' )
201207 new_version_id = gandi .domain .zone .version .new (zone_id )
202- print 'Current zone version cloned.'
208+ log . info ( 'Current zone version cloned.' )
203209
204- print 'Getting cloned zone records...'
210+ log . info ( 'Getting cloned zone records...' )
205211 new_zone_records = gandi .domain .zone .record .list (zone_id , new_version_id )
206- print 'Cloned zone records retrieved.'
212+ log . info ( 'Cloned zone records retrieved.' )
207213
208214 # find the configured record, or None if there's not a valid one
209- print 'Locating dynamic record in cloned zone version...'
215+ log . info ( 'Locating dynamic record in cloned zone version...' )
210216 new_dynamic_record = None
211217 for record in new_zone_records :
212218 if is_valid_dynamic_record (config ['name' ], record ):
@@ -215,13 +221,13 @@ def main():
215221
216222 # fail if we couldn't find the dynamic record again (this shouldn't happen...)
217223 if new_dynamic_record is None :
218- print 'Could not find dynamic record in cloned zone version!'
224+ log . fatal ( 'Could not find dynamic record in cloned zone version!' )
219225 sys .exit (3 )
220226
221- print 'Cloned dynamic record found.'
227+ log . info ( 'Cloned dynamic record found.' )
222228
223229 # update the new version's dynamic record value (i.e. its IP address)
224- print 'Updating dynamic record with current external IP...'
230+ log . info ( 'Updating dynamic record with current external IP...' )
225231 updated_records = gandi .domain .zone .record .update (zone_id , new_version_id , {
226232 'id' : new_dynamic_record ['id' ]
227233 }, {
@@ -231,20 +237,20 @@ def main():
231237 })
232238
233239 # ensure that we successfully set the new dynamic record
234- if (len (updated_records ) < = 0 or
240+ if (len (updated_records ) = = 0 or
235241 'value' not in updated_records [0 ] or
236242 updated_records [0 ]['value' ] != external_ip ):
237- print 'Failed to successfully update dynamic record!'
243+ log . fatal ( 'Failed to successfully update dynamic record!' )
238244 sys .exit (4 )
239245
240- print 'Dynamic record updated.'
246+ log . info ( 'Dynamic record updated.' )
241247
242248 # set the new zone version as the active version
243- print 'Updating active zone version...'
249+ log . info ( 'Updating active zone version...' )
244250 gandi .domain .zone .version .set (zone_id , new_version_id )
245251
246- print 'Set zone %d as the active zone version.' % new_version_id
247- print 'Dynamic record successfully updated to %s!' % external_ip
252+ log . info ( 'Set zone %d as the active zone version.' , new_version_id )
253+ log . info ( 'Dynamic record successfully updated to %s!' , external_ip )
248254
249255if __name__ == '__main__' :
250256 main ()
0 commit comments