Ejemplo n.º 1
0
    def get_build(self, train_id, build_id):
        """
        Return information about a specific build

        :param str train_id: The train ID that owns the provided build ID
        :param str build_id: The build ID to retrieve information for
        :returns: :py:class:`fl33t.models.Build`
        :raises InvalidBuildIdError: if the build does not exist
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = "/".join((self.base_team_url,
                        'train/{}/build/{}'.format(train_id, build_id)))

        result = self.get(url)

        if result.status_code in (400, 404):
            raise InvalidBuildIdError('train={}:{}'.format(train_id, build_id))

        if 'build' in result.json():
            build = result.json()['build']
            return Build(client=self, **build)

        raise Fl33tApiException(ENDPOINT_FAILED_MSG.format('build retrieval'))
Ejemplo n.º 2
0
    def has_upgrade_available(self, device_id, currently_installed_id=None):
        """
        Does this device have pending firmware updates?

        :param str device_id: The device ID to check for updates
        :param currently_installed_id: If provided, the build ID currently
            installed on the device
        :type currently_installed_id: str or None
        :returns: :py:class:`fl33t.models.Build` or False
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = '/'.join(
            (self.base_team_url, 'device/{}/build'.format(device_id)))

        params = None
        if currently_installed_id:
            params = {'installed_build_id': currently_installed_id}

        result = self.get(url, params=params)

        # No update available.
        if result.status_code == 204:
            return False

        if 'build' in result.json():
            build = result.json()['build']
            return self.Build(**build)

        raise Fl33tApiException(
            ENDPOINT_FAILED_MSG.format('device firmware check'))
Ejemplo n.º 3
0
    def get_session(self, session_token):
        """
        Return information about a specific session_token

        :param str session_token: The session token that you want information
            about
        :returns: :py:class:`fl33t.models.Session`
        :raises InvalidSessionIdError: if the session token does not exist
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = "/".join(
            (self.base_team_url, 'session/{}'.format(session_token)))

        result = self.get(url)
        if result.status_code in (400, 404):
            raise InvalidSessionIdError()

        if 'session' in result.json():
            session = result.json()['session']
            return Session(client=self, **session)

        raise Fl33tApiException(
            ENDPOINT_FAILED_MSG.format('session retrieval'))
Ejemplo n.º 4
0
    def create(self):
        """
        Create this object in fl33t

        :returns: :py:class:`self`, on success or False, on failure
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        :raises Fl33tClientException: if the model was instantiated without a
            :py:class:`fl33t.Fl33tClient`
        """

        if not self._client:
            raise Fl33tClientException()

        class_name = self.__class__.__name__.lower()

        result = self._client.post(self.base_url, data=self)

        # 409 is a duplicate ID error for devices. For other models, this
        # error shouldn't occur because all other ID's are generated by fl33t
        if result.status_code == 409:
            raise Fl33tApiException(result.text)

        if class_name not in result.json():
            self.logger.exception('Could not create {}'.format(class_name))
            return False

        data = result.json()[class_name]
        for key in data.keys():
            setattr(self, key, data[key])

        return self
Ejemplo n.º 5
0
    def _paginator(self, single_page, url, params, model_name, model,
                   error_msg):
        """
        Paginate through a specific listing endpoint.

        :param bool single_page: If we should be doing pagination, or simply
            returning the single page of results.
        :param str url: The URL to use for the page retrieval
        :param dict params: A :py:`dict` of parameteres to send with the
            request
        :param str model_name: The name of the model that will be used to
            return data
        :param model: The actual class to be used to return data
        :type model: Any subclass of :py:class:`fl33t.models.Base`
        :param str error_msg: The error message to return in the case of an
            API communication exception
        :yields: generator of the provided `model` type
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        total_count = None

        plural_model = '{}s'.format(model_name)

        while True:
            result = self.get(url, params=params)
            data = result.json()
            if plural_model not in data:
                raise Fl33tApiException(ENDPOINT_FAILED_MSG.format(error_msg))

            record_count = 0

            for item in data[plural_model]:
                record_count += 1
                yield model(client=self, **item)

            if single_page:
                break

            total_returned = params['offset'] + record_count

            if total_count is None:
                if '{}_count'.format(model_name) in data:
                    total_count = data['{}_count'.format(model_name)]
                else:
                    total_count = 0

            if total_count > total_returned:
                params['offset'] = params['offset'] + params['limit']

            else:
                break
Ejemplo n.º 6
0
    def get_device(self, device_id):
        """
        Get a device by ID from fl33t.

        :param str device_id: The device ID to retrieve information for
        :returns: :py:class:`fl33t.models.Device`
        :raises InvalidDeviceIdError: if the device does not exist
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = "/".join((self.base_team_url, 'device/{}'.format(device_id)))

        result = self.get(url)

        if result.status_code in (400, 404):
            raise InvalidDeviceIdError(device_id)

        if 'device' in result.json():
            device = result.json()['device']
            return Device(client=self, **device)

        raise Fl33tApiException(ENDPOINT_FAILED_MSG.format('device retrieval'))
Ejemplo n.º 7
0
    def get_train(self, train_id):
        """
        Return information about a specific train

        :param str train_id: The train ID to retrieve information for
        :returns: :py:class:`fl33t.models.Train`
        :raises InvalidTrainIdError: if the train does not exist
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = "/".join((self.base_team_url, 'train/{}'.format(train_id)))

        result = self.get(url)

        if result.status_code in (400, 404):
            raise InvalidTrainIdError(train_id)

        if 'train' in result.json():
            train = result.json()['train']
            return Train(client=self, **train)

        raise Fl33tApiException(ENDPOINT_FAILED_MSG.format('train retrieval'))
