def wait_for_status(session, resource, status, failures, interval, wait): """Wait for the resource to be in a particular status. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param resource: The resource to wait on to reach the status. The resource must have a status attribute. :type resource: :class:`~openstack.resource.Resource` :param status: Desired status of the resource. :param list failures: Statuses that would indicate the transition failed such as 'ERROR'. Defaults to ['ERROR']. :param interval: Number of seconds to wait between checks. :param wait: Maximum number of seconds to wait for transition. :return: Method returns self on success. :raises: :class:`~openstack.exceptions.ResourceTimeout` transition to status failed to occur in wait seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` resource transitioned to one of the failure states. :raises: :class:`~AttributeError` if the resource does not have a status attribute """ if resource.status == status: return resource if failures is None: failures = ['ERROR'] failures = [f.lower() for f in failures] name = "{res}:{id}".format(res=resource.__class__.__name__, id=resource.id) msg = "Timeout waiting for {name} to transition to {status}".format( name=name, status=status) for count in utils.iterate_timeout( timeout=wait, message=msg, wait=interval): resource = resource.get(session) new_status = resource.status if not resource: raise exceptions.ResourceFailure( "{name} went away while waiting for {status}".format( name=name, status=status)) if new_status.lower() == status.lower(): return resource if resource.status.lower() in failures: raise exceptions.ResourceFailure( "{name} transitioned to failure state {status}".format( name=name, status=resource.status))
def create(self, session, prepend_key=True): """Create a remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource creation request. Default to True. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_create` is not set to ``True``. """ if not self.allow_create: raise exceptions.MethodNotSupported(self, "create") session = self._get_session(session) if self.create_method == 'PUT': request = self._prepare_request(requires_id=True, prepend_key=prepend_key) response = session.put(request.url, json=request.body, headers=request.headers) elif self.create_method == 'POST': request = self._prepare_request(requires_id=False, prepend_key=prepend_key) response = session.post(request.url, json=request.body, headers=request.headers) else: raise exceptions.ResourceFailure(msg="Invalid create method: %s" % self.create_method) self._translate_response(response) return self
def create(self, session, prepend_key=True, requires_id=True, endpoint_override=None, headers=None): if not self.allow_create: raise exceptions.MethodNotSupported(self, "create") session = self._get_session(session) if self.create_method == 'PUT': request = self._prepare_request(requires_id=True, prepend_key=prepend_key) req_args = self._prepare_override_args( endpoint_override=endpoint_override, request_headers=request.headers, additional_headers=headers) response = session.put(request.url, json=request.body, **req_args) elif self.create_method == 'POST': request = self._prepare_request(requires_id=False, prepend_key=prepend_key) req_args = self._prepare_override_args( endpoint_override=endpoint_override, request_headers=request.headers, additional_headers=headers) response = session.post(request.url, json=request.body, **req_args) else: raise exceptions.ResourceFailure( msg="Invalid create method: %s" % self.create_method) # This is an only difference to the existing sdk_resource.create self._translate_response(response, has_body=False) return self
def wait(self, session, timeout=None, ignore_error=False): """Wait for the allocation to become active. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param timeout: How much (in seconds) to wait for the allocation. The value of ``None`` (the default) means no client-side timeout. :param ignore_error: If ``True``, this call will raise an exception if the allocation reaches the ``error`` state. Otherwise the error state is considered successful and the call returns. :return: This :class:`Allocation` instance. :raises: :class:`~openstack.exceptions.ResourceFailure` if allocation fails and ``ignore_error`` is ``False``. :raises: :class:`~openstack.exceptions.ResourceTimeout` on timeout. """ if self.state == 'active': return self for count in utils.iterate_timeout( timeout, "Timeout waiting for the allocation %s" % self.id): self.fetch(session) if self.state == 'error' and not ignore_error: raise exceptions.ResourceFailure( "Allocation %(allocation)s failed: %(error)s" % {'allocation': self.id, 'error': self.last_error}) elif self.state != 'allocating': return self session.log.debug( 'Still waiting for the allocation %(allocation)s ' 'to become active, the current state is %(state)s', {'allocation': self.id, 'state': self.state})
def create(self, session, prepend_key=True, base_path=None, **params): """Create a remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource creation request. Default to True. :param str base_path: Base part of the URI for creating resources, if different from :data:`~openstack.resource.Resource.base_path`. :param dict params: Additional params to pass. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_create` is not set to ``True``. """ if not self.allow_create: raise exceptions.MethodNotSupported(self, "create") session = self._get_session(session) microversion = self._get_microversion_for(session, 'create') requires_id = (self.create_requires_id if self.create_requires_id is not None else self.create_method == 'PUT') if self.create_exclude_id_from_body: self._body._dirty.discard("id") if self.create_method == 'POST': request = self._prepare_request(requires_id=requires_id, prepend_key=prepend_key, base_path=base_path) # NOTE(gtema) this is a funny example of when attribute # is called "incremental" on create, "is_incremental" on get # and use of "alias" or "aka" is not working for such conflict, # since our preferred attr name is exactly "is_incremental" body = request.body if 'is_incremental' in body['backup']: body['backup']['incremental'] = \ body['backup'].pop('is_incremental') response = session.post(request.url, json=request.body, headers=request.headers, microversion=microversion, params=params) else: # Just for safety of the implementation (since PUT removed) raise exceptions.ResourceFailure(msg="Invalid create method: %s" % self.create_method) has_body = (self.has_body if self.create_returns_body is None else self.create_returns_body) self.microversion = microversion self._translate_response(response, has_body=has_body) # direct comparision to False since we need to rule out None if self.has_body and self.create_returns_body is False: # fetch the body if it's required but not returned by create return self.fetch(session) return self
def _check_state_reached(self, session, expected_state, abort_on_failed_state=True): """Wait for the node to reach the expected state. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param expected_state: The expected provisioning state to reach. :param abort_on_failed_state: If ``True`` (the default), abort waiting if the node reaches a failure state which does not match the expected one. Note that the failure state for ``enroll`` -> ``manageable`` transition is ``enroll`` again. :return: ``True`` if the target state is reached :raises: :class:`~openstack.exceptions.ResourceFailure` if the node reaches an error state and ``abort_on_failed_state`` is ``True``. """ # NOTE(dtantsur): microversion 1.2 changed None to available if (self.provision_state == expected_state or (expected_state == 'available' and self.provision_state is None)): return True elif not abort_on_failed_state: return False if (self.provision_state.endswith(' failed') or self.provision_state == 'error'): raise exceptions.ResourceFailure( "Node %(node)s reached failure state \"%(state)s\"; " "the last error is %(error)s" % { 'node': self.id, 'state': self.provision_state, 'error': self.last_error }) # Special case: a failure state for "manage" transition can be # "enroll" elif (expected_state == 'manageable' and self.provision_state == 'enroll' and self.last_error): raise exceptions.ResourceFailure( "Node %(node)s could not reach state manageable: " "failed to verify management credentials; " "the last error is %(error)s" % { 'node': self.id, 'error': self.last_error })
def _check_state(self, ignore_error): if self.state == 'error' and not ignore_error: raise exceptions.ResourceFailure( "Introspection of node %(node)s failed: %(error)s" % { 'node': self.id, 'error': self.error }) else: return self.is_finished
def update_no_id(self, session, prepend_key=True, has_body=True, endpoint_override=None, headers=None): """Update the remote resource based on this instance. Method is required for resources without ID (single resource at endpoint) :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource update request. Default to True. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_update` is not set to ``True``. """ # Only try to update if we actually have anything to update. if not any([self._body.dirty, self._header.dirty]): return self if not self.allow_update: raise exceptions.MethodNotSupported(self, "update") request = self._prepare_request(requires_id=False, prepend_key=prepend_key) session = self._get_session(session) update_args = self._prepare_override_args( endpoint_override=endpoint_override, request_headers=request.headers, additional_headers=headers) if self.commit_method == 'PATCH': response = session.patch(request.url, json=request.body, **update_args) elif self.commit_method == 'POST': response = session.post(request.url, json=request.body, **update_args) elif self.commit_method == 'PUT': response = session.put(request.url, json=request.body, **update_args) else: raise exceptions.ResourceFailure(msg="Invalid update method: %s" % self.commit_method) self._translate_response(response, has_body=has_body) return self
def _fake_get(_self, uuid): result = mock.Mock() result.id = uuid if uuid == '1': result._check_state_reached.return_value = True elif uuid == '2': result._check_state_reached.side_effect = \ exceptions.ResourceFailure("boom") else: result._check_state_reached.return_value = False return result
def wait_for_status(session, resource, status, failures=[], interval=5, wait=120): """Wait for the resource to be in a particular status. :param session: The session to use for making this request. :type session: :class:`~openstack.session.Session` :param resource: The resource to wait on to reach the status. The resource must have a status attribute. :type resource: :class:`~openstack.resource.Resource` :param status: Desired status of the resource. :param list failures: Statuses that would indicate the transition failed such as 'ERROR'. :param interval: Number of seconds to wait between checks. :param wait: Maximum number of seconds to wait for transition. :return: Method returns self on success. :raises: :class:`~openstack.exceptions.ResourceTimeout` transition to status failed to occur in wait seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` resource transitioned to one of the failure states. :raises: :class:`~AttributeError` if the resource does not have a status attribute """ if resource.status == status: return resource total_sleep = 0 if failures is None: failures = [] while total_sleep < wait: resource.get(session) if resource.status == status: return resource if resource.status in failures: msg = ("Resource %s transitioned to failure state %s" % (resource.id, resource.status)) raise exceptions.ResourceFailure(msg) time.sleep(interval) total_sleep += interval msg = "Timeout waiting for %s to transition to %s" % (resource.id, status) raise exceptions.ResourceTimeout(msg)
def update(self, session, prepend_key=True, has_body=True): """Update the remote resource based on this instance. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param prepend_key: A boolean indicating whether the resource_key should be prepended in a resource update request. Default to True. :return: This :class:`Resource` instance. :raises: :exc:`~openstack.exceptions.MethodNotSupported` if :data:`Resource.allow_update` is not set to ``True``. """ # The id cannot be dirty for an update self._body._dirty.discard("id") # Only try to update if we actually have anything to update. if not any([self._body.dirty, self._header.dirty]): return self if not self.allow_update: raise exceptions.MethodNotSupported(self, "update") request = self._prepare_request(prepend_key=prepend_key) session = self._get_session(session) if self.update_method == 'PATCH': response = session.patch(request.url, json=request.body, headers=request.headers) elif self.update_method == 'POST': response = session.post(request.url, json=request.body, headers=request.headers) elif self.update_method == 'PUT': response = session.put(request.url, json=request.body, headers=request.headers) else: raise exceptions.ResourceFailure(msg="Invalid update method: %s" % self.update_method) self._translate_response(response, has_body=has_body) return self
def wait_for_status(self, session, status='ACTIVE', failures=None, interval=5, wait=120): """Wait for the server to be in some status. :param session: The session to use for making this request. :type session: :class:`~openstack.session.Session` :param status: Desired status of the server. :param list failures: Statuses that would indicate the transition failed such as 'ERROR'. :param interval: Number of seconds to wait between checks. :param wait: Maximum number of seconds to wait for transition. :return: Method returns self on success. :raises: :class:`~openstack.exceptions.ResourceTimeout` transition to status failed to occur in wait seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` resource transitioned to one of the failure states. """ try: if self.status == status: return self except AttributeError: pass total_sleep = 0 if failures is None: failures = [] while total_sleep < wait: self.get(session) if self.status == status: return self if self.status in failures: msg = ("Resource %s transitioned to failure state %s" % (self.id, self.status)) raise exceptions.ResourceFailure(msg) time.sleep(interval) total_sleep += interval msg = "Timeout waiting for %s to transition to %s" % (self.id, status) raise exceptions.ResourceTimeout(msg)
def lb_wait_for_status(cls, lb, status, failures, interval=1, wait=120): """Wait for load balancer to be in a particular provisioning status. :param lb: The load balancer to wait on to reach the status. :type lb: :class:`~openstack.load_blanacer.v2.load_balancer :param status: Desired status of the resource. :param list failures: Statuses that would indicate the transition failed such as 'ERROR'. :param interval: Number of seconds to wait between checks. :param wait: Maximum number of seconds to wait for transition. Note, most actions should easily finish in 120 seconds, but for load balancer create slow hosts can take up to ten minutes for nova to fully boot a VM. :return: None :raises: :class:`~openstack.exceptions.ResourceTimeout` transition to status failed to occur in wait seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` resource transitioned to one of the failure states. """ total_sleep = 0 if failures is None: failures = [] while total_sleep < wait: lb = cls.conn.load_balancer.get_load_balancer(lb.id) if lb.provisioning_status == status: return None if lb.provisioning_status in failures: msg = ("Load Balancer %s transitioned to failure state %s" % (lb.id, lb.provisioning_status)) raise exceptions.ResourceFailure(msg) time.sleep(interval) total_sleep += interval msg = "Timeout waiting for Load Balancer %s to transition to %s" % ( lb.id, status) raise exceptions.ResourceTimeout(msg)
def wait_for_task(self, task, status='success', failures=None, interval=2, wait=120): """Wait for a task to be in a particular status. :param task: The resource to wait on to reach the specified status. The resource must have a ``status`` attribute. :type resource: A :class:`~openstack.resource.Resource` object. :param status: Desired status. :param failures: Statuses that would be interpreted as failures. :type failures: :py:class:`list` :param interval: Number of seconds to wait before to consecutive checks. Default to 2. :param wait: Maximum number of seconds to wait before the change. Default to 120. :returns: The resource is returned on success. :raises: :class:`~openstack.exceptions.ResourceTimeout` if transition to the desired status failed to occur in specified seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` if the resource has transited to one of the failure statuses. :raises: :class:`~AttributeError` if the resource does not have a ``status`` attribute. """ if failures is None: failures = ['failure'] else: failures = [f.lower() for f in failures] if task.status.lower() == status.lower(): return task name = "{res}:{id}".format(res=task.__class__.__name__, id=task.id) msg = "Timeout waiting for {name} to transition to {status}".format( name=name, status=status) for count in utils.iterate_timeout(timeout=wait, message=msg, wait=interval): task = task.fetch(self) if not task: raise exceptions.ResourceFailure( "{name} went away while waiting for {status}".format( name=name, status=status)) new_status = task.status normalized_status = new_status.lower() if normalized_status == status.lower(): return task elif normalized_status in failures: if task.message == _IMAGE_ERROR_396: task_args = dict(input=task.input, type=task.type) task = self.create_task(**task_args) self.log.debug('Got error 396. Recreating task %s' % task) else: raise exceptions.ResourceFailure( "{name} transitioned to failure state {status}".format( name=name, status=new_status)) self.log.debug( 'Still waiting for resource %s to reach state %s, ' 'current state is %s', name, status, new_status)
def wait_for_status(session, resource, status, failures, interval=None, wait=None, attribute='status'): """Wait for the resource to be in a particular status. :param session: The session to use for making this request. :type session: :class:`~keystoneauth1.adapter.Adapter` :param resource: The resource to wait on to reach the status. The resource must have a status attribute specified via ``attribute``. :type resource: :class:`~openstack.resource.Resource` :param status: Desired status of the resource. :param list failures: Statuses that would indicate the transition failed such as 'ERROR'. Defaults to ['ERROR']. :param interval: Number of seconds to wait between checks. Set to ``None`` to use the default interval. :param wait: Maximum number of seconds to wait for transition. Set to ``None`` to wait forever. :param attribute: Name of the resource attribute that contains the status. :return: The updated resource. :raises: :class:`~openstack.exceptions.ResourceTimeout` transition to status failed to occur in wait seconds. :raises: :class:`~openstack.exceptions.ResourceFailure` resource transitioned to one of the failure states. :raises: :class:`~AttributeError` if the resource does not have a status attribute """ log = _log.setup_logging(__name__) current_status = getattr(resource, attribute) if _normalize_status(current_status) == status.lower(): return resource if failures is None: failures = ['ERROR'] failures = [f.lower() for f in failures] name = "{res}:{id}".format(res=resource.__class__.__name__, id=resource.id) msg = "Timeout waiting for {name} to transition to {status}".format( name=name, status=status) for count in utils.iterate_timeout(timeout=wait, message=msg, wait=interval): resource = resource.get(session) if not resource: raise exceptions.ResourceFailure( "{name} went away while waiting for {status}".format( name=name, status=status)) new_status = getattr(resource, attribute) normalized_status = _normalize_status(new_status) if normalized_status == status.lower(): return resource elif normalized_status in failures: raise exceptions.ResourceFailure( "{name} transitioned to failure state {status}".format( name=name, status=new_status)) log.debug( 'Still waiting for resource %s to reach state %s, ' 'current state is %s', name, status, new_status)