Source code for ibm_analytics_engine.cf.client

from __future__ import absolute_import

from .logger import Logger

from .service_instances import ServiceInstance
from .service_keys import ServiceKey
from .spaces import Space
from .organizations import Organization

import requests
import json
from datetime import datetime, timedelta

class CloudFoundryException(Exception):
    def __init__(self, message, *args):
        self.message = message
        super(CloudFoundryException, self).__init__(message, *args) 


[docs]class CloudFoundryAPI(object): def __init__(self, api_key=None, api_key_filename=None, api_endpoint='https://api.ng.bluemix.net', provision_poll_timeout_mins=30): self.log = Logger().get_logger(self.__class__.__name__) self.provision_poll_timeout_mins = provision_poll_timeout_mins assert api_key is not None or api_key_filename is not None, "You must provide a value for api_key or for api_key_filename" # allow tests to override the api_key_filename parameter if hasattr(CloudFoundryAPI, 'api_key_filename') and CloudFoundryAPI is not None: api_key_filename = CloudFoundryAPI.api_key_filename if api_key_filename is not None: try: with open(api_key_filename, 'r') as api_file: d = json.load(api_file) try: self.api_key = d['apikey'] except KeyError: # The attibute name used to be self.api_key = d['apiKey'] except: self.log.error('Error retrieving "apiKey" from file {}'.format(api_key_filename)) raise else: self.api_key = api_key self.api_endpoint = api_endpoint self.info = self._get_info() self.service_instances = ServiceInstance(self) self.service_keys = ServiceKey(self) self.spaces = Space(self) self.organizations = Organization(self) def auth(self): self.log.debug('Authenticating to CloudFoundry') url = self.info['authorization_endpoint'] + '/oauth/token' headers = { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', 'Accept': 'application/x-www-form-urlencoded;charset=utf-8', 'Authorization': 'Basic Y2Y6' } data = 'grant_type=password&username=apikey&password={}'.format(self.api_key) try: response = requests.post(url, headers=headers, data=data) response.raise_for_status() except requests.exceptions.RequestException as e: self.log.error('Cloud Foundry Auth Response: ' + response.text) # TODO we should define a custom application exception for this raise self.auth_token = response.json() self.expires_at = datetime.now() + timedelta(seconds=self.auth_token['expires_in']/60) self.log.debug('Authenticated to CloudFoundry') def oidc_token(self): self.log.debug('Retrieving IAM token') url='https://iam.bluemix.net/identity/token' data="grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}".format(self.api_key) try: response = requests.post(url, data=data) response.raise_for_status() except requests.exceptions.RequestException as e: self.log.error('IAM token response: ' + response.text) raise self.oidc_token = response.json() self.oidc_expires_at = datetime.now() + timedelta(seconds=self.oidc_token['expires_in']/60) self.log.debug('Retrieved IAM token') return self.oidc_token def get_auth_token(self): if not hasattr(self, 'auth_token') or not hasattr(self, 'expires_at') or datetime.now() > self.expires_at: self.auth() return self.auth_token def get_oidc_token(self): if not hasattr(self, 'oidc_token') or not hasattr(self, 'oidc_expires_at') or datetime.now() > self.oidc_expires_at: self.oidc_token() return self.oidc_token def _request_headers(self): auth_token = self.get_auth_token() access_token = auth_token['access_token'] token_type = auth_token['token_type'] headers = { 'accept': 'application/json', 'authorization': '{} {}'.format(token_type, access_token), 'cache-control': 'no-cache', 'content-type': 'application/json' } return headers def _request(self, url, http_method='get', data=None, description='', create_auth_headers=True): if create_auth_headers: headers = self._request_headers() else: headers = {} try: if http_method == 'get': response = requests.get(url, headers=headers) elif http_method == 'post': response = requests.post(url, headers=headers, data=json.dumps(data)) elif http_method == 'delete': response = requests.delete(url, headers=headers) response.raise_for_status() except requests.exceptions.RequestException as e: self.log.debug('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, response.text)) raise CloudFoundryException(message=response.text) try: self.log.debug('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, json.dumps(response.json()))) except ValueError: self.log.debug('{} : {} {} : {} {}'.format(description, http_method, url, response.status_code, response.text)) return response def _get_info(self): url = '{}/v2/info'.format(self.api_endpoint) response = self._request(url=url, http_method='get', description='_get_info', create_auth_headers=False) return response.json() def space_guid(self, org_name, space_name): assert org_name is not None, "org_name must be provided" assert space_name is not None, "space_name must be provided" org_json = self.organizations.get_organizations(org_name) # Organisation names should be unique - there should only be one result if org_json['total_results'] != 1: raise ValueError('organization name "{}" was not found'.format(org_name)) org_guid = org_json['resources'][0]['metadata']['guid'] space_filter_string = 'q=organization_guid:{}'.format(org_guid) spaces_json = self.spaces.get_spaces(filter_string=space_filter_string) if spaces_json['total_results'] == 0: raise ValueError('no spaces found for orgnaization "{}"'.format(org_name)) for spc in spaces_json['resources']: if spc['entity']['name'] == space_name: return spc['metadata']['guid'] raise ValueError('space "{}" not found for organization "{}"'.format(space_name, org_name)) def orgs_and_spaces(self, org_name=None, space_name=None): if space_name is not None: spaces_json = self.spaces.get_spaces(name=space_name) else: spaces_json = self.spaces.get_spaces() if org_name is not None: organizations_json = self.organizations.get_organizations(org_name) else: organizations_json = self.organizations.get_organizations() def get_spaces_for_org(organization_guid, spaces_json): spaces = [] for spc in spaces_json['resources']: if spc['entity']['organization_guid'] == organization_guid: spaces.append(spc) return spaces orgs_and_spaces = [] for organization in organizations_json['resources']: spaces = [] for space in get_spaces_for_org(organization['metadata']['guid'], spaces_json): spaces.append({ 'name': space['entity']['name'], 'guid': space['metadata']['guid'] }) orgs_and_spaces.append({ 'name': organization['entity']['name'], 'guid': organization['metadata']['guid'], 'spaces': spaces }) return orgs_and_spaces def print_clusters_in_all_spaces(self): import pprint pp = pprint.PrettyPrinter(indent=4) for org in self.cf.orgs_and_spaces(): for space in org['spaces']: print('ORG {} | SPACE {}'.format(org['name'], space['name'])) print() clusters = iae.clusters(space_guid=space['guid']) if len(clusters) > 0: for cluster in clusters: pp.pprint(cluster) print() def print_orgs_and_spaces(self, org_name=None, space_name=None): oas = self.orgs_and_spaces(org_name, space_name) max_len = 0 for o in oas: if len(o['name']) > max_len: max_len = len(o['name']) for s in o['spaces']: if len(s['name']) > max_len: max_len = len(s['name']) for o in oas: orgname=o['name'] orgguid=o['guid'] format='Org: {orgname:{width}} {orgguid}'.format(orgname=orgname, width=max_len, orgguid=orgguid) print('-' * len(format)) print(format) for s in o['spaces']: spcname=s['name'] spcguid=s['guid'] print('> Spc: {spcname:{width}} {spcguid}'.format(spcname=spcname, width=max_len, spcguid=spcguid))