Example #1
0
 def test_async_client_iteration(self):
   ss1 = ServerSet(self.make_zk(self._server.ensemble), self.SERVICE_PATH)
   ss2 = ServerSet(self.make_zk(self._server.ensemble), self.SERVICE_PATH)
   ss1.join(self.INSTANCE1)
   ss2.join(self.INSTANCE2)
   assert list(ss1) == [ServiceInstance(self.INSTANCE1), ServiceInstance(self.INSTANCE2)]
   assert list(ss2) == [ServiceInstance(self.INSTANCE1), ServiceInstance(self.INSTANCE2)]
Example #2
0
 def test_shard_id_registers(self):
   ss1 = ServerSet(self.make_zk(self._server.ensemble), self.SERVICE_PATH)
   ss2 = ServerSet(self.make_zk(self._server.ensemble), self.SERVICE_PATH)
   ss1.join(self.INSTANCE1, shard=0)
   ss2.join(self.INSTANCE2, shard=1)
   assert list(ss1) == [ServiceInstance(self.INSTANCE1, shard=0), ServiceInstance(self.INSTANCE2, shard=1)]
   assert list(ss2) == [ServiceInstance(self.INSTANCE1, shard=0), ServiceInstance(self.INSTANCE2, shard=1)]
Example #3
0
  def test_canceled_join_long_time(self):
    zk = self.make_zk(self._server.ensemble)
    zk.live.wait()
    session_id = self.session_id(zk)
    ss = ServerSet(zk, self.SERVICE_PATH)
    join_signal = threading.Event()
    memberships = []

    def on_expire():
      pass

    def do_join():
      memberships.append(ss.join(self.INSTANCE1, expire_callback=on_expire))

    class JoinThread(threading.Thread):
      def run(_):
        while True:
          join_signal.wait()
          join_signal.clear()
          do_join()

    joiner = JoinThread()
    joiner.daemon = True
    joiner.start()

    do_join()
    assert len(memberships) == 1 and memberships[0] is not Membership.error()
    self._server.expire(session_id)
    self._server.shutdown()
    join_signal.set()
    self._server.start()
    while len(memberships) == 1:
      time.sleep(0.1)
    assert len(memberships) == 2 and memberships[1] is not Membership.error()
Example #4
0
 def get_scheduler_serverset(cls, cluster, port=2181, verbose=False, **kw):
   if cluster.zk is None:
     raise ValueError('Cluster has no associated zookeeper ensemble!')
   if cluster.scheduler_zk_path is None:
     raise ValueError('Cluster has no defined scheduler path, must specify scheduler_zk_path '
                      'in your cluster config!')
   hosts = [h + ':{p}' for h in cluster.zk.split(',')]
   zk = TwitterKazooClient.make(str(','.join(hosts).format(p=port)), verbose=verbose)
   return zk, ServerSet(zk, cluster.scheduler_zk_path, **kw)
Example #5
0
 def make_serverset(self, assigned_task):
     role, environment, name = (assigned_task.task.owner.role,
                                assigned_task.task.environment,
                                assigned_task.task.jobName)
     path = posixpath.join(self.__root, role, environment, name)
     client = KazooClient(self.__ensemble,
                          connection_retry=self.DEFAULT_RETRY_POLICY)
     client.start()
     return ServerSet(client, path)
 def get_scheduler_serverset(cls, cluster, port=2181, verbose=False, **kw):
     if cluster.zk is None:
         raise ValueError('Cluster has no associated zookeeper ensemble!')
     if cluster.scheduler_zk_path is None:
         raise ValueError(
             'Cluster has no defined scheduler path, must specify scheduler_zk_path '
             'in your cluster config!')
     zk = TwitterKazooClient.make(str('%s:%s' % (cluster.zk, port)),
                                  verbose=verbose)
     return zk, ServerSet(zk, cluster.scheduler_zk_path, **kw)
Example #7
0
 def __init__(self, client, path, timeout_secs, endpoint, additional=None, shard=None, name=None):
   self.__client = client
   self.__connect_event = client.start_async()
   self.__timeout_secs = timeout_secs
   self.__announcer = Announcer(ServerSet(client, path), endpoint, additional=additional,
       shard=shard)
   self.__name = name or self.DEFAULT_NAME
   self.__status = None
   self.start_event = threading.Event()
   self.metrics.register(LambdaGauge('disconnected_time', self.__announcer.disconnected_time))
