def remove_listener(client: etcd.Client, alb, identifier): try: client.delete("/alb/{alb}/listeners/{identifier}".format( alb=alb, identifier=identifier), recursive=True) except (etcd.EtcdKeyNotFound, KeyError): pass
def unregister_certificate(client: etcd.Client, certificate_name: str): """ Removes a registered certificate. If no certificate has been previously registered nothing happens. """ try: client.delete("/certs/{name}".format(name=certificate_name), recursive=True, dir=True) except KeyError: pass
def unregister_certbot(client: etcd.Client, alb, listener_id): """ Removes a registered certbot for a given listener. If no certbot has been previously registered nothing happens. """ try: client.delete("/alb/{alb}/certbot/{identifier}".format( alb=alb, identifier=listener_id), recursive=True, dir=True) except KeyError as e: logger.exception("Failed to unregister certbot: %s: %s", type(e).__name__, e)
def unregister_targets(client: etcd.Client, identifier, targets): try: client.read( "/target_group/{identifier}/targets".format(identifier=identifier)) except (etcd.EtcdKeyNotFound, KeyError): return for target in targets: host = target['host'] port = target['port'] alb = target.get('alb') try: client.delete("/target_group/{identifier}/targets/{name}".format( identifier=identifier, name="{}:{}".format(host, port))) except (etcd.EtcdKeyNotFound, KeyError): pass
def cluster_key(node_name, *clusters, **client_kwargs): key_name = '/unclustered/{node_name}'.format(node_name=node_name) client = Client(**client_kwargs) client.get_lock(key_name, ttl=60) value = client.get(key_name).value for cluster in clusters: client.set('/{cluster}/{node_name}'.format(cluster=cluster, node_name=node_name), value) return client.delete(key_name)
class EtcdWrapper: """ Pack etcd library, send data to etcd """ def __init__(self, conf): self.conf = conf self.host, self.port = self.conf['host'], self.conf['port'] self.client = Client(host=self.host, port=self.port) self.test_connect() def test_connect(self): """ test etcd server :return: """ url = 'http://' + self.host + ':' + str(self.port) + '/version' try: data = requests.get(url) if data.status_code == 200: log.info("etcd client init ok!") return True else: return False except Exception as e: log.error("\n%s", e) log.info("Check etcd server or network") return False def write(self, key, value): try: self.client.write(key, value) except Exception as e: log.error("\n%s", e) def read(self, key, value): try: return self.client.read(key) except Exception as e: log.error("\n%s", e) def delete(self, key): try: return self.client.delete(key) except Exception as e: log.error("\n%s", e)
class EtcdStore(CSStore): def __init__(self, config): super(EtcdStore, self).__init__(config) self.server = config.get('etcd_server', '127.0.0.1') self.port = int(config.get('etcd_port', 4001)) self.namespace = config.get('namespace', "/custodia") # Initialize the DB by trying to create the default table try: self.etcd = Client(self.server, self.port) self.etcd.write(self.namespace, None, dir=True) except EtcdNotFile: # Already exists pass except EtcdException: self.logger.exception("Error creating namespace %s", self.namespace) raise CSStoreError('Error occurred while trying to init db') def _absolute_key(self, key): """Get absolute path to key and validate key""" if '//' in key: raise ValueError("Invalid empty components in key '%s'" % key) parts = key.split('/') if set(parts).intersection({'.', '..'}): raise ValueError("Invalid relative components in key '%s'" % key) return '/'.join([self.namespace] + parts).replace('//', '/') def get(self, key): self.logger.debug("Fetching key %s", key) try: result = self.etcd.get(self._absolute_key(key)) except EtcdException: self.logger.exception("Error fetching key %s", key) raise CSStoreError('Error occurred while trying to get key') self.logger.debug("Fetched key %s got result: %r", key, result) return result.value def set(self, key, value, replace=False): self.logger.debug("Setting key %s to value %s (replace=%s)", key, value, replace) path = self._absolute_key(key) try: self.etcd.write(path, value, prevExist=replace) except EtcdAlreadyExist as err: raise CSStoreExists(str(err)) except EtcdException: self.logger.exception("Error storing key %s", key) raise CSStoreError('Error occurred while trying to store key') def span(self, key): path = self._absolute_key(key) self.logger.debug("Creating directory %s", path) try: self.etcd.write(path, None, dir=True, prevExist=False) except EtcdAlreadyExist as err: raise CSStoreExists(str(err)) except EtcdException: self.logger.exception("Error storing key %s", key) raise CSStoreError('Error occurred while trying to store key') def list(self, keyfilter='/'): path = self._absolute_key(keyfilter) if path != '/': path = path.rstrip('/') self.logger.debug("Listing keys matching %s", path) try: result = self.etcd.read(path, recursive=True) except EtcdKeyNotFound: return None except EtcdException: self.logger.exception("Error listing %s", keyfilter) raise CSStoreError('Error occurred while trying to list keys') self.logger.debug("Searched for %s got result: %r", path, result) value = set() for entry in result.get_subtree(): if entry.key == path: continue name = entry.key[len(path):] if entry.dir and not name.endswith('/'): name += '/' value.add(name.lstrip('/')) return sorted(value) def cut(self, key): self.logger.debug("Removing key %s", key) try: self.etcd.delete(self._absolute_key(key)) except EtcdKeyNotFound: self.logger.debug("Key %s not found", key) return False except EtcdException: self.logger.exception("Error removing key %s", key) raise CSStoreError('Error occurred while trying to cut key') self.logger.debug("Key %s removed", key) return True
class Etcd(ConfigHandlerBase): def __init__(self, keyspace=None, **kwargs): """ Initialize the handler data store. :param keyspace: etcd keyspace for configuration map starting with / :type key: string :param kwargs: generic params forwarded from the Configmanager :type key: dict """ super().__init__() self.client = Client(**kwargs) self.keyspace = keyspace if keyspace else '/config' if not self.keyspace.startswith('/'): self.keyspace = '/' + self.keyspace self.config = self.load() def load(self): """ Load all configuration key values from the Etcd data store. Returns a nested dict. :rtype: dict """ try: directory = self.client.read(self.keyspace, recursive=True) items = {} for item in directory.leaves: recursive(item.key.replace(self.keyspace + '/', '').replace('/', '.'), items, item.value, update=True) return items except EtcdKeyNotFound: return {} def dump(self): """ Serialize and store the configuration key, values to the Etcd data store. :rtype: bool (success) """ flat_dict = flatten(self.config, parent_key=self.keyspace, separator='/') for key, value in flat_dict.items(): try: self.client.set(key, value) except EtcdNotDir as exc: directory_not_created = exc.payload['cause'] self.client.write(directory_not_created, None, dir=True) self.client.set(key, value) self.updated = False return True def destroy(self): """ destroys given keyspace in etcd :rtype: bool (success) """ try: self.client.delete(self.keyspace, recursive=True, dir=True) return True except EtcdKeyNotFound: return False
class Store: '''Handles persistence of streams information in backend.''' def __init__(self, config): self.etcd = Client(host=config.etcd_hosts, allow_reconnect=True) self.stream_changes = ReplaySubject() self.initialize_store() def initialize_store(self): '''Clears leftover store data at the beginning of execution.''' logger.info('Initializing store...') try: self.etcd.delete(STREAMS_KEY, recursive=True) except EtcdKeyNotFound: return def write_or_update_stream(self, stream): '''Adds a new stream to the store or updates the existing stream.''' key = path.join(STREAMS_KEY, str(stream.id)) value = dumps(stream.data, default=str) try: self.etcd.write(key, value, prevExist=False) logger.info('Key \'%s\' did not exist. Added new entry.' % (key)) self.stream_changes.on_next( dict(type='add', target=stream.id, changed_fields=stream.data.keys(), stream=stream)) except EtcdAlreadyExist: prevStream = self.read_stream(stream.id) if stream != prevStream: self.etcd.write(key, value, prevExist=True) logger.info('Key \'%s\' updated.' % (key)) self.stream_changes.on_next( dict(type='change', target=stream.id, changed_fields=prevStream.changed_fields(stream), stream=stream)) def read_stream(self, stream_id): '''Reads a stream by ID from the store.''' data = loads( self.etcd.read(path.join(STREAMS_KEY, str(stream_id))).value) return Stream(data) def read_streams(self): '''Reads a list of all streams from the store.''' try: return [ Stream(loads(stream.value)) for stream in self.etcd.read(STREAMS_KEY).children ] except EtcdKeyNotFound: return [] def delete_stream(self, stream): '''Deletes a stream from the store.''' try: self.etcd.delete(path.join(STREAMS_KEY, str(stream.id))) logger.info('Key \'%s\' deleted.') self.stream_changes.on_next( dict(type='delete', target=stream.id, changed_fields=stream.data.keys(), stream=stream)) except EtcdKeyNotFound: return
class RPCServer(asyncore.dispatcher): zk_root = "/demo" zk_rpc = zk_root + "/rpc" def __init__(self, port): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind(("localhost", port)) self.listen(1) self.child_pids = None self.zk = KazooClient() self.etcd = Client(port=2379) self.port = port if self.prefork(10): # self.register_zk(port) self.register_etcd() self.register_parent_signal() else: # in child process, pid = 0 self.register_child_signal() def prefork(self, n): for i in range(n): pid = os.fork() if pid < 0: raise Exception("fork error") if pid > 0: # in parent process if self.child_pids: self.child_pids.append(pid) else: self.child_pids = [pid] continue if pid == 0: # in child process return False return True def register_zk(self, port): self.zk.start() # 确保节点存在 self.zk.ensure_path(self.zk_root) value = json.dumps({"host": "127.0.0.1", "port": port}) # 可以自动移除的节点 self.zk.create("/demo/rpc", str.encode(value), ephemeral=True, sequence=True) child = self.zk.get_children(self.zk_root) print("node in zk are:") for c in child: print(self.zk.get(self.zk_root+"/"+c)[0]) def register_etcd(self): value = json.dumps({"host": "127.0.0.1", "port": self.port}) res = self.etcd.write(self.zk_rpc + "/localhost:%s" % self.port, value) directory = self.etcd.get(self.zk_rpc) for d in directory.leaves: print(d.key, ":", d.value) def exit_parent(self, sig, frame): # self.zk.stop() self.etcd.delete(self.zk_rpc + "/localhost:%s" % self.port) self.close() asyncore.close_all() pids = [] for pid in self.child_pids: print("before kill") try: # 中止子进程 os.kill(pid, signal.SIGINT) pids.append(pid) except OSError as ex: if ex.args[0] == errno.ECHILD: # 目标子进程已经提前挂了 continue raise ex print("after kill", pid) for pid in pids: while True: try: os.waitpid(pid, 0) break except OSError as ex: if ex.args[0] == errno.ECHILD: # 子进程已经割过了 break if ex.args[0] != errno.EINTR: # 被其它信号打断了 raise ex print("wait over", pid) def reap_child(self, sig, frame): # 监听子进程退出 print("before reap") info = None while True: try: info = os.waitpid(-1, os.WNOHANG) break except OSError as ex: if ex.args[0] == errno.ECHILD: # 子进程已经割过了 break if ex.args[0] != errno.EINTR: # 被其它信号打断了 raise ex if info: pid = info[0] try: self.child_pids.remove(pid) except ValueError: pass print("after reap", pid) def register_parent_signal(self): signal.signal(signal.SIGINT, self.exit_parent) signal.signal(signal.SIGTERM, self.exit_parent) signal.signal(signal.SIGCHLD, self.reap_child) def exit_child(self, sig, frame): self.close() asyncore.close_all() print("child all closed") def register_child_signal(self): signal.signal(signal.SIGINT, self.exit_child) signal.signal(signal.SIGTERM, self.exit_child) def handle_accept(self): pair = self.accept() if pair is not None: sock, addr = pair RPCHandler(sock, addr)