Ejemplo n.º 8
0
    def get_fleet(self, fleet_id):
        """
        Return information about a specific fleet

        :param str fleet_id: The fleet ID to retrieve information for
        :returns: :py:class:`fl33t.models.Fleet`
        :raises InvalidFleetIdError: if the fleet does not exist
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        url = "/".join((self.base_team_url, 'fleet/{}'.format(fleet_id)))

        result = self.get(url)

        if result.status_code in (400, 404):
            raise InvalidFleetIdError(fleet_id)

        if 'fleet' in result.json():
            fleet = result.json()['fleet']
            return Fleet(client=self, **fleet)

        raise Fl33tApiException(ENDPOINT_FAILED_MSG.format('fleet retrieval'))
Ejemplo n.º 9
0
    def _request(self, method, url, **kwargs):
        """
        Wrapper for `requests` methods to include the bearer token
        If you need to make a call without the bearer token, make it
        directly against `requests`

        :param str method: The request method to use
        :param str url: The URL to request
        :param kwargs: Any keyword args that :py:module:`requests` methods
            accept
        :returns: :py:class:`requests.Response`
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        headers = kwargs.get('headers') if kwargs.get('headers') else {}
        if 'Authorization' not in headers:
            headers['Authorization'] = 'Bearer {}'.format(self.token)
        if 'Content-Type' not in headers:
            headers['Content-Type'] = 'application/json'
        if 'Accept' not in headers:
            headers['Accept'] = 'application/json'
        kwargs['headers'] = headers

        data = kwargs.pop('data', None)
        if data and isinstance(data, BaseModel):
            data = data.to_json()

        params = kwargs.pop('params', None)

        self.logger.debug('Sending {} request with params: {}'.format(
            method, params))
        self.logger.debug('Sending {} request with payload: {}'.format(
            method, data))
        method = getattr(requests, method.lower())

        try:
            result = method(url, params=params, data=data, **kwargs)
            result.raise_for_status()

        except requests.exceptions.HTTPError as exc:
            if not isinstance(result, requests.Response):
                # The request failed in a way that we don't handle, give the
                # user the raised exception directly.
                raise

            if result.status_code in (401, 403):
                raise UnprivilegedToken(url)

            if result.status_code >= 500:
                # Raise that something went wrong with the fl33t API request
                message = '{} returned a {} error: {}'.format(
                    url, result.status_code, result.text)
                raise Fl33tApiException(message)

            if result.status_code not in (400, 404, 409):
                # Log the exception if the request failed with a status code
                # that we don't handle gracefully. 400/404 (InvalidIdError) and
                # 409 (DuplicateIdError) are meant to be handled by the caller.
                self.logger.exception(exc)

        return result
Ejemplo n.º 10
0
    def _paginator(self, offset, limit, url, params, model_name, model,
                   error_msg):
        """
        Paginate through a specific listing endpoint.

        :param offset: If provided, the starting offset for result sets.
            If not provided, will paginate through *all* records available,
            effectively ignoring the `limit` parameter. To only retrieve the
            first page of results, you must specifically set offset to `0`.
        :type offset: int or None
        :param limit: If provided, the number of records to return.
            Defaults to :py:attr:`default_query_limit`
        :type limit: int or None
        :param str url: The URL to use for the page retrieval
        :param dict params: A :py:`dict` of parameteres to send with the
            request
        :param str model_name: The name of the model that will be used to
            return data
        :param model: The actual class to be used to return data
        :type model: Any subclass of :py:class:`fl33t.models.Base`
        :param str error_msg: The error message to return in the case of an
            API communication exception
        :yields: generator of the provided `model` type
        :raises UnprivilegedToken: if the session token does not have enough
            privilege to perform this action
        :raises Fl33tApiException: if there was a 5xx error returned by fl33t
        """

        total_count = None

        plural_model = '{}s'.format(model_name)
        params.update(self._build_offset_limit(offset=offset, limit=limit))

        single_page_only = not (offset is None and limit is None)

        while True:
            result = self.get(url, params=params)
            data = result.json()
            if plural_model not in data:
                raise Fl33tApiException(ENDPOINT_FAILED_MSG.format(error_msg))

            record_count = 0

            for item in data[plural_model]:
                record_count += 1
                yield model(client=self, **item)

            if single_page_only:
                break

            total_returned = params['offset'] + record_count

            if total_count is None:
                if '{}_count'.format(model_name) in data:
                    total_count = data['{}_count'.format(model_name)]
                else:
                    total_count = 0

            if total_count > total_returned:
                params['offset'] = params['offset'] + params['limit']

            else:
                break