def make_rest_api_call(method, url, http_headers=None, timeout=None, proxy=None): """Makes a SoftLayer API call against the REST endpoint. :param string method: HTTP method: GET, POST, PUT, DELETE :param string url: endpoint URL :param dict http_headers: HTTP headers to use for the request :param int timeout: number of seconds to use as a timeout """ LOGGER.info('%s %s', method, url) try: resp = requests.request(method, url, headers=http_headers, timeout=timeout, proxies=_proxies_dict(proxy)) resp.raise_for_status() LOGGER.debug(resp.content) if url.endswith('.json'): return json.loads(resp.content) else: return resp.text except requests.HTTPError as ex: if url.endswith('.json'): content = json.loads(ex.response.content) raise exceptions.SoftLayerAPIError(ex.response.status_code, content['error']) else: raise exceptions.SoftLayerAPIError(ex.response.status_code, ex.response.text) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. This currently only works with GET requests :param request request: Request object """ url_parts = [request.endpoint, request.service, request.method] if request.identifier is not None: url_parts.append(str(request.identifier)) url = '%s.%s' % ('/'.join(url_parts), 'json') LOGGER.debug("=== REQUEST ===") LOGGER.info(url) LOGGER.debug(request.transport_headers) try: resp = requests.request('GET', url, headers=request.transport_headers, timeout=request.timeout, proxies=_proxies_dict(request.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.content) resp.raise_for_status() return json.loads(resp.content) except requests.HTTPError as ex: content = json.loads(ex.response.content) raise exceptions.SoftLayerAPIError(ex.response.status_code, content['error']) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def test_exception_from_api(self, _sleep, _time, _random, vs): """Tests escalating scale back when an excaption is thrown""" self.guestObject.return_value = {'activeTransaction': {'id': 1}} vs.side_effect = exceptions.TransportError(104, "Its broken") _time.side_effect = [0, 0, 2, 6, 14, 20, 100] _random.side_effect = [0, 0, 0, 0, 0] value = self.vs.wait_for_ready(1, 20, delay=1) _sleep.assert_has_calls( [mock.call(2), mock.call(4), mock.call(8), mock.call(6)]) self.assertFalse(value)
def test_exception_from_api(self, _sleep, _time, _vs, _dsleep): """Tests escalating scale back when an excaption is thrown""" _dsleep.return_value = False self.guestObject.side_effect = [ exceptions.TransportError(104, "Its broken"), {'activeTransaction': {'id': 1}}, {'provisionDate': 'aaa'} ] # logging calls time.time as of pytest3.3, not sure if there is a better way of getting around that. _time.side_effect = [0, 1, 2, 3, 4] value = self.vs.wait_for_ready(1, 20, delay=1) _sleep.assert_called_once() _dsleep.assert_called_once() self.assertTrue(value)
def test_exception_from_api(self, _sleep, _time, _random, _vs): """Tests escalating scale back when an excaption is thrown""" self.guestObject.return_value = {'activeTransaction': {'id': 1}} _vs.side_effect = exceptions.TransportError(104, "Its broken") # logging calls time.time as of pytest3.3, not sure if there is a better way of getting around that. _time.side_effect = [ 0, 0, 0, 0, 2, 2, 2, 6, 6, 6, 14, 14, 14, 20, 20, 20, 100, 100, 100 ] _random.side_effect = [0, 0, 0, 0, 0] value = self.vs.wait_for_ready(1, 20, delay=1) _sleep.assert_has_calls( [mock.call(2), mock.call(4), mock.call(8), mock.call(6)]) self.assertFalse(value)
def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. This currently only works with GET requests :param request request: Request object """ url_parts = [self.endpoint_url, request.service] if request.identifier is not None: url_parts.append(str(request.identifier)) if request.method is not None: url_parts.append(request.method) for arg in request.args: url_parts.append(str(arg)) request.transport_headers.setdefault('Content-Type', 'application/json') request.transport_headers.setdefault('User-Agent', self.user_agent) url = '%s.%s' % ('/'.join(url_parts), 'json') LOGGER.debug("=== REQUEST ===") LOGGER.info(url) LOGGER.debug(request.transport_headers) try: resp = requests.request('GET', url, headers=request.transport_headers, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.content) resp.raise_for_status() return json.loads(resp.content) except requests.HTTPError as ex: content = json.loads(ex.response.content) raise exceptions.SoftLayerAPIError(ex.response.status_code, content['error']) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the SOAP endpoint. :param request request: Request object """ zeep_settings = Settings(strict=False, xml_huge_tree=True) zeep_transport = Transport(cache=SqliteCache(timeout=86400)) client = Client(f"{self.endpoint_url}/{request.service}?wsdl", settings=zeep_settings, transport=zeep_transport, plugins=[self.history]) # print(client.wsdl.dump()) # print("=============== WSDL ==============") # Must define headers like this because otherwise the objectMask header doesn't work # because it isn't sent in with a namespace. xsd_userauth = xsd.Element( f"{{{self.soapns}}}authenticate", xsd.ComplexType([ xsd.Element(f'{{{self.soapns}}}username', xsd.String()), xsd.Element(f'{{{self.soapns}}}apiKey', xsd.String()) ])) # factory = client.type_factory(f"{self.soapns}") the_mask = client.get_type(f"{{{self.soapns}}}SoftLayer_ObjectMask") xsd_mask = xsd.Element(f"{{{self.soapns}}}SoftLayer_ObjectMask", the_mask) # Object Filter filter_type = client.get_type( f"{{{self.soapns}}}{request.service}ObjectFilter") xsd_filter = xsd.Element( f"{{{self.soapns}}}{request.service}ObjectFilter", filter_type) # Result Limit xsd_resultlimit = xsd.Element( f"{{{self.soapns}}}resultLimit", xsd.ComplexType([ xsd.Element('limit', xsd.String()), xsd.Element('offset', xsd.String()), ])) # Might one day want to support unauthenticated requests, but for now assume user auth. headers = [ xsd_userauth(username=request.transport_user, apiKey=request.transport_password), ] if request.limit: headers.append( xsd_resultlimit(limit=request.limit, offset=request.offset)) if request.mask: headers.append(xsd_mask(mask=request.mask)) if request.filter: # The ** here forces python to treat this dict as properties headers.append(xsd_filter(**request.filter)) if request.identifier: init_param = f"{request.service}InitParameters" init_paramtype = client.get_type(f"{{{self.soapns}}}{init_param}") xsdinit_param = xsd.Element(f"{{{self.soapns}}}{init_param}", init_paramtype) # Might want to check if its an id or globalIdentifier at some point, for now only id. headers.append(xsdinit_param(id=request.identifier)) # NEXT Add params... maybe try: method = getattr(client.service, request.method) except AttributeError as ex: message = f"{request.service}::{request.method}() does not exist in {self.soapns}{request.service}?wsdl" raise exceptions.TransportError(404, message) from ex result = method(_soapheaders=headers) # NEXT GET A WAY TO FIND TOTAL ITEMS try: method_return = f"{request.method}Return" serialize = serialize_object(result) if serialize.get('body'): return serialize['body'][method_return] else: # Some responses (like SoftLayer_Account::getObject) don't have a body? return serialize except KeyError as ex: message = f"Error serializeing response\n{result}\n{ex}" raise exceptions.TransportError(500, message)
def __call__(self, request): """Makes a SoftLayer API call against the XML-RPC endpoint. :param request request: Request object """ largs = list(request.args) headers = request.headers if request.identifier is not None: header_name = request.service + 'InitParameters' headers[header_name] = {'id': request.identifier} if request.mask is not None: headers.update(_format_object_mask(request.mask, request.service)) if request.filter is not None: headers['%sObjectFilter' % request.service] = request.filter if request.limit: headers['resultLimit'] = { 'limit': request.limit, 'offset': request.offset or 0, } largs.insert(0, {'headers': headers}) request.transport_headers.setdefault('Content-Type', 'application/xml') request.transport_headers.setdefault('User-Agent', self.user_agent) url = '/'.join([self.endpoint_url, request.service]) payload = utils.xmlrpc_client.dumps(tuple(largs), methodname=request.method, allow_none=True) LOGGER.debug("=== REQUEST ===") LOGGER.info('POST %s', url) LOGGER.debug(request.transport_headers) LOGGER.debug(payload) try: response = requests.request('POST', url, data=payload, headers=request.transport_headers, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(response.headers) LOGGER.debug(response.content) response.raise_for_status() return utils.xmlrpc_client.loads(response.content)[0][0] except utils.xmlrpc_client.Fault as ex: # These exceptions are formed from the XML-RPC spec # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php error_mapping = { '-32700': exceptions.NotWellFormed, '-32701': exceptions.UnsupportedEncoding, '-32702': exceptions.InvalidCharacter, '-32600': exceptions.SpecViolation, '-32601': exceptions.MethodNotFound, '-32602': exceptions.InvalidMethodParameters, '-32603': exceptions.InternalError, '-32500': exceptions.ApplicationError, '-32400': exceptions.RemoteSystemError, '-32300': exceptions.TransportError, } _ex = error_mapping.get(ex.faultCode, exceptions.SoftLayerAPIError) raise _ex(ex.faultCode, ex.faultString) except requests.HTTPError as ex: raise exceptions.TransportError(ex.response.status_code, str(ex)) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. This currently only works with GET requests :param request request: Request object """ request.transport_headers.setdefault('Content-Type', 'application/json') request.transport_headers.setdefault('User-Agent', self.user_agent) params = request.headers.copy() if request.mask: params['objectMask'] = _format_object_mask(request.mask) if request.limit: params['limit'] = request.limit if request.offset: params['offset'] = request.offset if request.filter: params['objectFilter'] = json.dumps(request.filter) auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth( request.transport_user, request.transport_password, ) method = REST_SPECIAL_METHODS.get(request.method) is_special_method = True if method is None: is_special_method = False method = 'GET' body = {} if request.args: # NOTE(kmcdonald): force POST when there are arguments because # the request body is ignored otherwise. method = 'POST' body['parameters'] = request.args raw_body = None if body: raw_body = json.dumps(body) url_parts = [self.endpoint_url, request.service] if request.identifier is not None: url_parts.append(str(request.identifier)) # Special methods (createObject, editObject, etc) use the HTTP verb # to determine the action on the resource if request.method is not None and not is_special_method: url_parts.append(request.method) url = '%s.%s' % ('/'.join(url_parts), 'json') LOGGER.debug("=== REQUEST ===") LOGGER.info(url) LOGGER.debug(request.transport_headers) LOGGER.debug(raw_body) try: resp = requests.request(method, url, auth=auth, headers=request.transport_headers, params=params, data=raw_body, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.content) resp.raise_for_status() result = json.loads(resp.content) if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except requests.HTTPError as ex: content = json.loads(ex.response.content) raise exceptions.SoftLayerAPIError(ex.response.status_code, content['error']) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. REST calls should mostly work, but is not fully tested. XML-RPC should be used when in doubt :param request request: Request object """ params = request.headers.copy() if request.mask: request.mask = _format_object_mask(request.mask) params['objectMask'] = request.mask if request.limit or request.offset: limit = request.limit or 0 offset = request.offset or 0 params['resultLimit'] = "%d,%d" % (offset, limit) if request.filter: params['objectFilter'] = json.dumps(request.filter) request.params = params auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth( request.transport_user, request.transport_password, ) method = REST_SPECIAL_METHODS.get(request.method) if method is None: method = 'GET' body = {} if request.args: # NOTE(kmcdonald): force POST when there are arguments because # the request body is ignored otherwise. method = 'POST' body['parameters'] = request.args if body: request.payload = json.dumps(body, cls=ComplexEncoder) url_parts = [self.endpoint_url, request.service] if request.identifier is not None: url_parts.append(str(request.identifier)) if request.method is not None: url_parts.append(request.method) request.url = '%s.%s' % ('/'.join(url_parts), 'json') # Prefer the request setting, if it's not None if request.verify is None: request.verify = self.verify try: resp = self.client.request(method, request.url, auth=auth, headers=request.transport_headers, params=request.params, data=request.payload, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) request.url = resp.url resp.raise_for_status() if resp.text != "": try: result = json.loads(resp.text) except ValueError as json_ex: raise exceptions.SoftLayerAPIError(resp.status_code, str(json_ex)) else: raise exceptions.SoftLayerAPIError(resp.status_code, "Empty response.") request.result = result if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except requests.HTTPError as ex: try: message = json.loads(ex.response.text)['error'] request.url = ex.response.url except ValueError as json_ex: if ex.response.text == "": raise exceptions.SoftLayerAPIError(resp.status_code, "Empty response.") raise exceptions.SoftLayerAPIError(resp.status_code, str(json_ex)) raise exceptions.SoftLayerAPIError(ex.response.status_code, message) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the XML-RPC endpoint. :param request request: Request object """ largs = list(request.args) headers = request.headers auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth(request.transport_user, request.transport_password) if request.identifier is not None: header_name = request.service + 'InitParameters' headers[header_name] = {'id': request.identifier} if request.mask is not None: if isinstance(request.mask, dict): mheader = '%sObjectMask' % request.service else: mheader = 'SoftLayer_ObjectMask' request.mask = _format_object_mask(request.mask) headers.update({mheader: {'mask': request.mask}}) if request.filter is not None: headers['%sObjectFilter' % request.service] = request.filter if request.limit: headers['resultLimit'] = { 'limit': request.limit, 'offset': request.offset or 0, } largs.insert(0, {'headers': headers}) request.transport_headers.setdefault('Content-Type', 'application/xml') request.transport_headers.setdefault('User-Agent', self.user_agent) request.url = '/'.join([self.endpoint_url, request.service]) request.payload = xmlrpc.client.dumps(tuple(largs), methodname=request.method, allow_none=True) # Prefer the request setting, if it's not None verify = request.verify if verify is None: request.verify = self.verify try: resp = self.client.request('POST', request.url, data=request.payload, auth=auth, headers=request.transport_headers, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) resp.raise_for_status() result = xmlrpc.client.loads(resp.content)[0][0] if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except xmlrpc.client.Fault as ex: # These exceptions are formed from the XML-RPC spec # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php error_mapping = { '-32700': exceptions.NotWellFormed, '-32701': exceptions.UnsupportedEncoding, '-32702': exceptions.InvalidCharacter, '-32600': exceptions.SpecViolation, '-32601': exceptions.MethodNotFound, '-32602': exceptions.InvalidMethodParameters, '-32603': exceptions.InternalError, '-32500': exceptions.ApplicationError, '-32400': exceptions.RemoteSystemError, '-32300': exceptions.TransportError, } _ex = error_mapping.get(ex.faultCode, exceptions.SoftLayerAPIError) raise _ex(ex.faultCode, ex.faultString) except requests.HTTPError as ex: raise exceptions.TransportError(ex.response.status_code, str(ex)) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. REST calls should mostly work, but is not fully tested. XML-RPC should be used when in doubt :param request request: Request object """ request.transport_headers.setdefault('Content-Type', 'application/json') request.transport_headers.setdefault('User-Agent', self.user_agent) params = request.headers.copy() if request.mask: params['objectMask'] = _format_object_mask(request.mask) if request.limit: params['limit'] = request.limit if request.offset: params['offset'] = request.offset if request.filter: params['objectFilter'] = json.dumps(request.filter) auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth( request.transport_user, request.transport_password, ) method = REST_SPECIAL_METHODS.get(request.method) if method is None: method = 'GET' body = {} if request.args: # NOTE(kmcdonald): force POST when there are arguments because # the request body is ignored otherwise. method = 'POST' body['parameters'] = request.args raw_body = None if body: raw_body = json.dumps(body) url_parts = [self.endpoint_url, request.service] if request.identifier is not None: url_parts.append(str(request.identifier)) if request.method is not None: url_parts.append(request.method) url = '%s.%s' % ('/'.join(url_parts), 'json') # Prefer the request setting, if it's not None verify = request.verify if verify is None: verify = self.verify LOGGER.debug("=== REQUEST ===") LOGGER.info(url) LOGGER.debug(request.transport_headers) LOGGER.debug(raw_body) try: resp = requests.request(method, url, auth=auth, headers=request.transport_headers, params=params, data=raw_body, timeout=self.timeout, verify=verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.text) resp.raise_for_status() result = json.loads(resp.text) if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except requests.HTTPError as ex: message = json.loads(ex.response.text)['error'] raise exceptions.SoftLayerAPIError(ex.response.status_code, message) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))
def make_xml_rpc_api_call(uri, method, args=None, headers=None, http_headers=None, timeout=None, proxy=None): """Makes a SoftLayer API call against the XML-RPC endpoint. :param string uri: endpoint URL :param string method: method to call E.G.: 'getObject' :param dict headers: XML-RPC headers to use for the request :param dict http_headers: HTTP headers to use for the request :param int timeout: number of seconds to use as a timeout """ if args is None: args = tuple() try: largs = list(args) largs.insert(0, {'headers': headers}) payload = utils.xmlrpc_client.dumps(tuple(largs), methodname=method, allow_none=True) session = requests.Session() req = requests.Request('POST', uri, data=payload, headers=http_headers).prepare() LOGGER.debug("=== REQUEST ===") LOGGER.info('POST %s', uri) LOGGER.debug(req.headers) LOGGER.debug(payload) response = session.send(req, timeout=timeout, proxies=_proxies_dict(proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(response.headers) LOGGER.debug(response.content) response.raise_for_status() result = utils.xmlrpc_client.loads(response.content, )[0][0] return result except utils.xmlrpc_client.Fault as ex: # These exceptions are formed from the XML-RPC spec # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php error_mapping = { '-32700': exceptions.NotWellFormed, '-32701': exceptions.UnsupportedEncoding, '-32702': exceptions.InvalidCharacter, '-32600': exceptions.SpecViolation, '-32601': exceptions.MethodNotFound, '-32602': exceptions.InvalidMethodParameters, '-32603': exceptions.InternalError, '-32500': exceptions.ApplicationError, '-32400': exceptions.RemoteSystemError, '-32300': exceptions.TransportError, } raise error_mapping.get(ex.faultCode, exceptions.SoftLayerAPIError)(ex.faultCode, ex.faultString) except requests.HTTPError as ex: raise exceptions.TransportError(ex.response.status_code, str(ex)) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex))