def main(self): # Continue looping while the service is running. while not self._terminate_flag: # This blocks on changes to the watched key in etcd. _log.debug("Waiting for change from etcd for key {}".format( self._plugin.key())) old_value = self._last_value value = self.update_from_etcd() if self._terminate_flag: break if value and value != old_value: _log.info("Got new config value from etcd - filename {}, file size {}, SHA512 hash {}".format( self._plugin.file(), len(value), sha512(utils.safely_encode(value)).hexdigest())) _log.debug("Got new config value from etcd:\n{}".format( utils.safely_encode(value))) self._plugin.on_config_changed(value, self._alarm) FILE_CHANGED.log(filename=self._plugin.file())
def test_safely_encode(self): self.assertEquals(safely_encode(None), None) self.assertEquals(safely_encode(u'ASCII'), 'ASCII') self.assertEquals(safely_encode(u'\x80nonASCII'), '\xc2\x80nonASCII')
def read_from_etcd(self, wait=True): result = None wait_index = None try: result = self._client.read(self.key(), quorum=True) wait_index = result.etcd_index + 1 if wait: # If the cluster view hasn't changed since we last saw it, then # wait for it to change before doing anything else. _log.info("Read value {} from etcd, " "comparing to last value {}".format( utils.safely_encode(result.value), utils.safely_encode(self._last_value))) if result.value == self._last_value: _log.info("Watching for changes with {}".format(wait_index)) while not self._terminate_flag and not self._abort_read and self.is_running(): _log.debug("Started a new watch") try: result = self._client.read(self.key(), timeout=self.TIMEOUT_ON_WATCH, waitIndex=wait_index, wait=True, recursive=False) break except etcd.EtcdException as e: if "Read timed out" in e.message: # Timeouts after TIMEOUT_ON_WATCH seconds are expected, so # ignore them - unless we're terminating, we'll # stay in the while loop and try again pass else: raise _log.debug("Finished watching") # Return if we're terminating. if self._terminate_flag: return self.tuple_from_result(result) except etcd.EtcdKeyError: _log.info("Key {} doesn't exist in etcd yet".format(self.key())) # Use any value on disk first, but the default value if not found try: f = open(self._plugin.file(), 'r') value = f.read() # pragma: no cover except: value = self.default_value() # Attempt to create new key in etcd. try: # The prevExist set to False will fail the write if it finds a # key already in etcd. This stops us overwriting a manually # uploaded file with the default template. self._client.write(self.key(), value, prevExist=False) return (value, None) except: # pragma: no cover _log.debug("Failed to create new key in the etcd store") # Sleep briefly to avoid hammering a non-existent key sleep(self.PAUSE_BEFORE_RETRY_ON_MISSING_KEY) # Return 'None' so that plugins do not write config to disk # that does not exist in the etcd store, leaving us out of sync return (None, None) except Exception as e: # Catch-all error handler (for invalid requests, timeouts, etc - # start over. _log.error("{} caught {!r} when trying to read with index {}" " - pause before retry". format(self._ip, e, wait_index)) # Sleep briefly to avoid hammering a failed server self.pause() # The main loop (which reads from etcd in a loop) should call this # function again after we return, causing the read to be retried. return self.tuple_from_result(result)
def read_from_etcd(self, wait=True): result = None wait_index = None try: result = self._client.read(self.key(), quorum=True) wait_index = result.etcd_index + 1 if wait: # If the cluster view hasn't changed since we last saw it, then # wait for it to change before doing anything else. _log.info("Read value {} from etcd, " "comparing to last value {}".format( utils.safely_encode(result.value), utils.safely_encode(self._last_value))) if result.value == self._last_value: _log.info( "Watching for changes with {}".format(wait_index)) while not self._terminate_flag and not self._abort_read and self.is_running( ): _log.debug("Started a new watch") try: result = self._client.read( self.key(), timeout=self.TIMEOUT_ON_WATCH, waitIndex=wait_index, wait=True, recursive=False) break except etcd.EtcdException as e: if "Read timed out" in e.message: # Timeouts after TIMEOUT_ON_WATCH seconds are expected, so # ignore them - unless we're terminating, we'll # stay in the while loop and try again pass else: raise _log.debug("Finished watching") # Return if we're terminating. if self._terminate_flag: return self.tuple_from_result(result) except etcd.EtcdKeyError: _log.info("Key {} doesn't exist in etcd yet".format(self.key())) # Use any value on disk first, but the default value if not found try: f = open(self._plugin.file(), 'r') value = f.read() # pragma: no cover except: value = self.default_value() # Attempt to create new key in etcd. try: # The prevExist set to False will fail the write if it finds a # key already in etcd. This stops us overwriting a manually # uploaded file with the default template. self._client.write(self.key(), value, prevExist=False) return (value, None) except: # pragma: no cover _log.debug("Failed to create new key in the etcd store") # Sleep briefly to avoid hammering a non-existent key sleep(self.PAUSE_BEFORE_RETRY_ON_MISSING_KEY) # Return 'None' so that plugins do not write config to disk # that does not exist in the etcd store, leaving us out of sync return (None, None) except Exception as e: # Catch-all error handler (for invalid requests, timeouts, etc - # start over. _log.error("{} caught {!r} when trying to read with index {}" " - pause before retry".format(self._ip, e, wait_index)) # Sleep briefly to avoid hammering a failed server self.pause() # The main loop (which reads from etcd in a loop) should call this # function again after we return, causing the read to be retried. return self.tuple_from_result(result)