Exemple #1
0
 def run(self, query, parameters=None, mode=None, bookmarks=None, metadata=None, timeout=None, db=None, **handlers):
     if db is not None:
         raise ConfigurationError("Database name parameter for selecting database is not supported in Bolt Protocol {!r}. Database name {!r}.".format(Bolt3.PROTOCOL_VERSION, db))
     if not parameters:
         parameters = {}
     extra = {}
     if mode in (READ_ACCESS, "r"):
         extra["mode"] = "r"  # It will default to mode "w" if nothing is specified
     if bookmarks:
         try:
             extra["bookmarks"] = list(bookmarks)
         except TypeError:
             raise TypeError("Bookmarks must be provided within an iterable")
     if metadata:
         try:
             extra["tx_metadata"] = dict(metadata)
         except TypeError:
             raise TypeError("Metadata must be coercible to a dict")
     if timeout:
         try:
             extra["tx_timeout"] = int(1000 * timeout)
         except TypeError:
             raise TypeError("Timeout must be specified as a number of seconds")
     fields = (query, parameters, extra)
     log.debug("[#%04X]  C: RUN %s", self.local_port, " ".join(map(repr, fields)))
     if query.upper() == u"COMMIT":
         self._append(b"\x10", fields, CommitResponse(self, **handlers))
     else:
         self._append(b"\x10", fields, Response(self, **handlers))
     self._is_reset = False
Exemple #2
0
    def __init__(self, opener, pool_config, workspace_config, routing_context,
                 addresses):
        """

        :param opener:
        :param pool_config:
        :param workspace_config:
        :param routing_context: Dictionary with routing information
        :param addresses:
        """
        super(Neo4jPool, self).__init__(opener, pool_config, workspace_config)
        # Each database have a routing table, the default database is a special case.
        log.debug("[#0000]  C: <NEO4J POOL> routing addresses %r", addresses)
        self.init_address = addresses[0]
        self.routing_tables = {
            workspace_config.database:
            RoutingTable(database=workspace_config.database, routers=addresses)
        }
        self.routing_context = routing_context
        if self.routing_context is None:
            self.routing_context = {}
        elif "address" in self.routing_context:
            raise ConfigurationError(
                "The key 'address' is reserved for routing context.")
        self.routing_context["address"] = str(self.init_address)
        self.refresh_lock = Lock()
Exemple #3
0
    def route(self, database=None, bookmarks=None):
        if database is not None:  # default database
            raise ConfigurationError("Database name parameter for selecting database is not "
                                     "supported in Bolt Protocol {!r}. Database name {!r}. "
                                     "Server Agent {!r}.".format(Bolt3.PROTOCOL_VERSION, database,
                                                                 self.server_info.agent))

        metadata = {}
        records = []

        def fail(md):
            from neo4j._exceptions import BoltRoutingError
            if md.get("code") == "Neo.ClientError.Procedure.ProcedureNotFound":
                raise BoltRoutingError("Server does not support routing", self.unresolved_address)
            else:
                raise BoltRoutingError("Routing support broken on server", self.unresolved_address)

        # Ignoring database and bookmarks because there is no multi-db support.
        # The bookmarks are only relevant for making sure a previously created
        # db exists before querying a routing table for it.
        self.run(
            "CALL dbms.cluster.routing.getRoutingTable($context)",  # This is an internal procedure call. Only available if the Neo4j 3.5 is setup with clustering.
            {"context": self.routing_context},
            mode="r",                                               # Bolt Protocol Version(3, 0) supports mode="r"
            on_success=metadata.update, on_failure=fail
        )
        self.pull(on_success=metadata.update, on_records=records.extend)
        self.send_all()
        self.fetch_all()
        routing_info = [dict(zip(metadata.get("fields", ()), values)) for values in records]
        return routing_info
Exemple #4
0
 def begin(self,
           mode=None,
           bookmarks=None,
           metadata=None,
           timeout=None,
           db=None,
           **handlers):
     if db is not None:
         raise ConfigurationError(
             "Database name parameter for selecting database is not supported in Bolt Protocol {!r}. Database name {!r}."
             .format(Bolt3.PROTOCOL_VERSION, db))
     extra = {}
     if mode in (READ_ACCESS, "r"):
         extra[
             "mode"] = "r"  # It will default to mode "w" if nothing is specified
     if bookmarks:
         try:
             extra["bookmarks"] = list(bookmarks)
         except TypeError:
             raise TypeError(
                 "Bookmarks must be provided within an iterable")
     if metadata:
         try:
             extra["tx_metadata"] = dict(metadata)
         except TypeError:
             raise TypeError("Metadata must be coercible to a dict")
     if timeout:
         try:
             extra["tx_timeout"] = int(1000 * timeout)
         except TypeError:
             raise TypeError(
                 "Timeout must be specified as a number of seconds")
     log.debug("[#%04X]  C: BEGIN %r", self.local_port, extra)
     self._append(b"\x11", (extra, ), Response(self, **handlers))
     self._is_reset = False