Example #8
0
    def _construct_serverset(self, options):
        import socket
        import threading
        import zookeeper
        from twitter.common.zookeeper.client import ZooKeeper
        from twitter.common.zookeeper.serverset import Endpoint, ServerSet
        log.debug('ServerSet module constructing serverset.')

        hostname = socket.gethostname()
        primary_port = int(options.serverset_module_primary_port)
        primary = Endpoint(hostname, primary_port)
        additional = dict((port_name, Endpoint(hostname, port_number))
                          for port_name, port_number in
                          options.serverset_module_extra.items())

        # TODO(wickman) Add timeout parameterization here.
        self._zookeeper = ZooKeeper(options.serverset_module_ensemble)
        self._serverset = ServerSet(self._zookeeper,
                                    options.serverset_module_path)
        self._join_args = (primary, additional)
Example #9
0
 def test_client_iteration(self):
   ss = ServerSet(self.make_zk(self._server.ensemble), self.SERVICE_PATH)
   assert list(ss) == []
   ss.join(self.INSTANCE1)
   assert list(ss) == [ServiceInstance(self.INSTANCE1)]
   ss.join(self.INSTANCE2)
   assert list(ss) == [ServiceInstance(self.INSTANCE1), ServiceInstance(self.INSTANCE2)]
Example #10
0
def main(args):
    if len(args) != 1:
        app.error('Must supply a serverset path to monitor.')

    def on_join(endpoint):
        print('@ %s += %s' % (datetime.now(), endpoint))

    def on_leave(endpoint):
        print('@ %s -= %s' % (datetime.now(), endpoint))

    ss = ServerSet(ZooKeeper(), args[0], on_join=on_join, on_leave=on_leave)

    while True:
        time.sleep(100)
    def test_client_watcher(self):
        canceled_endpoints = []
        canceled = threading.Event()
        joined_endpoints = []
        joined = threading.Event()

        def on_join(endpoint):
            joined_endpoints[:] = [endpoint]
            joined.set()

        def on_leave(endpoint):
            canceled_endpoints[:] = [endpoint]
            canceled.set()

        service1 = ServerSet(self.make_zk(self._server.ensemble),
                             self.SERVICE_PATH,
                             on_join=on_join,
                             on_leave=on_leave)
        service2 = ServerSet(self.make_zk(self._server.ensemble),
                             self.SERVICE_PATH)

        member1 = service2.join(self.INSTANCE1)
        joined.wait(2.0)
        assert joined.is_set()
        assert not canceled.is_set()
        assert joined_endpoints == [ServiceInstance(self.INSTANCE1)]
        joined.clear()

        service2.join(self.INSTANCE2)
        joined.wait(2.0)
        assert joined.is_set()
        assert not canceled.is_set()
        assert joined_endpoints == [ServiceInstance(self.INSTANCE2)]
        joined.clear()

        service2.cancel(member1)
        canceled.wait(2.0)
        assert canceled.is_set()
        assert not joined.is_set()
        assert canceled_endpoints == [ServiceInstance(self.INSTANCE1)]
        canceled.clear()
Example #12
0
    def _construct_serverset(self, options):
        import socket
        import threading
        import zookeeper
        from twitter.common.zookeeper.client import ZooKeeper
        from twitter.common.zookeeper.serverset import Endpoint, ServerSet

        log.debug("ServerSet module constructing serverset.")

        hostname = socket.gethostname()
        primary_port = int(options.serverset_module_primary_port)
        primary = Endpoint(hostname, primary_port)
        additional = dict(
            (port_name, Endpoint(hostname, port_number))
            for port_name, port_number in options.serverset_module_extra.items()
        )

        # TODO(wickman) Add timeout parameterization here.
        self._zookeeper = ZooKeeper(options.serverset_module_ensemble)
        self._serverset = ServerSet(self._zookeeper, options.serverset_module_path)
        self._join_args = (primary, additional)
        self._join_kwargs = {"shard": options.serverset_module_shard_id} if options.serverset_module_shard_id else {}
