def update_routing_table(self): """ Update the routing table from the first router able to provide valid routing information. """ # copied because it can be modified copy_of_routers = list(self.routing_table.routers) has_tried_initial_routers = False if self.missing_writer: has_tried_initial_routers = True if self.update_routing_table_with_routers(resolve(self.initial_address)): return if self.update_routing_table_with_routers(copy_of_routers): return if not has_tried_initial_routers: initial_routers = resolve(self.initial_address) for router in copy_of_routers: if router in initial_routers: initial_routers.remove(router) if initial_routers: if self.update_routing_table_with_routers(initial_routers): return # None of the routers have been successful, so just fail raise ServiceUnavailable("Unable to retrieve routing information")
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)
def __init__(self, uri, **config): self.initial_address = initial_address = SocketAddress.from_uri( uri, DEFAULT_PORT) self.security_plan = security_plan = SecurityPlan.build(**config) self.encrypted = security_plan.encrypted routing_context = SocketAddress.parse_routing_context(uri) if not security_plan.routing_compatible: # this error message is case-specific as there is only one incompatible # scenario right now raise ValueError( "TRUST_ON_FIRST_USE is not compatible with routing") def connector(address, error_handler): return connect(address, security_plan.ssl_context, urlparse(uri).hostname, error_handler, **config) pool = RoutingConnectionPool(connector, initial_address, routing_context, *resolve(initial_address), **config) try: pool.update_routing_table() except: pool.close() raise else: Driver.__init__(self, pool, **config)
def acquire(self, access_mode=None): for address in resolve(self.address): try: connection = self.acquire_direct(address) # should always be a resolved address except ServiceUnavailable: pass else: return connection raise ServiceUnavailable("Cannot acquire connection to {!r}".format(self.address))
def connect(address, ssl_context=None, error_handler=None, **config): """ Connect and perform a handshake and return a valid Connection object, assuming a protocol version can be agreed. """ # Establish a connection to the host and port specified # Catches refused connections see: # https://docs.python.org/2/library/errno.html log_debug("~~ [RESOLVE] %s", address) last_error = None for resolved_address in resolve(address): log_debug("~~ [RESOLVED] %s -> %s", address, resolved_address) try: s = _connect(resolved_address, **config) s, der_encoded_server_certificate = _secure(s, address[0], ssl_context, **config) connection = _handshake(s, resolved_address, der_encoded_server_certificate, error_handler, **config) except Exception as error: last_error = error else: return connection if last_error is None: raise ServiceUnavailable("Failed to resolve addresses for %s" % address) else: raise last_error