Exemple #5
0
    def open(cls,
             *addresses,
             auth,
             pool_config,
             workspace_config,
             routing_context=None):
        """Create a new Neo4jPool

        :param addresses: one or more address as positional argument
        :param auth:
        :param pool_config:
        :param workspace_config:
        :param routing_context:
        :return: Neo4jPool
        """

        address = addresses[0]
        if routing_context is None:
            routing_context = {}
        elif "address" in routing_context:
            raise ConfigurationError(
                "The key 'address' is reserved for routing context.")
        routing_context["address"] = str(address)

        def opener(addr, timeout):
            return Bolt.open(addr,
                             auth=auth,
                             timeout=timeout,
                             routing_context=routing_context,
                             **pool_config)

        pool = cls(opener, pool_config, workspace_config, routing_context,
                   address)
        return pool
Exemple #6
0
    def open(cls,
             address,
             *,
             auth,
             pool_config,
             workspace_config,
             routing_context=None):
        """Create a new BoltPool

        :param address:
        :param auth:
        :param pool_config:
        :param workspace_config:
        :param routing_context:
        :return: BoltPool
        """

        if routing_context is None:
            routing_context = {}
        elif "address" in routing_context:
            raise ConfigurationError(
                "The key 'address' is reserved for routing context.")
        routing_context["address"] = str(address)

        def opener(addr, timeout):
            return Bolt.open(addr,
                             auth=auth,
                             timeout=timeout,
                             routing_context=routing_context,
                             **pool_config)

        pool = cls(opener, pool_config, workspace_config, routing_context,
                   address)
        return pool
 def consume_chain(data, *config_classes):
     values = []
     for config_class in config_classes:
         if not issubclass(config_class, Config):
             raise TypeError("%r is not a Config subclass" % config_class)
         values.append(config_class._consume(data))
     if data:
         raise ConfigurationError("Unexpected config keys: %s" % ", ".join(data.keys()))
     return values
 def run_get_routing_table(self, on_success, on_failure, database=DEFAULT_DATABASE):
     if database != DEFAULT_DATABASE:
         raise ConfigurationError("Database name parameter for selecting database is not supported in Bolt Protocol {!r}. Database name {!r}. Server Agent {!r}.".format(Bolt3.PROTOCOL_VERSION, database, self.server_info.agent))
     self.run(
         "CALL dbms.cluster.routing.getRoutingTable($context)",  # This is an internal procedure call. Only available if the Neo4j 3.5 is setup with clustering.
         {"context": self.routing_context},
         mode="r",                                               # Bolt Protocol Version(3, 0) supports mode="r"
         on_success=on_success,
         on_failure=on_failure,
     )
