예제 #1
0
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
예제 #2
0
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
예제 #3
0
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)
예제 #4
0
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
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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)