def consume_in_thread(self): """Runs the ZmqProxy service.""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) try: os.makedirs(ipc_dir) except os.error: if not os.path.isdir(ipc_dir): with excutils.save_and_reraise_exception(): LOG.error(_("Required IPC directory does not exist at" " %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL) except zmq.ZMQError: if os.access(ipc_dir, os.X_OK): with excutils.save_and_reraise_exception(): LOG.error(_("Permission denied to IPC directory at" " %s") % (ipc_dir, )) with excutils.save_and_reraise_exception(): LOG.error(_("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def consume_in_thread(self): """Runs the ZmqProxy service""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) if not os.path.isdir(ipc_dir): try: utils.execute('mkdir', '-p', ipc_dir, run_as_root=True) utils.execute('chown', "%s:%s" % (os.getuid(), os.getgid()), ipc_dir, run_as_root=True) utils.execute('chmod', '750', ipc_dir, run_as_root=True) except utils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error( _("Could not create IPC directory %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL, out_bind=True) except zmq.ZMQError: with excutils.save_and_reraise_exception(): LOG.error( _("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def _action_recorder(self, action, expected_exceptions=tuple()): '''Return a context manager to record the progress of an action. Upon entering the context manager, the state is set to IN_PROGRESS. Upon exiting, the state will be set to COMPLETE if no exception was raised, or FAILED otherwise. Non-exit exceptions will be translated to ResourceFailure exceptions. Expected exceptions are re-raised, with the Resource left in the IN_PROGRESS state. ''' try: self.state_set(action, self.IN_PROGRESS) yield except expected_exceptions as ex: with excutils.save_and_reraise_exception(): LOG.debug('%s', six.text_type(ex)) except Exception as ex: LOG.info('%(action)s: %(info)s', { "action": action, "info": str(self) }, exc_info=True) failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure except: # noqa with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, '%s aborted' % action) except Exception: LOG.exception(_('Error marking resource as failed')) else: self.state_set(action, self.COMPLETE)
def consume_in_thread(self): """Runs the ZmqProxy service.""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) try: os.makedirs(ipc_dir) except os.error: if not os.path.isdir(ipc_dir): with excutils.save_and_reraise_exception(): LOG.error( _("Required IPC directory does not exist at" " %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL) except zmq.ZMQError: if os.access(ipc_dir, os.X_OK): with excutils.save_and_reraise_exception(): LOG.error( _("Permission denied to IPC directory at" " %s") % (ipc_dir, )) with excutils.save_and_reraise_exception(): LOG.error( _("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def _action_recorder(self, action, expected_exceptions=tuple()): """Return a context manager to record the progress of an action. Upon entering the context manager, the state is set to IN_PROGRESS. Upon exiting, the state will be set to COMPLETE if no exception was raised, or FAILED otherwise. Non-exit exceptions will be translated to ResourceFailure exceptions. Expected exceptions are re-raised, with the Resource left in the IN_PROGRESS state. """ try: self.state_set(action, self.IN_PROGRESS) yield except expected_exceptions as ex: with excutils.save_and_reraise_exception(): LOG.debug("%s", six.text_type(ex)) except Exception as ex: LOG.exception("%s: %s" % (action, str(self))) # noqa failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure except: # noqa with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, "%s aborted" % action) except Exception: LOG.exception(_("Error marking resource as failed")) else: self.state_set(action, self.COMPLETE)
def create(self): ''' Create the resource. Subclasses should provide a handle_create() method to customise creation. ''' assert self.state is None, 'Resource create requested in invalid state' logger.info('creating %s' % str(self)) # Re-resolve the template, since if the resource Ref's # the AWS::StackId pseudo parameter, it will change after # the parser.Stack is stored (which is after the resources # are __init__'d, but before they are create()'d) self.t = self.stack.resolve_static_data(self.json_snippet) self.properties = Properties(self.properties_schema, self.t.get('Properties', {}), self.stack.resolve_runtime_data, self.name) try: self.properties.validate() self.state_set(self.CREATE_IN_PROGRESS) create_data = None if callable(getattr(self, 'handle_create', None)): create_data = self.handle_create() yield while not self.check_create_complete(create_data): yield except greenlet.GreenletExit: # Older versions of greenlet erroneously had GreenletExit inherit # from Exception instead of BaseException with excutils.save_and_reraise_exception(): try: self.state_set(self.CREATE_FAILED, 'Creation aborted') except Exception: logger.exception('Error marking resource as failed') except Exception as ex: logger.exception('create %s', str(self)) failure = exception.ResourceFailure(ex) self.state_set(self.CREATE_FAILED, str(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(self.CREATE_FAILED, 'Creation aborted') except Exception: logger.exception('Error marking resource as failed') else: self.state_set(self.CREATE_COMPLETE)
def handle_create(self): """Allocate a floating IP for the current tenant.""" ips = None if self.properties[self.DOMAIN]: from heat.engine.resources.internet_gateway import InternetGateway ext_net = InternetGateway.get_external_network_id(self.neutron()) props = {'floating_network_id': ext_net} ips = self.neutron().create_floatingip({ 'floatingip': props})['floatingip'] self.ipaddress = ips['floating_ip_address'] self.resource_id_set(ips['id']) LOG.info(_('ElasticIp create %s') % str(ips)) else: try: ips = self.nova().floating_ips.create() except Exception as e: with excutils.save_and_reraise_exception(): if self.client_plugin('nova').is_not_found(e): msg = _("No default floating IP pool configured. " "Set 'default_floating_pool' in nova.conf.") LOG.error(msg) if ips: self.ipaddress = ips.ip self.resource_id_set(ips.id) LOG.info(_('ElasticIp create %s') % str(ips)) instance_id = self.properties[self.INSTANCE_ID] if instance_id: server = self.nova().servers.get(instance_id) server.add_floating_ip(self._ipaddress())
def handle_create(self): """Allocate a floating IP for the current tenant.""" ips = None if self.properties[self.DOMAIN]: from heat.engine.resources.internet_gateway import InternetGateway ext_net = InternetGateway.get_external_network_id(self.neutron()) props = {'floating_network_id': ext_net} ips = self.neutron().create_floatingip({'floatingip': props})['floatingip'] self.ipaddress = ips['floating_ip_address'] self.resource_id_set(ips['id']) LOG.info(_('ElasticIp create %s') % str(ips)) else: try: ips = self.nova().floating_ips.create() except Exception as e: with excutils.save_and_reraise_exception(): if self.client_plugin('nova').is_not_found(e): msg = _("No default floating IP pool configured. " "Set 'default_floating_pool' in nova.conf.") LOG.error(msg) if ips: self.ipaddress = ips.ip self.resource_id_set(ips.id) LOG.info(_('ElasticIp create %s') % str(ips)) instance_id = self.properties[self.INSTANCE_ID] if instance_id: server = self.nova().servers.get(instance_id) server.add_floating_ip(self._ipaddress())
def __call__(self): """Return a co-routine which runs the task group.""" raised_exceptions = [] while any(self._runners.itervalues()): try: for k, r in self._ready(): r.start() yield for k, r in self._running(): if r.step(): del self._graph[k] except Exception: exc_info = sys.exc_info() if self.aggregate_exceptions: self._cancel_recursively(k, r) else: self.cancel_all(grace_period=self.error_wait_time) raised_exceptions.append(exc_info) except: # noqa with excutils.save_and_reraise_exception(): self.cancel_all() if raised_exceptions: if self.aggregate_exceptions: raise ExceptionGroup(v for t, v, tb in raised_exceptions) else: exc_type, exc_val, traceback = raised_exceptions[0] raise exc_type, exc_val, traceback
def _start_thread_with_lock(self, cnxt, stack, func, *args): """ Try to acquire a stack lock and, if successful, run the method in a sub-thread. :param cnxt: RPC context :param stack: Stack to be operated on :type stack: heat.engine.parser.Stack :param func: Callable to be invoked in sub-thread :type func: function or instancemethod :param args: Args to be passed to func """ lock = stack_lock.StackLock(cnxt, stack) def release(gt, *args, **kwargs): """ Callback function that will be passed to GreenThread.link(). """ lock.release() lock.acquire() try: th = self._start_in_thread(stack.id, func, *args) th.link(release) except: with excutils.save_and_reraise_exception(): lock.release()
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY): """ Adjust the size of the scaling group if the cooldown permits. """ if self._cooldown_inprogress(): LOG.info( _("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s") % { 'name': self.name, 'cooldown': self.properties[self.COOLDOWN] }) return capacity = len(self.get_instances()) lower = self.properties[self.MIN_SIZE] upper = self.properties[self.MAX_SIZE] new_capacity = _calculate_new_capacity(capacity, adjustment, adjustment_type, lower, upper) if new_capacity == capacity: LOG.debug('no change in capacity %d' % capacity) return # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId() }, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({ 'suffix': 'error', 'message': six.text_type(resize_ex), }) notification.send(**notif) except Exception: LOG.exception(_('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname'] }, }) notification.send(**notif) self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
def delete(self): """ Delete the resource. Subclasses should provide a handle_delete() method to customise deletion. """ if self.state == self.DELETE_COMPLETE: return if self.state == self.DELETE_IN_PROGRESS: raise exception.Error("Resource deletion already in progress") # No need to delete if the resource has never been created if self.state is None: return logger.info("deleting %s" % str(self)) try: self.state_set(self.DELETE_IN_PROGRESS) if callable(getattr(self, "handle_delete", None)): self.handle_delete() except Exception as ex: logger.exception("Delete %s", str(self)) failure = exception.ResourceFailure(ex) self.state_set(self.DELETE_FAILED, str(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(self.DELETE_FAILED, "Deletion aborted") except Exception: logger.exception("Error marking resource deletion failed") else: self.state_set(self.DELETE_COMPLETE)
def handle_create(self): """Allocate a floating IP for the current tenant.""" ips = None if self.properties['Domain'] and clients.neutronclient: from heat.engine.resources.internet_gateway import InternetGateway ext_net = InternetGateway.get_external_network_id(self.neutron()) props = {'floating_network_id': ext_net} ips = self.neutron().create_floatingip({ 'floatingip': props})['floatingip'] self.ipaddress = ips['floating_ip_address'] self.resource_id_set(ips['id']) logger.info('ElasticIp create %s' % str(ips)) else: if self.properties['Domain']: raise exception.Error('Domain property can not be set on ' 'resource %s without Neutron available' % self.name) try: ips = self.nova().floating_ips.create() except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): msg = ("No default floating IP pool configured." "Set 'default_floating_pool' in nova.conf.") logger.error(msg) if ips: self.ipaddress = ips.ip self.resource_id_set(ips.id) logger.info('ElasticIp create %s' % str(ips)) if self.properties['InstanceId']: server = self.nova().servers.get(self.properties['InstanceId']) res = server.add_floating_ip(self._ipaddress())
def start_with_acquired_lock(self, stack, lock, func, *args, **kwargs): """ Run the given method in a sub-thread and release the provided lock when the thread finishes. :param stack: Stack to be operated on :type stack: heat.engine.parser.Stack :param lock: The acquired stack lock :type lock: heat.engine.stack_lock.StackLock :param func: Callable to be invoked in sub-thread :type func: function or instancemethod :param args: Args to be passed to func :param kwargs: Keyword-args to be passed to func """ def release(gt, *args): """ Callback function that will be passed to GreenThread.link(). """ lock.release(*args) try: th = self.start(stack.id, func, *args, **kwargs) th.link(release, stack.id) except: with excutils.save_and_reraise_exception(): lock.release(stack.id)
def handle_create(self): """Allocate a floating IP for the current tenant.""" ips = None if self.properties[self.DOMAIN] and clients.neutronclient: from heat.engine.resources.internet_gateway import InternetGateway ext_net = InternetGateway.get_external_network_id(self.neutron()) props = {'floating_network_id': ext_net} ips = self.neutron().create_floatingip({'floatingip': props})['floatingip'] self.ipaddress = ips['floating_ip_address'] self.resource_id_set(ips['id']) logger.info(_('ElasticIp create %s') % str(ips)) else: if self.properties[self.DOMAIN]: raise exception.Error( _('Domain property can not be set on ' 'resource %s without Neutron available') % self.name) try: ips = self.nova().floating_ips.create() except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): msg = _("No default floating IP pool configured. " "Set 'default_floating_pool' in nova.conf.") logger.error(msg) if ips: self.ipaddress = ips.ip self.resource_id_set(ips.id) logger.info(_('ElasticIp create %s') % str(ips)) instance_id = self.properties[self.INSTANCE_ID] if instance_id: server = self.nova().servers.get(instance_id) server.add_floating_ip(self._ipaddress())
def __call__(self): """Return a co-routine which runs the task group.""" raised_exceptions = [] try: while any(self._runners.itervalues()): try: for k, r in self._ready(): r.start() yield for k, r in self._running(): if r.step(): del self._graph[k] except Exception as e: self._cancel_recursively(k, r) if not self.aggregate_exceptions: raise raised_exceptions.append(e) except: with excutils.save_and_reraise_exception(): for r in self._runners.itervalues(): r.cancel() if raised_exceptions: raise ExceptionGroup(raised_exceptions)
def abandon_stack(self, cnxt, stack_identity): """ The abandon_stack method abandons a given stack. :param cnxt: RPC context. :param stack_identity: Name of the stack you want to abandon. """ st = self._get_stack(cnxt, stack_identity) logger.info(_('abandoning stack %s') % st.name) stack = parser.Stack.load(cnxt, stack=st) lock = stack_lock.StackLock(cnxt, stack, self.engine_id) acquire_result = lock.try_acquire() # If an action is in progress, 'try_acquire' returns engine UUID. If # the returned engine is alive, then throw ActionInProgress exception if (acquire_result and (acquire_result == self.engine_id or stack_lock.StackLock.engine_alive(cnxt, acquire_result))): raise exception.ActionInProgress(stack_name=stack.name, action=stack.action) try: # Get stack details before deleting it. stack_info = stack.get_abandon_data() # Set deletion policy to 'Retain' for all resources in the stack. stack.set_deletion_policy(resource.RETAIN) except: with excutils.save_and_reraise_exception(): lock.release(stack.id) self.thread_group_mgr.start_with_acquired_lock(stack, lock, stack.delete) return stack_info
def abandon_stack(self, cnxt, stack_identity): """ The abandon_stack method abandons a given stack. :param cnxt: RPC context. :param stack_identity: Name of the stack you want to abandon. """ st = self._get_stack(cnxt, stack_identity) logger.info(_('abandoning stack %s') % st.name) stack = parser.Stack.load(cnxt, stack=st) lock = stack_lock.StackLock(cnxt, stack, self.engine_id) acquire_result = lock.try_acquire() # If an action is in progress, 'try_acquire' returns engine UUID. If # the returned engine is alive, then throw ActionInProgress exception if (acquire_result and (acquire_result == self.engine_id or stack_lock.StackLock.engine_alive(cnxt, acquire_result))): raise exception.ActionInProgress(stack_name=stack.name, action=stack.action) try: # Get stack details before deleting it. stack_info = stack.prepare_abandon() except: with excutils.save_and_reraise_exception(): lock.release(stack.id) self.thread_group_mgr.start_with_acquired_lock(stack, lock, stack.delete) return stack_info
def handle_create(self): """Allocate a floating IP for the current tenant.""" ips = None if self.properties[self.DOMAIN] and clients.neutronclient: from heat.engine.resources.internet_gateway import InternetGateway ext_net = InternetGateway.get_external_network_id(self.neutron()) props = {"floating_network_id": ext_net} ips = self.neutron().create_floatingip({"floatingip": props})["floatingip"] self.ipaddress = ips["floating_ip_address"] self.resource_id_set(ips["id"]) LOG.info(_("ElasticIp create %s") % str(ips)) else: if self.properties[self.DOMAIN]: raise exception.Error( _("Domain property can not be set on " "resource %s without Neutron available") % self.name ) try: ips = self.nova().floating_ips.create() except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): msg = _("No default floating IP pool configured. " "Set 'default_floating_pool' in nova.conf.") LOG.error(msg) if ips: self.ipaddress = ips.ip self.resource_id_set(ips.id) LOG.info(_("ElasticIp create %s") % str(ips)) instance_id = self.properties[self.INSTANCE_ID] if instance_id: server = self.nova().servers.get(instance_id) server.add_floating_ip(self._ipaddress())
def __call__(self): """Return a co-routine which runs the task group.""" raised_exceptions = [] try: while any(self._runners.itervalues()): try: for k, r in self._ready(): r.start() yield for k, r in self._running(): if r.step(): del self._graph[k] except Exception as e: self._cancel_recursively(k, r) if not self.aggregate_exceptions: raise raised_exceptions.append(e) except: # noqa with excutils.save_and_reraise_exception(): for r in self._runners.itervalues(): r.cancel() if raised_exceptions: raise ExceptionGroup(raised_exceptions)
def start_with_acquired_lock(self, stack, lock, func, *args, **kwargs): """ Run the given method in a sub-thread and release the provided lock when the thread finishes. :param stack: Stack to be operated on :type stack: heat.engine.parser.Stack :param lock: The acquired stack lock :type lock: heat.engine.stack_lock.StackLock :param func: Callable to be invoked in sub-thread :type func: function or instancemethod :param args: Args to be passed to func :param kwargs: Keyword-args to be passed to func """ def release(gt, *args, **kwargs): """ Callback function that will be passed to GreenThread.link(). """ lock.release(*args) try: th = self.start(stack.id, func, *args) th.link(release, stack.id) except: with excutils.save_and_reraise_exception(): lock.release(stack.id)
def update(self, after, before=None, prev_resource=None): ''' update the resource. Subclasses should provide a handle_update() method to customise update, the base-class handle_update will fail by default. ''' action = self.UPDATE (cur_class_def, cur_ver) = self.implementation_signature() prev_ver = cur_ver if prev_resource is not None: (prev_class_def, prev_ver) = prev_resource.implementation_signature() if prev_class_def != cur_class_def: raise UpdateReplace(self.name) if before is None: before = self.parsed_template() if prev_ver == cur_ver and before == after: return if (self.action, self.status) in ((self.CREATE, self.IN_PROGRESS), (self.UPDATE, self.IN_PROGRESS), (self.ADOPT, self.IN_PROGRESS)): exc = Exception(_('Resource update already requested')) raise exception.ResourceFailure(exc, self, action) logger.info('updating %s' % str(self)) try: self.updated_time = datetime.utcnow() self.state_set(action, self.IN_PROGRESS) properties = Properties(self.properties_schema, after.get('Properties', {}), self._resolve_runtime_data, self.name, self.context) properties.validate() tmpl_diff = self.update_template_diff(after, before) prop_diff = self.update_template_diff_properties(after, before) if callable(getattr(self, 'handle_update', None)): handle_data = self.handle_update(after, tmpl_diff, prop_diff) yield if callable(getattr(self, 'check_update_complete', None)): while not self.check_update_complete(handle_data): yield except UpdateReplace: with excutils.save_and_reraise_exception(): logger.debug(_("Resource %s update requires replacement") % self.name) except Exception as ex: logger.exception('update %s : %s' % (str(self), str(ex))) failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, str(failure)) raise failure else: self.json_snippet = copy.deepcopy(after) self.reparse() self.state_set(action, self.COMPLETE)
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY): """ Adjust the size of the scaling group if the cooldown permits. """ if self._cooldown_inprogress(): LOG.info(_("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s") % {'name': self.name, 'cooldown': self.properties[self.COOLDOWN]}) return capacity = len(self.get_instances()) lower = self.properties[self.MIN_SIZE] upper = self.properties[self.MAX_SIZE] new_capacity = _calculate_new_capacity(capacity, adjustment, adjustment_type, lower, upper) if new_capacity == capacity: LOG.debug('no change in capacity %d' % capacity) return # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId()}, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({'suffix': 'error', 'message': six.text_type(resize_ex), }) notification.send(**notif) except Exception: LOG.exception(_('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname']}, }) notification.send(**notif) self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
def delete(self): ''' Delete the resource. Subclasses should provide a handle_delete() method to customise deletion. ''' action = self.DELETE if (self.action, self.status) == (self.DELETE, self.COMPLETE): return # No need to delete if the resource has never been created if self.action == self.INIT: return initial_state = self.state logger.info(_('deleting %s') % str(self)) try: self.state_set(action, self.IN_PROGRESS) if self.abandon_in_progress: deletion_policy = RETAIN else: deletion_policy = self.t.get('DeletionPolicy', DELETE) handle_data = None if deletion_policy == DELETE: if callable(getattr(self, 'handle_delete', None)): handle_data = self.handle_delete() yield elif deletion_policy == SNAPSHOT: if callable(getattr(self, 'handle_snapshot_delete', None)): handle_data = self.handle_snapshot_delete(initial_state) yield if (deletion_policy != RETAIN and callable( getattr(self, 'check_delete_complete', None))): while not self.check_delete_complete(handle_data): yield except Exception as ex: logger.exception(_('Delete %s') % str(self)) failure = exception.ResourceFailure(ex, self, self.action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, 'Deletion aborted') except Exception: logger.exception( _('Error marking resource deletion ' 'failed')) else: self.state_set(action, self.COMPLETE)
def delete(self): ''' Delete the resource. Subclasses should provide a handle_delete() method to customise deletion. ''' action = self.DELETE if (self.action, self.status) == (self.DELETE, self.COMPLETE): return # No need to delete if the resource has never been created if self.action == self.INIT: return initial_state = self.state logger.info(_('deleting %s') % str(self)) try: self.state_set(action, self.IN_PROGRESS) if self.abandon_in_progress: deletion_policy = RETAIN else: deletion_policy = self.t.get('DeletionPolicy', DELETE) handle_data = None if deletion_policy == DELETE: if callable(getattr(self, 'handle_delete', None)): handle_data = self.handle_delete() yield elif deletion_policy == SNAPSHOT: if callable(getattr(self, 'handle_snapshot_delete', None)): handle_data = self.handle_snapshot_delete(initial_state) yield if (deletion_policy != RETAIN and callable(getattr(self, 'check_delete_complete', None))): while not self.check_delete_complete(handle_data): yield except Exception as ex: logger.exception(_('Delete %s') % str(self)) failure = exception.ResourceFailure(ex, self, self.action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, 'Deletion aborted') except Exception: logger.exception(_('Error marking resource deletion ' 'failed')) else: self.state_set(action, self.COMPLETE)
def _serialize(data): """Serialization wrapper. We prefer using JSON, but it cannot encode all types. Error if a developer passes us bad data. """ try: return jsonutils.dumps(data, ensure_ascii=True) except TypeError: with excutils.save_and_reraise_exception(): LOG.error(_("JSON serialization failed."))
def remove_path_on_error(path): """Protect code that wants to operate on PATH atomically. Any exception will cause PATH to be removed. :param path: File to work with """ try: yield except Exception: with excutils.save_and_reraise_exception(): delete_if_exists(path)
def thread_lock(self, stack_id): """ Acquire a lock and release it only if there is an exception. The release method still needs to be scheduled to be run at the end of the thread using the Thread.link method. """ try: self.acquire() yield except: # noqa with excutils.save_and_reraise_exception(): self.release(stack_id)
def thread_lock(self, stack_id): """ Acquire a lock and release it only if there is an exception. The release method still needs to be scheduled to be run at the end of the thread using the Thread.link method. """ try: self.acquire() yield except: with excutils.save_and_reraise_exception(): self.release(stack_id)
def handle_create(self): try: pool = self.properties.get(self.POOL) floating_ip = self.nova().floating_ips.create(pool=pool) except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): if pool is None: msg = _('Could not allocate floating IP. Probably there ' 'is no default floating IP pool is configured.') logger.error(msg) self.resource_id_set(floating_ip.id) self._floating_ip = floating_ip
def handle_create(self): try: pool = self.properties.get(self.POOL) floating_ip = self.nova().floating_ips.create(pool=pool) except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): if pool is None: msg = _('Could not allocate floating IP. Probably there ' 'is no default floating IP pool is configured.') LOG.error(msg) self.resource_id_set(floating_ip.id) self._floating_ip = floating_ip
def remove_path_on_error(path, remove=delete_if_exists): """Protect code that wants to operate on PATH atomically. Any exception will cause PATH to be removed. :param path: File to work with :param remove: Optional function to remove passed path """ try: yield except Exception: with excutils.save_and_reraise_exception(): remove(path)
def consume_in_thread(self): """Runs the ZmqProxy service""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) if not os.path.isdir(ipc_dir): try: utils.execute("mkdir", "-p", ipc_dir, run_as_root=True) utils.execute("chown", "%s:%s" % (os.getuid(), os.getgid()), ipc_dir, run_as_root=True) utils.execute("chmod", "750", ipc_dir, run_as_root=True) except utils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Could not create IPC directory %s") % (ipc_dir,)) try: self.register(consumption_proxy, consume_in, zmq.PULL, out_bind=True) except zmq.ZMQError: with excutils.save_and_reraise_exception(): LOG.error(_("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def _do_action(self, action, pre_func=None, resource_data=None): ''' Perform a transition to a new state via a specified action action should be e.g self.CREATE, self.UPDATE etc, we set status based on this, the transition is handled by calling the corresponding handle_* and check_*_complete functions Note pre_func is an optional function reference which will be called before the handle_<action> function If the resource does not declare a check_$action_complete function, we declare COMPLETE status as soon as the handle_$action call has finished, and if no handle_$action function is declared, then we do nothing, useful e.g if the resource requires no action for a given state transition ''' assert action in self.ACTIONS, 'Invalid action %s' % action try: self.state_set(action, self.IN_PROGRESS) action_l = action.lower() handle = getattr(self, 'handle_%s' % action_l, None) check = getattr(self, 'check_%s_complete' % action_l, None) if callable(pre_func): pre_func() handle_data = None if callable(handle): handle_data = (handle(resource_data) if resource_data else handle()) yield if callable(check): while not check(handle_data): yield except Exception as ex: LOG.exception('%s : %s' % (action, str(self))) # noqa failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, '%s aborted' % action) except Exception: LOG.exception(_('Error marking resource as failed')) else: self.state_set(action, self.COMPLETE)
def _do_action(self, action, pre_func=None, resource_data=None): ''' Perform a transition to a new state via a specified action action should be e.g self.CREATE, self.UPDATE etc, we set status based on this, the transition is handled by calling the corresponding handle_* and check_*_complete functions Note pre_func is an optional function reference which will be called before the handle_<action> function If the resource does not declare a check_$action_complete function, we declare COMPLETE status as soon as the handle_$action call has finished, and if no handle_$action function is declared, then we do nothing, useful e.g if the resource requires no action for a given state transition ''' assert action in self.ACTIONS, 'Invalid action %s' % action try: self.state_set(action, self.IN_PROGRESS) action_l = action.lower() handle = getattr(self, 'handle_%s' % action_l, None) check = getattr(self, 'check_%s_complete' % action_l, None) if callable(pre_func): pre_func() handle_data = None if callable(handle): handle_data = (handle(resource_data) if resource_data else handle()) yield if callable(check): while not check(handle_data): yield except Exception as ex: logger.exception('%s : %s' % (action, str(self))) failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, str(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, '%s aborted' % action) except Exception: logger.exception(_('Error marking resource as failed')) else: self.state_set(action, self.COMPLETE)
def __call__(self): """Return a co-routine which runs the task group.""" runners = [TaskRunner(t) for t in self._tasks] try: for r in runners: r.start() while runners: yield runners = list(itertools.dropwhile(lambda r: r.step(), runners)) except: with excutils.save_and_reraise_exception(): for r in runners: r.cancel()
def try_thread_lock(self, stack_id): """ Similar to thread_lock, but acquire the lock using try_acquire and only release it upon any exception after a successful acquisition. """ result = None try: result = self.try_acquire() yield result except: if result is None: # Lock was successfully acquired with excutils.save_and_reraise_exception(): self.release(stack_id) raise
def handle_create(self): try: pool = self.properties.get(self.POOL) floating_ip = self.nova().floating_ips.create(pool=pool) except Exception as e: with excutils.save_and_reraise_exception(): if self.client_plugin().is_not_found(e): if pool is None: msg = _('Could not allocate floating IP. Probably ' 'there is no default floating IP pool is ' 'configured.') LOG.error(msg) self.resource_id_set(floating_ip.id) self._floating_ip = floating_ip
def try_thread_lock(self, stack_id): """ Similar to thread_lock, but acquire the lock using try_acquire and only release it upon any exception after a successful acquisition. """ result = None try: result = self.try_acquire() yield result except: # noqa if result is None: # Lock was successfully acquired with excutils.save_and_reraise_exception(): self.release(stack_id) raise
def delete(self): """ Delete the resource. Subclasses should provide a handle_delete() method to customise deletion. """ action = self.DELETE if (self.action, self.status) == (self.DELETE, self.COMPLETE): return # No need to delete if the resource has never been created if self.action == self.INIT: return initial_state = self.state logger.info(_("deleting %s") % str(self)) try: self.state_set(action, self.IN_PROGRESS) deletion_policy = self.t.get("DeletionPolicy", DELETE) handle_data = None if deletion_policy == DELETE: if callable(getattr(self, "handle_delete", None)): handle_data = self.handle_delete() yield elif deletion_policy == SNAPSHOT: if callable(getattr(self, "handle_snapshot_delete", None)): handle_data = self.handle_snapshot_delete(initial_state) yield if deletion_policy != RETAIN and callable(getattr(self, "check_delete_complete", None)): while not self.check_delete_complete(handle_data): yield except Exception as ex: logger.exception(_("Delete %s"), str(self)) failure = exception.ResourceFailure(ex, self, self.action) self.state_set(action, self.FAILED, str(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, "Deletion aborted") except Exception: logger.exception(_("Error marking resource deletion " "failed")) else: self.state_set(action, self.COMPLETE)
def __call__(self): """Return a co-routine which runs the task group.""" try: while any(self._runners.itervalues()): for k, r in self._ready(): r.start() yield for k, r in self._running(): if r.step(): del self._graph[k] except: with excutils.save_and_reraise_exception(): for r in self._runners.itervalues(): r.cancel()
def handle_create(self): """Allocate a floating IP for the current tenant.""" try: ips = self.nova().floating_ips.create() except clients.novaclient.exceptions.NotFound: with excutils.save_and_reraise_exception(): msg = ("No default floating IP pool configured." "Set 'default_floating_pool' in nova.conf.") logger.error(msg) if ips: logger.info('ElasticIp create %s' % str(ips)) self.ipaddress = ips.ip self.resource_id_set(ips.id) if self.properties['InstanceId']: server = self.nova().servers.get(self.properties['InstanceId']) res = server.add_floating_ip(self._ipaddress())
def __iter__(self): """Return a result until we get a 'None' response from consumer""" if self._done: raise StopIteration while True: try: self._iterator.next() except Exception: with excutils.save_and_reraise_exception(): self.done() if self._got_ending: self.done() raise StopIteration result = self._result if isinstance(result, Exception): self.done() raise result yield result
def update(self, after, before=None): """ update the resource. Subclasses should provide a handle_update() method to customise update, the base-class handle_update will fail by default. """ action = self.UPDATE if before is None: before = self.parsed_template() elif before == after: return if (self.action, self.status) in ((self.CREATE, self.IN_PROGRESS), (self.UPDATE, self.IN_PROGRESS)): exc = Exception(_("Resource update already requested")) raise exception.ResourceFailure(exc, self, action) logger.info("updating %s" % str(self)) try: self.state_set(action, self.IN_PROGRESS) properties = Properties( self.properties_schema, after.get("Properties", {}), self._resolve_runtime_data, self.name ) properties.validate() tmpl_diff = self.update_template_diff(after, before) prop_diff = self.update_template_diff_properties(after, before) if callable(getattr(self, "handle_update", None)): handle_data = self.handle_update(after, tmpl_diff, prop_diff) yield if callable(getattr(self, "check_update_complete", None)): while not self.check_update_complete(handle_data): yield except UpdateReplace: with excutils.save_and_reraise_exception(): logger.debug(_("Resource %s update requires replacement") % self.name) except Exception as ex: logger.exception("update %s : %s" % (str(self), str(ex))) failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, str(failure)) raise failure else: self.t = self.stack.resolve_static_data(after) self.state_set(action, self.COMPLETE)
def delete(self): ''' Delete the resource. Subclasses should provide a handle_delete() method to customise deletion. ''' action = self.DELETE if (self.action, self.status) == (self.DELETE, self.COMPLETE): return # No need to delete if the resource has never been created if self.action == self.INIT: return initial_state = self.state logger.info('deleting %s' % str(self)) try: self.state_set(action, self.IN_PROGRESS) deletion_policy = self.t.get('DeletionPolicy', 'Delete') if deletion_policy == 'Delete': if callable(getattr(self, 'handle_delete', None)): self.handle_delete() elif deletion_policy == 'Snapshot': if callable(getattr(self, 'handle_snapshot_delete', None)): self.handle_snapshot_delete(initial_state) except Exception as ex: logger.exception('Delete %s', str(self)) failure = exception.ResourceFailure(ex, self, self.action) self.state_set(action, self.FAILED, str(failure)) raise failure except: with excutils.save_and_reraise_exception(): try: self.state_set(action, self.FAILED, 'Deletion aborted') except Exception: logger.exception('Error marking resource deletion failed') else: self.state_set(action, self.COMPLETE)
def __iter__(self): """Return a result until we get a reply with an 'ending' flag.""" if self._done: raise StopIteration while True: try: data = self._dataqueue.get(timeout=self._timeout) result = self._process_data(data) except queue.Empty: self.done() raise rpc_common.Timeout() except Exception: with excutils.save_and_reraise_exception(): self.done() if self._got_ending: self.done() raise StopIteration if isinstance(result, Exception): self.done() raise result yield result
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY): """ Adjust the size of the scaling group if the cooldown permits. """ if self._cooldown_inprogress(): logger.info( _("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s") % { 'name': self.name, 'cooldown': self.properties[self.COOLDOWN] }) return capacity = len(self.get_instances()) if adjustment_type == CHANGE_IN_CAPACITY: new_capacity = capacity + adjustment elif adjustment_type == EXACT_CAPACITY: new_capacity = adjustment else: # PercentChangeInCapacity delta = capacity * adjustment / 100.0 if math.fabs(delta) < 1.0: rounded = int( math.ceil(delta) if delta > 0.0 else math.floor(delta)) else: rounded = int( math.floor(delta) if delta > 0.0 else math.ceil(delta)) new_capacity = capacity + rounded upper = self.properties[self.MAX_SIZE] lower = self.properties[self.MIN_SIZE] if new_capacity > upper: if upper > capacity: logger.info(_('truncating growth to %s') % upper) new_capacity = upper else: logger.warn(_('can not exceed %s') % upper) return if new_capacity < lower: if lower < capacity: logger.info(_('truncating shrinkage to %s') % lower) new_capacity = lower else: logger.warn(_('can not be less than %s') % lower) return if new_capacity == capacity: logger.debug('no change in capacity %d' % capacity) return # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId() }, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({ 'suffix': 'error', 'message': six.text_type(resize_ex), }) notification.send(**notif) except Exception: logger.exception(_('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname'] }, }) notification.send(**notif) self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
def update(self, after, before=None, prev_resource=None): ''' update the resource. Subclasses should provide a handle_update() method to customise update, the base-class handle_update will fail by default. ''' action = self.UPDATE assert isinstance(after, rsrc_defn.ResourceDefinition) (cur_class_def, cur_ver) = self.implementation_signature() prev_ver = cur_ver if prev_resource is not None: (prev_class_def, prev_ver) = prev_resource.implementation_signature() if prev_class_def != cur_class_def: raise UpdateReplace(self.name) if before is None: before = self.parsed_template() if prev_ver == cur_ver and before == after: return if (self.action, self.status) in ((self.CREATE, self.IN_PROGRESS), (self.UPDATE, self.IN_PROGRESS), (self.ADOPT, self.IN_PROGRESS)): exc = Exception(_('Resource update already requested')) raise exception.ResourceFailure(exc, self, action) LOG.info(_('updating %s') % str(self)) try: self.updated_time = datetime.utcnow() self.state_set(action, self.IN_PROGRESS) before_properties = Properties(self.properties_schema, before.get('Properties', {}), function.resolve, self.name, self.context) after_properties = after.properties(self.properties_schema, self.context) after_properties.validate() tmpl_diff = self.update_template_diff(function.resolve(after), before) prop_diff = self.update_template_diff_properties( after_properties, before_properties) if callable(getattr(self, 'handle_update', None)): handle_data = self.handle_update(after, tmpl_diff, prop_diff) yield if callable(getattr(self, 'check_update_complete', None)): while not self.check_update_complete(handle_data): yield except UpdateReplace: with excutils.save_and_reraise_exception(): LOG.debug("Resource %s update requires replacement" % self.name) except Exception as ex: LOG.exception( _('update %(resource)s : %(err)s') % { 'resource': str(self), 'err': ex }) failure = exception.ResourceFailure(ex, self, action) self.state_set(action, self.FAILED, six.text_type(failure)) raise failure else: self.t = after self.reparse() self.state_set(action, self.COMPLETE)