Example #13
0
  def test_client_watcher(self):
    canceled_endpoints = []
    canceled = threading.Event()
    joined_endpoints = []
    joined = threading.Event()

    def on_join(endpoint):
      joined_endpoints[:] = [endpoint]
      joined.set()

    def on_leave(endpoint):
      canceled_endpoints[:] = [endpoint]
      canceled.set()

    service1 = ServerSet(self.make_zk(self._server.ensemble),
        self.SERVICE_PATH, on_join=on_join, on_leave=on_leave)
    service2 = ServerSet(self.make_zk(self._server.ensemble),
        self.SERVICE_PATH)

    member1 = service2.join(self.INSTANCE1)
    joined.wait(2.0)
    assert joined.is_set()
    assert not canceled.is_set()
    assert joined_endpoints == [ServiceInstance(self.INSTANCE1)]
    joined.clear()

    service2.join(self.INSTANCE2)
    joined.wait(2.0)
    assert joined.is_set()
    assert not canceled.is_set()
    assert joined_endpoints == [ServiceInstance(self.INSTANCE2)]
    joined.clear()

    service2.cancel(member1)
    canceled.wait(2.0)
    assert canceled.is_set()
    assert not joined.is_set()
    assert canceled_endpoints == [ServiceInstance(self.INSTANCE1)]
    canceled.clear()
