def fetch_routing_table(self, address):
        """ Fetch a routing table from a given router address.

        :param address: router address
        :return: a new RoutingTable instance or None if the given router is
                 currently unable to provide routing information
        :raise ServiceUnavailable: if no writers are available
        :raise ProtocolError: if the routing information received is unusable
        """
        new_routing_info = self.fetch_routing_info(address)
        if new_routing_info is None:
            return None

        # Parse routing info and count the number of each type of server
        new_routing_table = RoutingTable.parse_routing_info(new_routing_info)
        num_routers = len(new_routing_table.routers)
        num_readers = len(new_routing_table.readers)
        num_writers = len(new_routing_table.writers)

        # No writers are available. This likely indicates a temporary state,
        # such as leader switching, so we should not signal an error.
        # When no writers available, then we flag we are reading in absence of writer
        self.missing_writer = (num_writers == 0)

        # No routers
        if num_routers == 0:
            raise ProtocolError("No routing servers returned from server %r" % (address,))

        # No readers
        if num_readers == 0:
            raise ProtocolError("No read servers returned from server %r" % (address,))

        # At least one of each is fine, so return this table
        return new_routing_table
 def parse_routing_info(cls, records):
     """ Parse the records returned from a getServers call and
     return a new RoutingTable instance.
     """
     if len(records) != 1:
         raise ProtocolError("Expected exactly one record")
     record = records[0]
     routers = []
     readers = []
     writers = []
     try:
         servers = record["servers"]
         for server in servers:
             role = server["role"]
             addresses = []
             for address in server["addresses"]:
                 addresses.extend(resolve(SocketAddress.parse(address, DEFAULT_PORT)))
             if role == "ROUTE":
                 routers.extend(addresses)
             elif role == "READ":
                 readers.extend(addresses)
             elif role == "WRITE":
                 writers.extend(addresses)
         ttl = record["ttl"]
     except (KeyError, TypeError):
         raise ProtocolError("Cannot parse routing info")
     else:
         return cls(routers, readers, writers, ttl)
Exemple #3
0
    def driver(cls, uri, **config):
        """ Acquire a :class:`.Driver` instance for the given URI and
        configuration. The URI scheme determines the Driver implementation
        that will be returned. Options are:

            ``bolt``
              Returns a :class:`.DirectDriver`.

            ``bolt+routing``
              Returns a :class:`.RoutingDriver`.

        :param uri: URI for a graph database service
        :param config: configuration and authentication details (valid keys are listed below)

            `auth`
              An authentication token for the server, for example
              ``("neo4j", "password")``.

            `der_encoded_server_certificate`
              The server certificate in DER format, if required.

            `encrypted`
              A boolean flag to determine whether encryption should be used.
              Defaults to :const:`True`.

            `trust`
              Trust level: one of :attr:`.TRUST_ALL_CERTIFICATES` (default) or
              :attr:`.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES`.

            `user_agent`
              A custom user agent string, if required.

        """
        parsed = urlparse(uri)
        try:
            driver_class = cls.uri_schemes[parsed.scheme]
        except KeyError:
            raise ProtocolError("URI scheme %r not supported" % parsed.scheme)
        else:
            return driver_class(uri, **config)