Exemple #1
0
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!")
Exemple #2
0
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")
Exemple #3
0
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)
Exemple #4
0
 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])
Exemple #5
0
 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()
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
 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)
Exemple #9
0
    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
Exemple #10
0
    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
Exemple #11
0
    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
Exemple #12
0
    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)
Exemple #13
0
    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)
Exemple #14
0
 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")
Exemple #15
0
    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
Exemple #16
0
    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
Exemple #18
0
    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)
Exemple #19
0
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
Exemple #20
0
    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
Exemple #21
0
    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
Exemple #22
0
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!")
Exemple #23
0
 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])
Exemple #24
0
 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
Exemple #26
0
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!")
Exemple #27
0
 def clean(response):
     if response.code == 200:
         print('deleting and recreating index')
         return treq.delete('http://localhost:9200/history')
Exemple #28
0
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
Exemple #30
0
 def delete(self, url):
     return treq.delete("%s%s" % (self.url, url), persistent=False)
Exemple #31
0
    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
Exemple #32
0
def cb_delete(url):
    d = treq.delete(url)
    d.addCallbacks(print_response, log.err)

    return d
Exemple #33
0
 def delete_notification(_):
     notification_url = http.append_segments(self._endpoint, "notifications", notification_id)
     return treq.delete(notification_url, headers=http.headers(self._auth_token))
Exemple #34
0
 def delete_notification(_):
     notification_url = http.append_segments(self._endpoint,
                                             'notifications',
                                             notification_id)
     return treq.delete(notification_url,
                        headers=http.headers(self._auth_token))
Exemple #35
0
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
Exemple #36
0
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
Exemple #37
0
    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)