async def _connect(self, endpoints): if len(endpoints) == 0: raise errors.JujuConnectionError('no endpoints to connect to') async def _try_endpoint(endpoint, cacert, delay): if delay: await asyncio.sleep(delay) return await self._open(endpoint, cacert) # Try all endpoints in parallel, with slight increasing delay (+100ms # for each subsequent endpoint); the delay allows us to prefer the # earlier endpoints over the latter. Use first successful connection. tasks = [ self.loop.create_task(_try_endpoint(endpoint, cacert, 0.1 * i)) for i, (endpoint, cacert) in enumerate(endpoints) ] for task in asyncio.as_completed(tasks, loop=self.loop): try: result = await task break except ConnectionError: continue # ignore; try another endpoint else: raise errors.JujuConnectionError( 'Unable to connect to any endpoint: {}'.format(', '.join( [endpoint for endpoint, cacert in endpoints]))) for task in tasks: task.cancel() self.ws = result[0] self.addr = result[1] self.endpoint = result[2] self.cacert = result[3] self._receiver_task.start() log.info("Driver connected to juju %s", self.addr) self.monitor.close_called.clear()
async def _connect(self, endpoints): if len(endpoints) == 0: raise errors.JujuConnectionError('no endpoints to connect to') async def _try_endpoint(endpoint, cacert, delay): if delay: await asyncio.sleep(delay) return await self._open(endpoint, cacert) # Try all endpoints in parallel, with slight increasing delay (+100ms # for each subsequent endpoint); the delay allows us to prefer the # earlier endpoints over the latter. Use first successful connection. tasks = [ self.loop.create_task(_try_endpoint(endpoint, cacert, 0.1 * i)) for i, (endpoint, cacert) in enumerate(endpoints) ] for attempt in range(self._retries + 1): for task in asyncio.as_completed(tasks, loop=self.loop): try: result = await task break except ConnectionError: continue # ignore; try another endpoint else: _endpoints_str = ', '.join( [endpoint for endpoint, cacert in endpoints]) if attempt < self._retries: log.debug('Retrying connection to endpoints: {}; ' 'attempt {} of {}'.format( _endpoints_str, attempt + 1, self._retries + 1)) await asyncio.sleep((attempt + 1) * self._retry_backoff) continue else: raise errors.JujuConnectionError( 'Unable to connect to any endpoint: ' '{}'.format(_endpoints_str)) # only executed if inner loop's else did not continue # (i.e., inner loop did break due to successful connection) break for task in tasks: task.cancel() self.ws = result[0] self.addr = result[1] self.endpoint = result[2] self.cacert = result[3] self._receiver_task.start() log.debug("Driver connected to juju %s", self.addr) self.monitor.close_called.clear()
def _build_facades(self, facades): self.facades.clear() for facade in facades: name = facade['name'] # the following attempts to get the best facade version for the # client. The client knows about the best facade versions it speaks, # so in order to be compatible forwards and backwards we speak a # common facade versions. if (name not in client_facades) and (name not in self.specified_facades): # if a facade is required but the client doesn't know about # it, then log a warning. log.warning('unknown facade {}'.format(name)) try: known = [] # allow the ability to specify a set of facade versions, so the # client can define the non-conservitive facade client pinning. if name in self.specified_facades: known = self.specified_facades[name]['versions'] elif name in client_facades: known = client_facades[name]['versions'] else: raise errors.JujuConnectionError( "unexpected facade {}".format(name)) discovered = facade['versions'] version = max(set(known).intersection(set(discovered))) except ValueError: # this can occur if known is [1, 2] and discovered is [3, 4] # there is just no way to know how to communicate with the # facades we're trying to call. log.warning( "unknown common facade version for {}".format(name)) except errors.JujuConnectionError: # If the facade isn't with in the local facades then it's not # possible to reason about what version should be used. In this # case we should log the facade was found, but we couldn't # handle it. log.warning( "unexpected facade {} found, unable to decipher version to use" .format(name)) else: self.facades[name] = version