def test_twisted_consume_serverside_cancel(): """ Assert the consumer halts and ``consumer.result`` errbacks when the server explicitly cancels the consumer (by deleting the queue). """ queue = str(uuid.uuid4()) queues = { queue: { "auto_delete": False, "arguments": { "x-expires": 60 * 1000 } } } bindings = [{ "queue": queue, "exchange": "amq.topic", "routing_keys": ["#"] }] consumers = yield api.twisted_consume(lambda x: x, bindings, queues) # Delete the queue and assert the consumer errbacks url = "{base}queues/%2F/{queue}".format(base=HTTP_API, queue=queue) yield treq.delete(url, auth=HTTP_AUTH, timeout=3) _add_timeout(consumers[0].result, 10) try: yield consumers[0].result pytest.fail("Consumer did not errback!") except exceptions.ConsumerCanceled: pass except (defer.TimeoutError, defer.CancelledError): pytest.fail("Timeout reached without consumer calling its errback!")
def test_no_vhost_permissions(admin_user): """Assert a hint is given if the user doesn't have any access to the vhost""" url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user) resp = yield treq.delete(url, auth=HTTP_AUTH, timeout=3) assert resp.code == 204 queue = str(uuid.uuid4()) queues = { queue: { "auto_delete": False, "arguments": { "x-expires": 60 * 1000 } } } amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user) with mock.patch.dict(config.conf, {"amqp_url": amqp_url}): try: yield api.twisted_consume(lambda x: x, [], queues) except exceptions.ConnectionException as e: assert e.reason == ( "The TCP connection appears to have started, but the TLS or AMQP " "handshake with the broker failed; check your connection and " "authentication parameters and ensure your user has permission " "to access the vhost")
def admin_user(): """ Fixture that creates a random admin user and deletes the user afterwards. Useful if the test wishes to alter permissions to test failure cases. Default permissions is complete access to the "/" vhost. Returns: The username of the new administrator. The password is "guest". """ # Create a user with no permissions username = str(uuid.uuid4()) url = "{base}users/{user}".format(base=HTTP_API, user=username) body = {"username": username, "password": "******", "tags": "administrator"} deferred_resp = treq.put(url, json=body, auth=HTTP_AUTH, timeout=3) @pytest_twisted.inlineCallbacks def cp(resp): assert resp.code == 201 url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=username) body = {"configure": ".*", "write": ".*", "read": ".*"} resp = yield treq.put(url, json=body, auth=HTTP_AUTH, timeout=3) assert resp.code == 201 deferred_resp.addCallbacks(cp, cp) pytest_twisted.blockon(deferred_resp) yield username # Cleanup deferred_resp = treq.delete(url, auth=HTTP_AUTH, timeout=3) pytest_twisted.blockon(deferred_resp)
def remove_alarm(self, entity_id, alarm_id): """Remove an alarm.""" d = treq.delete( http.append_segments(self._endpoint, "entities", entity_id, "alarms", alarm_id), headers=http.headers(self._auth_token), ) return d.addCallback(http.check_success, [204])
def _send_request(self): if not self.queue: if self.request_loop.running: self.request_loop.stop() return now = time.time() - 1 # 1 second buffer if (self.rate_remaining < 1+1 and self.rate_reset > now or DiscordRestApiLoop.global_wait > now): self.log.warn("Rate limited: {}".format(self.channel_id)) return payload = self.queue.pop() method = payload['method'] url = payload['url'] content = payload['content'] # url = '{}/channels/{}/messages'.format(HOST, self.channel_id) content = json.dumps({"content": content}) self.log.debug('at _send_request: {} url {}'.format(self.channel_id, url)) if method == 'post': d = treq.post(url, content, headers=HEADERS) elif method == 'patch': d = treq.patch(url, content, headers=HEADERS) elif method == 'delete': d = treq.delete(url, headers=HEADERS) elif method == 'get': d = treq.get(url, headers=HEADERS) d.addCallback(self.update_rate_limits) if not self.queue: self.request_loop.stop()
def delete_entity(self, entity_id): entity_url = http.append_segments(self._endpoint, 'entities', entity_id) d = treq.delete(entity_url, headers=http.headers(self._auth_token)) d.addCallback(http.check_success, [204]) return d
def request(self, method, uri, data=None, name=''): """ Send a REST request to the Adtran device :param method: (string) HTTP method :param uri: (string) fully URL to perform method on :param data: (string) optional data for the request body :param name: (string) optional name of the request, useful for logging purposes :return: (deferred) """ if method.upper() not in self._valid_methods: raise NotImplementedError( "REST method '{}' is not supported".format(method)) url = 'http://{}:{}{}{}'.format(self.ip, self.rest_port, '/' if uri[0] != '/' else '', uri) try: if method.upper() == 'GET': response = yield treq.get(url, auth=(self.username, self.password), timeout=self.timeout, headers=self.REST_GET_REQUEST_HEADER) elif method.upper() == 'POST' or method.upper() == 'PUT': response = yield treq.post( url, data=data, auth=(self.username, self.password), timeout=self.timeout, headers=self.REST_POST_REQUEST_HEADER) elif method.upper() == 'PATCH': response = yield treq.patch( url, data=data, auth=(self.username, self.password), timeout=self.timeout, headers=self.REST_PATCH_REQUEST_HEADER) elif method.upper() == 'DELETE': response = yield treq.delete( url, auth=(self.username, self.password), timeout=self.timeout, headers=self.REST_DELETE_REQUEST_HEADER) else: raise NotImplementedError( "REST method '{}' is not supported".format(method)) except NotImplementedError: raise except ConnectionClosed: returnValue(None) except Exception, e: log.exception("REST {} '{}' request to '{}' failed: {}".format( method, name, url, str(e))) raise
def _delete(self, path, headers, args={}): response = yield treq.delete(path, params=args, agent=self.custom_agent, headers=headers) content = yield treq.content(response) final_response = self.decode_results(content, self.response_headers(response), response.code, response.phrase) returnValue(final_response)
def remove(self): headers = None if self.access_token: headers = {'Authorization': 'Bearer ' + self.access_token} def cbGotResponse(response): return self._handleResponse(response, self.path) d = treq.delete(self.path, headers=headers) d.addCallback(cbGotResponse) return d
def remove_notification_and_plan(self, notification_plan_id, notification_id): """Delete a notification plan and notification id.""" notification_plan_url = http.append_segments(self._endpoint, "notification_plans", notification_plan_id) d = treq.delete(notification_plan_url, headers=http.headers(self._auth_token)) d.addCallback(http.check_success, [204]) def delete_notification(_): notification_url = http.append_segments(self._endpoint, "notifications", notification_id) return treq.delete(notification_url, headers=http.headers(self._auth_token)) d.addCallback(delete_notification) d.addCallback(http.check_success, [204]) return d
def remove_container(self, name): """ Remove a container. :param unicode name: The name of the container to remove. :returns: A tuple of (cluster, api_response) """ request = delete(self.base_url + b"/configuration/containers/" + name.encode("ascii"), persistent=False) request.addCallback(check_and_decode_json, OK) request.addCallback(lambda response: (self, response)) return request
def delete(self, rcs): """Removes the scaling policy. :param TestResources rcs: The integration test resources instance. This provides useful information to complete the request, like which endpoint to use to make the API request. :return: A :class:`Deferred` which, when triggered, removes the scaling policy. It returns the test resources supplied, easing continuity of integration test code. """ return (treq.delete( "%s?force=true" % self.link, headers=headers(str(rcs.token)), pool=self.scaling_group.pool, ).addCallback(check_success, [204, 404])).addCallback(lambda _: rcs)
def test_cached_pool(self): """ The first use of the module-level API populates the global connection pool, which is used for all subsequent requests. """ pool = SyntacticAbominationHTTPConnectionPool() self.patch(treq.api, "HTTPConnectionPool", lambda reactor, persistent: pool) self.failureResultOf(treq.head("http://test.com"), TabError) self.failureResultOf(treq.get("http://test.com"), TabError) self.failureResultOf(treq.post("http://test.com"), TabError) self.failureResultOf(treq.put("http://test.com"), TabError) self.failureResultOf(treq.delete("http://test.com"), TabError) self.failureResultOf(treq.request("OPTIONS", "http://test.com"), TabError) self.assertEqual(pool.requests, 6)
def stop_responding(self, server_name, challenge, response): from twisted.internet import reactor full_name = challenge.validation_domain_name(server_name) subdomain = _split_zone(full_name, self._zone_name) url = ('https://dns.api.gandi.net/api/v5/domains/' '{zone}/records/{subdomain}/{type}').format( zone=self._zone_name, subdomain=subdomain, type='TXT', ) if subdomain == '': subdomain = '@' response = yield treq.delete(url, headers=self._headers()) print((yield response.text())) yield deferLater(reactor, self._settle_delay, lambda: None) print("stop settled")
def remove_container(self, name): """ Remove a container. :param unicode name: The name of the container to remove. :returns: A tuple of (cluster, api_response) """ request = delete( self.base_url + b"/configuration/containers/" + name.encode("ascii"), persistent=False ) request.addCallback(check_and_decode_json, OK) request.addCallback(lambda response: (self, response)) return request
def delete_dataset(self, dataset_id): """ Delete a dataset. :param unicode dataset_id: The uuid of the dataset to be modified. :returns: A 2-tuple of (cluster, api_response) """ request = delete(self.base_url + b"/configuration/datasets/%s" % (dataset_id.encode('ascii'), ), headers={b"content-type": b"application/json"}, persistent=False) request.addCallback(json_content) # Return cluster and API response request.addCallback(lambda response: (self, response)) return request
def remove_from_load_balancer(endpoint, auth_token, loadbalancer_id, node_id): """ Remove a node from a load balancer. :param str endpoint: Load balancer endpoint URI. :param str auth_token: Keystone Auth Token. :param str loadbalancer_id: The ID for a cloud loadbalancer. :param str node_id: The ID for a node in that cloudloadbalancer. :returns: A Deferred that fires with None if the operation completed successfully, or errbacks with an APIError. """ path = append_segments(endpoint, 'loadbalancers', str(loadbalancer_id), 'nodes', str(node_id)) d = treq.delete(path, headers=headers(auth_token)) d.addCallback(check_success, [200, 202]) d.addErrback(wrap_request_error, path, 'remove') d.addCallback(lambda _: None) return d
def delete(self, rcs): """Removes the scaling policy. :param TestResources rcs: The integration test resources instance. This provides useful information to complete the request, like which endpoint to use to make the API request. :return: A :class:`Deferred` which, when triggered, removes the scaling policy. It returns the test resources supplied, easing continuity of integration test code. """ return ( treq.delete( "%s?force=true" % self.link, headers=headers(str(rcs.token)), pool=self.scaling_group.pool, ) .addCallback(check_success, [204, 404]) ).addCallback(lambda _: rcs)
def remove_from_load_balancer(endpoint, auth_token, loadbalancer_id, node_id): """ Remove a node from a load balancer. :param str endpoint: Load balancer endpoint URI. :param str auth_token: Keystone Auth Token. :param str loadbalancer_id: The ID for a cloud loadbalancer. :param str node_id: The ID for a node in that cloudloadbalancer. :returns: A Deferred that fires with None if the operation completed successfully, or errbacks with an APIError. """ path = append_segments(endpoint, 'loadbalancers', str(loadbalancer_id), 'nodes', str(node_id)) d = treq.delete(path, headers=headers(auth_token)) d.addCallback(check_success, [200, 202]) d.addErrback(wrap_request_error, endpoint, 'remove') d.addCallback(lambda _: None) return d
def remove_notification_and_plan(self, notification_plan_id, notification_id): """Delete a notification plan and notification id.""" notification_plan_url = http.append_segments(self._endpoint, 'notification_plans', notification_plan_id) d = treq.delete(notification_plan_url, headers=http.headers(self._auth_token)) d.addCallback(http.check_success, [204]) def delete_notification(_): notification_url = http.append_segments(self._endpoint, 'notifications', notification_id) return treq.delete(notification_url, headers=http.headers(self._auth_token)) d.addCallback(delete_notification) d.addCallback(http.check_success, [204]) return d
def delete_dataset(self, dataset_id): """ Delete a dataset. :param unicode dataset_id: The uuid of the dataset to be modified. :returns: A 2-tuple of (cluster, api_response) """ request = delete( self.base_url + b"/configuration/datasets/%s" % ( dataset_id.encode('ascii'), ), headers={b"content-type": b"application/json"}, persistent=False ) request.addCallback(check_and_decode_json, OK) # Return cluster and API response request.addCallback(lambda response: (self, response)) return request
def test_twisted_consume_serverside_cancel(queue_and_binding): """ Assert the consumer halts and ``consumer.result`` errbacks when the server explicitly cancels the consumer (by deleting the queue). """ queues, bindings = queue_and_binding consumers = yield api.twisted_consume(lambda x: x, bindings, queues) # Delete the queue and assert the consumer errbacks url = "{base}queues/%2F/{queue}".format(base=HTTP_API, queue=list(queues.keys())[0]) yield treq.delete(url, auth=HTTP_AUTH, timeout=3) _add_timeout(consumers[0].result, 10) try: yield consumers[0].result pytest.fail("Consumer did not errback!") except exceptions.ConsumerCanceled: pass except (defer.TimeoutError, defer.CancelledError): pytest.fail("Timeout reached without consumer calling its errback!")
def remove_alarm(self, entity_id, alarm_id): """Remove an alarm.""" d = treq.delete(http.append_segments(self._endpoint, 'entities', entity_id, 'alarms', alarm_id), headers=http.headers(self._auth_token)) return d.addCallback(http.check_success, [204])
def delete(self, url): return treq.delete("%s%s" % (self.url, url), persistent=False)
def when_removed_from_loadbalancers(_ignore): d = treq.delete(append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) d.addCallback(check_success, [204]) d.addErrback(wrap_request_error, server_endpoint, 'server_delete') return d
def test_twisted_consume_connection_reset(queue_and_binding): """ Assert consuming works across connections and handles connection resets. This test sets up a queue, publishes 2 messages to itself, then kills all active connections on the broker. It then sends a third message and asserts the consumer gets it. """ queues, bindings = queue_and_binding msg = message.Message( topic=u"nice.message", headers={u"niceness": u"very"}, body={u"encouragement": u"You're doing great!"}, ) messages_received = [] two_received = defer.Deferred() # Fired by the callback on 2 messages def callback(message): """Count to, 2, fire a deferred, then count to 3 and quit.""" messages_received.append(message) if len(messages_received) == 2: two_received.callback(None) if len(messages_received) == 3: raise exceptions.HaltConsumer() consumers = yield api.twisted_consume(callback, bindings, queues) # Wait for two messages to get through, kill the connection, and then send # the third and wait for the consumer to finish yield threads.deferToThread(api.publish, msg, "amq.topic") yield threads.deferToThread(api.publish, msg, "amq.topic") _add_timeout(two_received, 10) try: yield two_received except (defer.TimeoutError, defer.CancelledError): pytest.fail("Timeout reached without receiving first two messages") for _ in range(10): conns = yield task.deferLater(reactor, 1, treq.get, HTTP_API + "connections", auth=HTTP_AUTH, timeout=3) conns = yield conns.json() this_conn = [ c for c in conns if "app" in c["client_properties"] and c["client_properties"] ["app"] == "test_twisted_consume_connection_reset" ] if this_conn: cname = six.moves.urllib.parse.quote(this_conn[0]["name"]) yield treq.delete(HTTP_API + "connections/" + cname, auth=HTTP_AUTH, timeout=3) break else: pytest.fail("Unable to find and kill connection!") # The consumer should receive this third message after restarting its connection # and then it should exit gracefully. yield threads.deferToThread(api.publish, msg, "amq.topic") _add_timeout(consumers[0].result, 10) try: yield consumers[0].result except exceptions.HaltConsumer: assert len(messages_received) == 3 except (defer.TimeoutError, defer.CancelledError): yield consumers[0].cancel() pytest.fail("Timeout reached without consumer halting!")
def clean(response): if response.code == 200: print('deleting and recreating index') return treq.delete('http://localhost:9200/history')
def clean_es(reactor): """ Drops everythin from elasticsearch """ return treq.delete('http://localhost:9200/_all')
def verified_delete(log, server_endpoint, auth_token, server_id, interval=5, timeout=3660, clock=None): """ Attempt to delete a server from the server endpoint, and ensure that it is deleted by trying again until getting the server results in a 404. There is a possibility Nova sometimes fails to delete servers. Log if this happens, and if so, re-evaluate workarounds. Time out attempting to verify deletes after a period of time and log an error. :param log: A bound logger. :param str server_endpoint: Server endpoint URI. :param str auth_token: Keystone Auth token. :param str server_id: Opaque nova server id. :param int interval: Deletion interval in seconds - how long until verifying a delete is retried. Default: 5. :param int timeout: Seconds after which the deletion will be logged as a failure, if Nova fails to return a 404. Default is 3660, because if the server is building, the delete will not happen until immediately after it has finished building. :return: Deferred that fires when the expected status has been seen. """ del_log = log.bind(instance_id=server_id) del_log.msg('Deleting server') path = append_segments(server_endpoint, 'servers', server_id) d = treq.delete(path, headers=headers(auth_token)) d.addCallback(check_success, [204]) d.addErrback(wrap_request_error, path, 'server_delete') if clock is None: # pragma: no cover from twisted.internet import reactor clock = reactor def verify(_): def check_status(): check_d = treq.head( append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) check_d.addCallback(check_success, [404]) return check_d start_time = clock.seconds() timeout_description = ( "Waiting for Nova to actually delete server {0}".format(server_id)) verify_d = retry_and_timeout(check_status, timeout, next_interval=repeating_interval(interval), clock=clock, deferred_description=timeout_description) def on_success(_): time_delete = clock.seconds() - start_time del_log.msg('Server deleted successfully: {time_delete} seconds.', time_delete=time_delete) verify_d.addCallback(on_success) verify_d.addErrback(del_log.err) d.addCallback(verify) return d
def delete_entity(self, entity_id): entity_url = http.append_segments(self._endpoint, "entities", entity_id) d = treq.delete(entity_url, headers=http.headers(self._auth_token)) d.addCallback(http.check_success, [204]) return d
def cb_delete(url): d = treq.delete(url) d.addCallbacks(print_response, log.err) return d
def delete_notification(_): notification_url = http.append_segments(self._endpoint, "notifications", notification_id) return treq.delete(notification_url, headers=http.headers(self._auth_token))
def delete_notification(_): notification_url = http.append_segments(self._endpoint, 'notifications', notification_id) return treq.delete(notification_url, headers=http.headers(self._auth_token))
def verified_delete(log, server_endpoint, auth_token, server_id, interval=5, timeout=120, clock=None): """ Attempt to delete a server from the server endpoint, and ensure that it is deleted by trying again until getting the server results in a 404. There is a possibility Nova sometimes fails to delete servers. Log if this happens, and if so, re-evaluate workarounds. Time out attempting to verify deletes after a period of time and log an error. :param log: A bound logger. :param str server_endpoint: Server endpoint URI. :param str auth_token: Keystone Auth token. :param str server_id: Opaque nova server id. :param int interval: Deletion interval in seconds - how long until verifying a delete is retried. Default: 2. :param int timeout: Seconds after which the deletion will be logged as a failure, if Nova fails to return a 404, :return: Deferred that fires when the expected status has been seen. """ del_log = log.bind(instance_id=server_id) del_log.msg('Deleting server') d = treq.delete(append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) d.addCallback(check_success, [204]) d.addErrback(wrap_request_error, server_endpoint, 'server_delete') if clock is None: # pragma: no cover from twisted.internet import reactor clock = reactor def verify(_): def check_status(): check_d = treq.head( append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) check_d.addCallback(check_success, [404]) return check_d start_time = clock.seconds() # this is treating all errors as transient, so the only error that can # occur is a CancelledError from timing out verify_d = retry_and_timeout(check_status, timeout, next_interval=repeating_interval(interval), clock=clock) def on_success(_): time_delete = clock.seconds() - start_time del_log.msg('Server deleted successfully: {time_delete} seconds.', time_delete=time_delete) verify_d.addCallback(on_success) def on_timeout(_): time_delete = clock.seconds() - start_time del_log.err(None, timeout=timeout, time_delete=time_delete, why=('Server {instance_id} failed to be deleted within ' 'a {timeout} second timeout (it has been ' '{time_delete} seconds).')) verify_d.addErrback(on_timeout) d.addCallback(verify) return d
def verified_delete(log, server_endpoint, auth_token, server_id, interval=5, timeout=120, clock=None): """ Attempt to delete a server from the server endpoint, and ensure that it is deleted by trying again until getting the server results in a 404. There is a possibility Nova sometimes fails to delete servers. Log if this happens, and if so, re-evaluate workarounds. Time out attempting to verify deletes after a period of time and log an error. :param log: A bound logger. :param str server_endpoint: Server endpoint URI. :param str auth_token: Keystone Auth token. :param str server_id: Opaque nova server id. :param int interval: Deletion interval in seconds - how long until verifying a delete is retried. Default: 2. :param int timeout: Seconds after which the deletion will be logged as a failure, if Nova fails to return a 404, :return: Deferred that fires when the expected status has been seen. """ del_log = log.bind(instance_id=server_id) del_log.msg('Deleting server') d = treq.delete(append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) d.addCallback(check_success, [204]) d.addErrback(wrap_request_error, server_endpoint, 'server_delete') if clock is None: # pragma: no cover from twisted.internet import reactor clock = reactor def verify(_): def check_status(): check_d = treq.head(append_segments(server_endpoint, 'servers', server_id), headers=headers(auth_token)) check_d.addCallback(check_success, [404]) return check_d start_time = clock.seconds() # this is treating all errors as transient, so the only error that can # occur is a CancelledError from timing out verify_d = retry_and_timeout( check_status, timeout, next_interval=repeating_interval(interval), clock=clock) def on_success(_): time_delete = clock.seconds() - start_time del_log.msg('Server deleted successfully: {time_delete} seconds.', time_delete=time_delete) verify_d.addCallback(on_success) def on_timeout(_): time_delete = clock.seconds() - start_time del_log.err( None, timeout=timeout, time_delete=time_delete, why=('Server {instance_id} failed to be deleted within ' 'a {timeout} second timeout (it has been ' '{time_delete} seconds).')) verify_d.addErrback(on_timeout) d.addCallback(verify) return d
def request(self, method, uri, data=None, name='', timeout=None, is_retry=False, suppress_error=False): """ Send a REST request to the Adtran device :param method: (string) HTTP method :param uri: (string) fully URL to perform method on :param data: (string) optional data for the request body :param name: (string) optional name of the request, useful for logging purposes :param timeout: (int) Number of seconds to wait for a response before timing out :param is_retry: (boolean) True if this method called recursively in order to recover from a connection loss. Can happen sometimes in debug sessions and in the real world. :param suppress_error: (boolean) If true, do not output ERROR message on REST request failure :return: (dict) On success with the proper results """ log.debug('request', method=method, uri=uri, data=data, retry=is_retry) if method.upper() not in self._valid_methods: raise NotImplementedError( "REST method '{}' is not supported".format(method)) url = 'http://{}:{}{}{}'.format(self._ip, self._port, '/' if uri[0] != '/' else '', uri) response = None timeout = timeout or self._timeout try: if method.upper() == 'GET': response = yield treq.get(url, auth=(self._username, self._password), timeout=timeout, headers=self.REST_GET_REQUEST_HEADER) elif method.upper() == 'POST' or method.upper() == 'PUT': response = yield treq.post( url, data=data, auth=(self._username, self._password), timeout=timeout, headers=self.REST_POST_REQUEST_HEADER) elif method.upper() == 'PATCH': response = yield treq.patch( url, data=data, auth=(self._username, self._password), timeout=timeout, headers=self.REST_PATCH_REQUEST_HEADER) elif method.upper() == 'DELETE': response = yield treq.delete( url, auth=(self._username, self._password), timeout=timeout, headers=self.REST_DELETE_REQUEST_HEADER) else: raise NotImplementedError( "REST method '{}' is not supported".format(method)) except NotImplementedError: raise except (ConnectionDone, ConnectionLost) as e: if is_retry: raise returnValue( self.request(method, uri, data=data, name=name, timeout=timeout, is_retry=True)) except ConnectionClosed: returnValue(ConnectionClosed) except Exception as e: log.exception("rest-request", method=method, url=url, name=name, e=e) raise if response.code not in self._valid_results[method.upper()]: message = "REST {} '{}' request to '{}' failed with status code {}".format( method, name, url, response.code) if not suppress_error: log.error(message) raise RestInvalidResponseCode(message, url, response.code) if response.code == self.HTTP_NO_CONTENT: returnValue(None) else: # TODO: May want to support multiple body encodings in the future headers = response.headers type_key = 'content-type' type_val = 'application/json' if not headers.hasHeader( type_key) or type_val not in headers.getRawHeaders( type_key, []): raise Exception( "REST {} '{}' request response from '{}' was not JSON", method, name, url) content = yield response.content() try: result = json.loads(content) except Exception as e: log.exception("json-decode", method=method, url=url, name=name, content=content, e=e) raise returnValue(result)
def request(self, method, uri, data=None, name='', timeout=None, is_retry=False, suppress_error=False): """ Send a REST request to the Adtran device :param method: (string) HTTP method :param uri: (string) fully URL to perform method on :param data: (string) optional data for the request body :param name: (string) optional name of the request, useful for logging purposes :param timeout: (int) Number of seconds to wait for a response before timing out :param is_retry: (boolean) True if this method called recursively in order to recover from a connection loss. Can happen sometimes in debug sessions and in the real world. :return: (dict) On success with the proper results """ log.debug('request', method=method, uri=uri, data=data, retry=is_retry) if method.upper() not in self._valid_methods: raise NotImplementedError("REST method '{}' is not supported".format(method)) url = 'http://{}:{}{}{}'.format(self._ip, self._port, '/' if uri[0] != '/' else '', uri) response = None timeout = timeout or self._timeout try: if method.upper() == 'GET': response = yield treq.get(url, auth=(self._username, self._password), timeout=timeout, headers=self.REST_GET_REQUEST_HEADER) elif method.upper() == 'POST' or method.upper() == 'PUT': response = yield treq.post(url, data=data, auth=(self._username, self._password), timeout=timeout, headers=self.REST_POST_REQUEST_HEADER) elif method.upper() == 'PATCH': response = yield treq.patch(url, data=data, auth=(self._username, self._password), timeout=timeout, headers=self.REST_PATCH_REQUEST_HEADER) elif method.upper() == 'DELETE': response = yield treq.delete(url, auth=(self._username, self._password), timeout=timeout, headers=self.REST_DELETE_REQUEST_HEADER) else: raise NotImplementedError("REST method '{}' is not supported".format(method)) except NotImplementedError: raise except (ConnectionDone, ConnectionLost) as e: if is_retry: raise returnValue(self.request(method, uri, data=data, name=name, timeout=timeout, is_retry=True)) except ConnectionClosed: returnValue(ConnectionClosed) except Exception as e: log.exception("rest-request", method=method, url=url, name=name, e=e) raise if response.code not in self._valid_results[method.upper()]: message = "REST {} '{}' request to '{}' failed with status code {}".format(method, name, url, response.code) if not suppress_error: log.error(message) raise RestInvalidResponseCode(message, url, response.code) if response.code == self.HTTP_NO_CONTENT: returnValue(None) else: # TODO: May want to support multiple body encodings in the future headers = response.headers type_key = 'content-type' type_val = 'application/json' if not headers.hasHeader(type_key) or type_val not in headers.getRawHeaders(type_key, []): raise Exception("REST {} '{}' request response from '{}' was not JSON", method, name, url) content = yield response.content() try: result = json.loads(content) except Exception as e: log.exception("json-decode", method=method, url=url, name=name, content=content, e=e) raise returnValue(result)