def get_plans(self, reference): """Get the plans for a given charm. @param the Reference to a charm. @return a tuple of plans or an empty tuple if no plans. @raise ServerError """ response = make_request( '{}charm?charm-url={}'.format(self.url, 'cs:' + reference.path()), timeout=self.timeout, client=self._client) try: return tuple(map(lambda plan: Plan( url=plan['url'], plan=plan['plan'], created_on=datetime.datetime.strptime( plan['created-on'], "%Y-%m-%dT%H:%M:%SZ" ), description=plan.get('description'), price=plan.get('price')), response)) except Exception as err: log.error( 'cannot process plans: invalid JSON response: {!r}'.format( response)) raise ServerError( 'unable to get list of plans for {}: {}'.format( reference.path(), err))
def get_wallet(self, wallet_name): """Get a single wallet. @param the name of the wallet. @return the wallet's total. @raise ServerError """ response = make_request( '{}wallet/{}'.format(self.url, wallet_name), timeout=self.timeout, client=self._client) try: total = response['total'] return { 'credit': response['credit'], 'limit': response['limit'], 'total': WalletTotal( limit=total['limit'], budgeted=total['budgeted'], available=total['available'], unallocated=total['unallocated'], usage=total['usage'], consumed=total['consumed']) } except Exception as exc: log.error( 'cannot get wallet from server: {!r}'.format(exc)) raise ServerError( 'unable to get list of wallets: {!r}'.format(exc))
def create_case(self, name, email, subject, description, businessImpact, priority, phone): """ Send a case creation to SalesForces to create a ticket. @param name of the person creating the case. @param email of the person creating the case. @param subject of the case. @param description of the case. @param businessImpact of the case. @param priority of the case. @param phone of the person creating the case. @return Nothing if this is ok. @raise ServerError when something goes wrong. @raise ValueError when data passed in are invalid """ if not ('@' in parseaddr(email)[1]): raise ValueError('invalid email: {}'.format(email)) if '' == name or name is None: raise ValueError('empty name') if '' == subject or subject is None: raise ValueError('empty subject') if '' == description or description is None: raise ValueError('empty description') if '' == businessImpact or businessImpact is None: raise ValueError('empty business impact') if priority is None: raise ValueError('Ensure the priority is from the set of ' 'known priorities') if '' == phone or phone is None: raise ValueError('empty phone') try: r = requests.post(self.url, data={ 'orgid': self.orgId, 'recordType': self.recordType, 'name': name, 'email': email, 'subject': subject, 'description': description, self.BUSINESS_IMPACT: businessImpact, 'priority': priority, 'phone': phone, 'external': 1 }, timeout=self.timeout) r.raise_for_status() except Timeout: message = 'Request timed out: {url} timeout: {timeout}' message = message.format(url=self.url, timeout=self.timeout) log.error(message) raise ServerError(message) except RequestException as err: log.info('cannot create case: {}'.format(err)) raise ServerError('cannot create case: {}'.format(err))
def list_wallets(self): """Get the list of wallets. @return an dict containing a list of wallets, a total, and available credit. @raise ServerError """ response = make_request( '{}wallet'.format(self.url), timeout=self.timeout, client=self._client) try: total = response['total'] return { 'credit': response['credit'], 'total': WalletTotal( limit=total['limit'], budgeted=total['budgeted'], available=total['available'], unallocated=total['unallocated'], usage=total['usage'], consumed=total['consumed']), 'wallets': tuple(Wallet( owner=wallet['owner'], wallet=wallet['wallet'], limit=wallet['limit'], budgeted=wallet['budgeted'], unallocated=wallet['unallocated'], available=wallet['available'], consumed=wallet['consumed'], default='default' in wallet) for wallet in response['wallets']), } except Exception as err: log.error( 'cannot process wallets: invalid JSON response: {!r}'.format( response)) raise ServerError( 'unable to get list of wallets: {!r}'.format(err))
def fetch_macaroon(self): """ Fetches the macaroon from the JIMM controller. @return The base64 encoded macaroon. """ try: # We don't use make_request b/c we don't want the request to be # fully handled. This lets us get the macaroon out of the request # and keep it. url = "{}model".format(self.url) response = requests.get(url, timeout=self.timeout) except requests.exceptions.Timeout: message = 'Request timed out: {url} timeout: {timeout}' message = message.format(url=url, timeout=self.timeout) log.error(message) return None except Exception as e: log.info('Unable to contact JIMM due to: {}'.format(e)) return None try: json_response = response.json() except ValueError: log.info( 'cannot process macaroon: ' 'cannot unmarshal response: {!r}'.format(response.content)) return None try: raw_macaroon = json_response['Info']['Macaroon'] except (KeyError, TypeError): log.info( 'cannot process macaroon: invalid JSON response: {!r}'.format( json_response)) return None return json.dumps(raw_macaroon)
def make_request(url, method='GET', query=None, body=None, auth=None, timeout=10, client=None, macaroons=None): """Make a request with the provided data. @param url The url to make the request to. @param method The HTTP request method (defaulting to "GET"). @param query A dict of the query key and values. @param body The optional body as a string or as a JSON decoded dict. @param auth The optional username and password as a tuple, not used if client is not None @param timeout The request timeout in seconds, defaulting to 10 seconds. @param client (httpbakery.Client) holds a context for making http requests with macaroons. @param macaroons Optional JSON serialized, base64 encoded macaroons to be included in the request header. POST/PUT request bodies are assumed to be in JSON format. Return the response content as a JSON decoded object, or an empty dict. Raise a ServerError if a problem occurs in the request/response process. Raise a ValueError if invalid parameters are provided. """ headers = {} kwargs = {'timeout': timeout, 'headers': headers} # Handle the request body. if body is not None: if isinstance(body, collections.Mapping): body = json.dumps(body) kwargs['data'] = body # Handle request methods. if method in ('GET', 'HEAD'): if query: url = '{}?{}'.format(url, urlencode(query, True)) elif method in ('DELETE', 'PATCH', 'POST', 'PUT'): headers['Content-Type'] = 'application/json' else: raise ValueError('invalid method {}'.format(method)) if macaroons is not None: headers['Macaroons'] = macaroons kwargs['auth'] = auth if client is None else client.auth() api_method = getattr(requests, method.lower()) # Perform the request. try: response = api_method(url, **kwargs) except requests.exceptions.Timeout: raise timeout_error(url, timeout) except Exception as err: msg = _server_error_message(url, err) raise ServerError(msg) # Handle error responses. try: response.raise_for_status() except HTTPError as err: msg = _server_error_message(url, err.response.text) raise ServerError(err.response.status_code, msg) except requests.exceptions.RequestException as err: msg = _server_error_message(url, err.message) raise ServerError(msg) # Some requests just result in a status with no response body. if not response.content: return {} # Assume the response body is a JSON encoded string. try: return response.json() except Exception as err: msg = 'Error decoding JSON response: {} message: {}'.format(url, err) log.error(msg) raise ServerError(msg)
def _server_error_message(url, message): """Log and return a server error message.""" msg = _error_message.format(url=url, message=message) log.error(msg) return msg
def make_request( url, method='GET', query=None, body=None, auth=None, macaroons=None, timeout=10): """Make a request with the provided data. @param url The url to make the request to. @param method The HTTP request method (defaulting to "GET"). @param query A dict of the query key and values. @param body The optional body as a string or as a JSON decoded dict. @param auth The optional username and password as a tuple. @param timeout The request timeout in seconds, defaulting to 10 seconds. POST/PUT request bodies are assumed to be in JSON format. Return the response content as a JSON decoded object, or an empty dict. Raise a ServerError if a problem occurs in the request/response process. Raise a ValueError if invalid parameters are provided. """ kwargs = {'auth': auth, 'timeout': timeout, 'headers': {}} # Handle the request body. if body is not None: if isinstance(body, collections.Mapping): body = json.dumps(body) kwargs['data'] = body # Handle request methods. if method in ('GET', 'HEAD'): if query: url = '{}?{}'.format(url, urlencode(query, True)) elif method in ('POST', 'PUT'): kwargs['headers'] = {'Content-Type': 'application/json'} else: raise ValueError('invalid method {}'.format(method)) if macaroons is not None: kwargs['headers']['Bakery-Protocol-Version'] = 1 kwargs['headers']['Macaroons'] = macaroons api_method = getattr(requests, method.lower()) # Perform the request. try: response = api_method(url, **kwargs) except requests.exceptions.Timeout: raise timeout_error(url, timeout) except Exception as err: msg = _server_error_message(url, err) raise ServerError(msg) # Handle error responses. try: response.raise_for_status() except HTTPError as err: msg = _server_error_message(url, err.response.text) raise ServerError(err.response.status_code, msg) except requests.exceptions.RequestException as err: msg = _server_error_message(url, err.message) raise ServerError(msg) # Some requests just result in a status with no response body. if not response.content: return {} # Assume the response body is a JSON encoded string. try: return response.json() except Exception as err: msg = 'Error decoding JSON response: {} message: {}'.format(url, err) log.error(msg) raise ServerError(msg)
def _server_error_message(url, message): """Log and return a server error message.""" msg = _error_message.format(url=url, message=message) log.error(msg) return msg