Exemple #9
0
    def fetch_routing_info(self, *, address, timeout, database):
        """ Fetch raw routing info from a given router address.

        :param address: router address
        :param timeout: seconds
        :param database: the data base name to get routing table for
        :return: list of routing records or
                 None if no connection could be established
        :raise ServiceUnavailable: if the server does not support routing or
                                   if routing support is broken
        """

        # The name of system database is fixed and named as "system".
        # It cannot be changed for a single instance or a cluster. (We can reliably assume that the system db exists on each instance.)
        #
        # Database name is NOT case sensitive.
        #
        # Each cluster member will host the exact same databases. For example, if a cluster member A has databases "foo" and
        # "system", then all other members in the cluster should also have and only have "foo" and "system".
        # However at a certain time, the cluster members may or may not up-to-date, as a result, cluster members may contain different databases.
        #
        # Maintain a routing table for each database.
        #
        # Default database is named "neo4j", (this can be renamed on the Neo4j server).
        #
        # Any core member in a cluster can provide a routing table for any database inside the cluster.
        # The seed_url can be used to find all databases in the cluster.
        #
        # If the driver failed to refresh routing table with all known routers, then the driver should retry a few times before it raises a ServiceUnavailable.
        #
        # A valid routing table should at least have one router and one reader.
        #
        # To prevent the routing tables from growing infinitely.
        # Stale/Aged routing tables is removed when there is a failure to obtain a routing table.
        # Remove a routing table if it have been aged, timeout = TTL + RoutingConfig.routing_table_purge_delay

        # Carry out Bolt subclass imports locally to avoid circular dependency issues.
        from neo4j.io._bolt3 import Bolt3
        from neo4j.io._bolt4x0 import Bolt4x0

        from neo4j.api import (
            SYSTEM_DATABASE,
            DEFAULT_DATABASE,
            READ_ACCESS,
        )

        metadata = {}
        records = []

        def fail(md):
            if md.get("code") == "Neo.ClientError.Procedure.ProcedureNotFound":
                raise BoltRoutingError("Server does not support routing", address)
            else:
                raise BoltRoutingError("Routing support broken on server", address)

        try:
            with self._acquire(address, timeout) as cx:
                _, _, server_version = (cx.server_info.agent or "").partition("/")
                log.debug("[#%04X]  C: <ROUTING> query=%r", cx.local_port, self.routing_context or {})

                if database is None:
                    database = self.workspace_config.database

                # TODO: This logic should be inside the Bolt subclasses, because it can change depending on Bolt Protocol Version.
                if cx.PROTOCOL_VERSION == Bolt3.PROTOCOL_VERSION:
                    if database != DEFAULT_DATABASE:
                        raise ConfigurationError("Database name parameter for selecting database is not supported in Bolt Protocol {!r}. Database name {!r}. Server Agent {!r}.".format(
                                Bolt3.PROTOCOL_VERSION, database, cx.server_info.agent))
                    cx.run(
                        "CALL dbms.cluster.routing.getRoutingTable($context)",  # This is an internal procedure call. Only available if the Neo4j 3.5 is setup with clustering.
                        {"context": self.routing_context},
                        mode="r",  # Bolt Protocol Version(3, 0) supports mode
                        on_success=metadata.update,
                        on_failure=fail,
                    )
                elif cx.PROTOCOL_VERSION == Bolt4x0.PROTOCOL_VERSION:
                    if database == DEFAULT_DATABASE:
                        cx.run(
                            "CALL dbms.routing.getRoutingTable($context)",
                            {"context": self.routing_context},
                            mode="r",
                            db=SYSTEM_DATABASE,
                            on_success=metadata.update,
                            on_failure=fail,
                        )
                    else:
                        cx.run(
                            "CALL dbms.routing.getRoutingTable($context, $database)",
                            {"context": self.routing_context, "database": database},
                            mode="r",
                            db=SYSTEM_DATABASE,
                            on_success=metadata.update,
                            on_failure=fail,
                        )
                cx.pull(on_success=metadata.update, on_records=records.extend)
                cx.send_all()
                cx.fetch_all()
                routing_info = [dict(zip(metadata.get("fields", ()), values)) for values in records]
                log.debug("[#%04X]  S: <ROUTING> info=%r", cx.local_port, routing_info)
            return routing_info
        except BoltRoutingError as error:
            raise ServiceUnavailable(*error.args)
        except ServiceUnavailable:
            self.deactivate(address=address)
            return None