Example #14
0
class ServerSetModule(app.Module):
    """
    Binds this application to a Zookeeper ServerSet.
  """

    OPTIONS = {
        "serverset-enable": options.Option(
            "--serverset-enable",
            default=False,
            action="store_true",
            dest="serverset_module_enable",
            help="Enable the ServerSet module.  Requires --serverset-path and --serverset-primary.",
        ),
        "serverset-ensemble": options.Option(
            "--serverset-ensemble",
            default="zookeeper.local.twitter.com:2181",
            dest="serverset_module_ensemble",
            metavar="HOST[:PORT]",
            help="The serverset ensemble to talk to.  HOST or HOST:PORT pair.  If the HOST is a RR DNS "
            "record, we fan out to the entire ensemble.  If no port is specified, 2181 assumed.",
        ),
        "serverset-path": options.Option(
            "--serverset-path",
            default=None,
            dest="serverset_module_path",
            metavar="PATH",
            type="str",
            help="The serverset path to join, preferably /twitter/service/(role)/(service)/(env) "
            "where env is prod, staging, devel.",
        ),
        "serverset-primary": options.Option(
            "--serverset-primary",
            type="int",
            metavar="PORT",
            dest="serverset_module_primary_port",
            default=None,
            help="Port on which to bind the primary endpoint.",
        ),
        "serverset-shard-id": options.Option(
            "--serverset-shard-id",
            type="int",
            metavar="INT",
            dest="serverset_module_shard_id",
            default=None,
            help="Shard id to assign this serverset entry.",
        ),
        "serverset-extra": options.Option(
            "--serverset-extra",
            default={},
            type="string",
            nargs=1,
            action="callback",
            metavar="NAME:PORT",
            callback=add_port_to("serverset_module_extra"),
            dest="serverset_module_extra",
            help="Additional endpoints to bind.  Format NAME:PORT.  May be specified multiple times.",
        ),
        "serverset-persistence": options.Option(
            "--serverset-persistence",
            "--no-serverset-persistence",
            action="callback",
            callback=set_bool,
            dest="serverset_module_persistence",
            default=True,
            help="If serverset persistence is enabled, if the serverset connection is dropped for any "
            "reason, we will retry to connect forever.  If serverset persistence is turned off, "
            "the application will commit seppuku -- sys.exit(1) -- upon session disconnection.",
        ),
    }

    def __init__(self):
        app.Module.__init__(self, __name__, description="ServerSet module")
        self._zookeeper = None
        self._serverset = None
        self._membership = None
        self._join_args = None
        self._torndown = False
        self._rejoin_event = threading.Event()
        self._joiner = None

    @property
    def serverset(self):
        return self._serverset

    @property
    def zh(self):
        if self._zookeeper:
            return self._zookeeper._zh

    def _assert_valid_inputs(self, options):
        if not options.serverset_module_enable:
            return

        assert (
            options.serverset_module_path is not None
        ), "If serverset module enabled, serverset path must be specified."
        assert (
            options.serverset_module_primary_port is not None
        ), "If serverset module enabled, serverset primary port must be specified."
        assert isinstance(options.serverset_module_extra, dict), "Serverset additional endpoints must be a dictionary!"
        for name, value in options.serverset_module_extra.items():
            assert isinstance(name, str), "Additional endpoints must be named by strings!"
            assert isinstance(value, int), "Additional endpoint ports must be integers!"

        try:
            primary_port = int(options.serverset_module_primary_port)
        except ValueError as e:
            raise ValueError("Could not parse serverset primary port: %s" % e)

    def _construct_serverset(self, options):
        import socket
        import threading
        import zookeeper
        from twitter.common.zookeeper.client import ZooKeeper
        from twitter.common.zookeeper.serverset import Endpoint, ServerSet

        log.debug("ServerSet module constructing serverset.")

        hostname = socket.gethostname()
        primary_port = int(options.serverset_module_primary_port)
        primary = Endpoint(hostname, primary_port)
        additional = dict(
            (port_name, Endpoint(hostname, port_number))
            for port_name, port_number in options.serverset_module_extra.items()
        )

        # TODO(wickman) Add timeout parameterization here.
        self._zookeeper = ZooKeeper(options.serverset_module_ensemble)
        self._serverset = ServerSet(self._zookeeper, options.serverset_module_path)
        self._join_args = (primary, additional)
        self._join_kwargs = {"shard": options.serverset_module_shard_id} if options.serverset_module_shard_id else {}

    def _join(self):
        log.debug("ServerSet module joining serverset.")
        primary, additional = self._join_args
        self._membership = self._serverset.join(
            primary, additional, expire_callback=self.on_expiration, **self._join_kwargs
        )

    def on_expiration(self):
        if self._torndown:
            return

        log.debug("Serverset session expired.")
        if not app.get_options().serverset_module_persistence:
            log.debug("Committing seppuku...")
            sys.exit(1)
        else:
            log.debug("Rejoining...")

        self._rejoin_event.set()

    def setup_function(self):
        options = app.get_options()
        if options.serverset_module_enable:
            self._assert_valid_inputs(options)
            self._construct_serverset(options)
            self._thread = ServerSetJoinThread(self._rejoin_event, self._join)
            self._thread.start()
            self._rejoin_event.set()

    def teardown_function(self):
        self._torndown = True
        if self._membership:
            self._serverset.cancel(self._membership)
            self._zookeeper.stop()
