X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FN2VC.git;a=blobdiff_plain;f=modules%2Flibjuju%2Fjuju%2Fclient%2Fconnection.py;h=f2150b7dc44481b6e43b0ed844dd2c9b1ea16a91;hp=13770a5343d841bbb8369034c03499eaf7d8bc5e;hb=b2a07f566be558a8b59b8b5dedfe8da5ae1b0132;hpb=7b4702c2e118bab49def498b4b4c236d430dbc13 diff --git a/modules/libjuju/juju/client/connection.py b/modules/libjuju/juju/client/connection.py index 13770a5..f2150b7 100644 --- a/modules/libjuju/juju/client/connection.py +++ b/modules/libjuju/juju/client/connection.py @@ -104,6 +104,8 @@ class Connection: bakery_client=None, loop=None, max_frame_size=None, + retries=3, + retry_backoff=10, ): """Connect to the websocket. @@ -125,6 +127,11 @@ class Connection: :param asyncio.BaseEventLoop loop: The event loop to use for async operations. :param int max_frame_size: The maximum websocket frame size to allow. + :param int retries: When connecting or reconnecting, and all endpoints + fail, how many times to retry the connection before giving up. + :param int retry_backoff: Number of seconds to increase the wait + between connection retry attempts (a backoff of 10 with 3 retries + would wait 10s, 20s, and 30s). """ self = cls() if endpoint is None: @@ -159,6 +166,9 @@ class Connection: self._pinger_task = _Task(self._pinger, self.loop) self._receiver_task = _Task(self._receiver, self.loop) + self._retries = retries + self._retry_backoff = retry_backoff + self.facades = {} self.messages = IdQueue(loop=self.loop) self.monitor = Monitor(connection=self) @@ -446,16 +456,30 @@ class 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 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] @@ -463,7 +487,7 @@ class Connection: self.endpoint = result[2] self.cacert = result[3] self._receiver_task.start() - log.info("Driver connected to juju %s", self.addr) + log.debug("Driver connected to juju %s", self.addr) self.monitor.close_called.clear() async def _connect_with_login(self, endpoints): @@ -562,7 +586,7 @@ class _Task: def start(self): async def run(): try: - return await(self.task()) + return await self.task() finally: self.stopped.set() self.stopped.clear()