Esempio n. 1
0
    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()
Esempio n. 2
0
    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()
Esempio n. 3
0
    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