Example #15
0
class ServerSetModule(app.Module):
    """
    Binds this application to a Zookeeper ServerSet.
  """
    OPTIONS = {
        'serverset-enable':
        options.Option(
            '--serverset-enable',
            default=False,
            action='store_true',
            dest='serverset_module_enable',
            help=
            'Enable the ServerSet module.  Requires --serverset-path and --serverset-primary.'
        ),
        'serverset-ensemble':
        options.Option(
            '--serverset-ensemble',
            default='zookeeper.local.twitter.com:2181',
            dest='serverset_module_ensemble',
            metavar='HOST[:PORT]',
            help=
            'The serverset ensemble to talk to.  HOST or HOST:PORT pair.  If the HOST is a RR DNS '
            'record, we fan out to the entire ensemble.  If no port is specified, 2181 assumed.'
        ),
        'serverset-path':
        options.Option(
            '--serverset-path',
            default=None,
            dest='serverset_module_path',
            metavar='PATH',
            type='str',
            help=
            'The serverset path to join, preferably /twitter/service/(role)/(service)/(env) '
            'where env is prod, staging, devel.'),
        'serverset-primary':
        options.Option('--serverset-primary',
                       type='int',
                       metavar='PORT',
                       dest='serverset_module_primary_port',
                       default=None,
                       help='Port on which to bind the primary endpoint.'),
        'serverset-shard-id':
        options.Option('--serverset-shard-id',
                       type='int',
                       metavar='INT',
                       dest='serverset_module_shard_id',
                       default=None,
                       help='Shard id to assign this serverset entry.'),
        'serverset-extra':
        options.Option(
            '--serverset-extra',
            default={},
            type='string',
            nargs=1,
            action='callback',
            metavar='NAME:PORT',
            callback=add_port_to('serverset_module_extra'),
            dest='serverset_module_extra',
            help=
            'Additional endpoints to bind.  Format NAME:PORT.  May be specified multiple times.'
        ),
        'serverset-persistence':
        options.Option(
            '--serverset-persistence',
            '--no-serverset-persistence',
            action='callback',
            callback=set_bool,
            dest='serverset_module_persistence',
            default=True,
            help=
            'If serverset persistence is enabled, if the serverset connection is dropped for any '
            'reason, we will retry to connect forever.  If serverset persistence is turned off, '
            'the application will commit seppuku -- sys.exit(1) -- upon session disconnection.'
        ),
    }

    def __init__(self):
        app.Module.__init__(self, __name__, description="ServerSet module")
        self._zookeeper = None
        self._serverset = None
        self._membership = None
        self._join_args = None
        self._torndown = False
        self._rejoin_event = threading.Event()
        self._joiner = None

    @property
    def serverset(self):
        return self._serverset

    @property
    def zh(self):
        if self._zookeeper:
            return self._zookeeper._zh

    def _assert_valid_inputs(self, options):
        if not options.serverset_module_enable:
            return

        assert options.serverset_module_path is not None, (
            'If serverset module enabled, serverset path must be specified.')
        assert options.serverset_module_primary_port is not None, (
            'If serverset module enabled, serverset primary port must be specified.'
        )
        assert isinstance(
            options.serverset_module_extra,
            dict), ('Serverset additional endpoints must be a dictionary!')
        for name, value in options.serverset_module_extra.items():
            assert isinstance(
                name, str), 'Additional endpoints must be named by strings!'
            assert isinstance(
                value, int), 'Additional endpoint ports must be integers!'

        try:
            primary_port = int(options.serverset_module_primary_port)
        except ValueError as e:
            raise ValueError('Could not parse serverset primary port: %s' % e)

    def _construct_serverset(self, options):
        import socket
        import threading
        import zookeeper
        from twitter.common.zookeeper.client import ZooKeeper
        from twitter.common.zookeeper.serverset import Endpoint, ServerSet
        log.debug('ServerSet module constructing serverset.')

        hostname = socket.gethostname()
        primary_port = int(options.serverset_module_primary_port)
        primary = Endpoint(hostname, primary_port)
        additional = dict((port_name, Endpoint(hostname, port_number))
                          for port_name, port_number in
                          options.serverset_module_extra.items())

        # TODO(wickman) Add timeout parameterization here.
        self._zookeeper = ZooKeeper(options.serverset_module_ensemble)
        self._serverset = ServerSet(self._zookeeper,
                                    options.serverset_module_path)
        self._join_args = (primary, additional)
        self._join_kwargs = ({
            'shard': options.serverset_module_shard_id
        } if options.serverset_module_shard_id else {})

    def _join(self):
        log.debug('ServerSet module joining serverset.')
        primary, additional = self._join_args
        self._membership = self._serverset.join(
            primary,
            additional,
            expire_callback=self.on_expiration,
            **self._join_kwargs)

    def on_expiration(self):
        if self._torndown:
            return

        log.debug('Serverset session expired.')
        if not app.get_options().serverset_module_persistence:
            log.debug('Committing seppuku...')
            sys.exit(1)
        else:
            log.debug('Rejoining...')

        self._rejoin_event.set()

    def setup_function(self):
        options = app.get_options()
        if options.serverset_module_enable:
            self._assert_valid_inputs(options)
            self._construct_serverset(options)
            self._thread = ServerSetJoinThread(self._rejoin_event, self._join)
            self._thread.start()
            self._rejoin_event.set()

    def teardown_function(self):
        self._torndown = True
        if self._membership:
            self._serverset.cancel(self._membership)
            self._zookeeper.stop()