Exemple #10
0
    def driver(cls, uri, *, auth=None, **config):
        """Create a driver.

        :param uri: the connection URI for the driver, see :ref:`uri-ref` for available URIs.
        :param auth: the authentication details, see :ref:`auth-ref` for available authentication details.
        :param config: driver configuration key-word arguments, see :ref:`driver-configuration-ref` for available key-word arguments.

        :return: :ref:`neo4j-driver-ref` or :ref:`bolt-driver-ref`
        """

        from neo4j.api import (
            parse_neo4j_uri,
            parse_routing_context,
            DRIVER_BOLT,
            DRIVER_NEO4j,
            SECURITY_TYPE_NOT_SECURE,
            SECURITY_TYPE_SELF_SIGNED_CERTIFICATE,
            SECURITY_TYPE_SECURE,
            URI_SCHEME_BOLT,
            URI_SCHEME_NEO4J,
            URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE,
            URI_SCHEME_BOLT_SECURE,
            URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE,
            URI_SCHEME_NEO4J_SECURE,
        )

        driver_type, security_type, parsed = parse_neo4j_uri(uri)

        if "trust" in config.keys():
            if config.get("trust") not in [
                    TRUST_ALL_CERTIFICATES, TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
            ]:
                from neo4j.exceptions import ConfigurationError
                raise ConfigurationError(
                    "The config setting `trust` values are {!r}".format([
                        TRUST_ALL_CERTIFICATES,
                        TRUST_SYSTEM_CA_SIGNED_CERTIFICATES,
                    ]))

        if security_type in [
                SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, SECURITY_TYPE_SECURE
        ] and ("encrypted" in config.keys() or "trust" in config.keys()):
            from neo4j.exceptions import ConfigurationError
            raise ConfigurationError(
                "The config settings 'encrypted' and 'trust' can only be used with the URI schemes {!r}. Use the other URI schemes {!r} for setting encryption settings."
                .format([
                    URI_SCHEME_BOLT,
                    URI_SCHEME_NEO4J,
                ], [
                    URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE,
                    URI_SCHEME_BOLT_SECURE,
                    URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE,
                    URI_SCHEME_NEO4J_SECURE,
                ]))

        if security_type == SECURITY_TYPE_SECURE:
            config["encrypted"] = True
        elif security_type == SECURITY_TYPE_SELF_SIGNED_CERTIFICATE:
            config["encrypted"] = True
            config["trust"] = TRUST_ALL_CERTIFICATES

        if driver_type == DRIVER_BOLT:
            return cls.bolt_driver(parsed.netloc, auth=auth, **config)
        elif driver_type == DRIVER_NEO4j:
            routing_context = parse_routing_context(parsed.query)
            return cls.neo4j_driver(parsed.netloc,
                                    auth=auth,
                                    routing_context=routing_context,
                                    **config)
    def driver(cls, uri, *, auth=None, **config):
        """ Create a Neo4j driver that uses socket I/O and thread-based
        concurrency.

        :param uri:

            ``bolt://host[:port]``

            **Settings:** BoltDriver with no encryption.

            ``bolt+ssc://host[:port]``

            **Settings:** BoltDriver with encryption (accepts self signed certificates).

            ``bolt+s://host[:port]``

            **Settings:** BoltDriver with encryption (accepts only certificates signed by an certificate authority), full certificate checks.

            ``neo4j://host[:port][?routing_context]``

            **Settings:** Neo4jDriver with no encryption.

            ``neo4j+ssc://host[:port][?routing_context]``

            **Settings:** Neo4jDriver with encryption (accepts self signed certificates).

            ``neo4j+s://host[:port][?routing_context]``

            **Settings:** Neo4jDriver with encryption (accepts only certificates signed by an certificate authority), full certificate checks.

        :param auth:
        :param config: connection configuration settings
        """

        from neo4j.api import (
            parse_neo4j_uri,
            parse_routing_context,
            DRIVER_BOLT,
            DRIVER_NEO4j,
            SECURITY_TYPE_NOT_SECURE,
            SECURITY_TYPE_SELF_SIGNED_CERTIFICATE,
            SECURITY_TYPE_SECURE,
            URI_SCHEME_BOLT,
            URI_SCHEME_NEO4J,
            URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE,
            URI_SCHEME_BOLT_SECURE,
            URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE,
            URI_SCHEME_NEO4J_SECURE,
        )

        driver_type, security_type, parsed = parse_neo4j_uri(uri)

        if "trust" in config.keys():
            if config.get("trust") not in [
                    TRUST_ALL_CERTIFICATES, TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
            ]:
                from neo4j.exceptions import ConfigurationError
                raise ConfigurationError(
                    "The config setting `trust` values are {!r}".format([
                        TRUST_ALL_CERTIFICATES,
                        TRUST_SYSTEM_CA_SIGNED_CERTIFICATES,
                    ]))

        if security_type in [
                SECURITY_TYPE_SELF_SIGNED_CERTIFICATE, SECURITY_TYPE_SECURE
        ] and ("encrypted" in config.keys() or "trust" in config.keys()):
            from neo4j.exceptions import ConfigurationError
            raise ConfigurationError(
                "The config settings 'encrypted' and 'trust' can only be used with the URI schemes {!r}. Use the other URI schemes {!r} for setting encryption settings."
                .format([
                    URI_SCHEME_BOLT,
                    URI_SCHEME_NEO4J,
                ], [
                    URI_SCHEME_BOLT_SELF_SIGNED_CERTIFICATE,
                    URI_SCHEME_BOLT_SECURE,
                    URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE,
                    URI_SCHEME_NEO4J_SECURE,
                ]))

        if security_type == SECURITY_TYPE_SECURE:
            config["encrypted"] = True
        elif security_type == SECURITY_TYPE_SELF_SIGNED_CERTIFICATE:
            config["encrypted"] = True
            config["trust"] = TRUST_ALL_CERTIFICATES

        if driver_type == DRIVER_BOLT:
            return cls.bolt_driver(parsed.netloc, auth=auth, **config)
        elif driver_type == DRIVER_NEO4j:
            routing_context = parse_routing_context(parsed.query)
            return cls.neo4j_driver(parsed.netloc,
                                    auth=auth,
                                    routing_context=routing_context,
                                    **config)